js/src/jsprf.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jsprf.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1157 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * Portable safe sprintf code.
    1.12 + *
    1.13 + * Author: Kipp E.B. Hickman
    1.14 + */
    1.15 +
    1.16 +#include "jsprf.h"
    1.17 +
    1.18 +#include <stdarg.h>
    1.19 +#include <stdio.h>
    1.20 +#include <stdlib.h>
    1.21 +#include <string.h>
    1.22 +
    1.23 +#include "jspubtd.h"
    1.24 +#include "jsstr.h"
    1.25 +#include "jsutil.h"
    1.26 +
    1.27 +using namespace js;
    1.28 +
    1.29 +/*
    1.30 + * Note: on some platforms va_list is defined as an array,
    1.31 + * and requires array notation.
    1.32 + */
    1.33 +#ifdef HAVE_VA_COPY
    1.34 +#define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo, bar)
    1.35 +#elif defined(HAVE_VA_LIST_AS_ARRAY)
    1.36 +#define VARARGS_ASSIGN(foo, bar)        foo[0] = bar[0]
    1.37 +#else
    1.38 +#define VARARGS_ASSIGN(foo, bar)        (foo) = (bar)
    1.39 +#endif
    1.40 +
    1.41 +struct SprintfState
    1.42 +{
    1.43 +    int (*stuff)(SprintfState *ss, const char *sp, size_t len);
    1.44 +
    1.45 +    char *base;
    1.46 +    char *cur;
    1.47 +    size_t maxlen;
    1.48 +
    1.49 +    int (*func)(void *arg, const char *sp, uint32_t len);
    1.50 +    void *arg;
    1.51 +};
    1.52 +
    1.53 +/*
    1.54 + * Numbered Argument State
    1.55 + */
    1.56 +struct NumArgState
    1.57 +{
    1.58 +    int type;       // type of the current ap
    1.59 +    va_list ap;     // point to the corresponding position on ap
    1.60 +};
    1.61 +
    1.62 +const size_t NAS_DEFAULT_NUM = 20;  // default number of NumberedArgumentState array
    1.63 +
    1.64 +
    1.65 +#define TYPE_INT16      0
    1.66 +#define TYPE_UINT16     1
    1.67 +#define TYPE_INTN       2
    1.68 +#define TYPE_UINTN      3
    1.69 +#define TYPE_INT32      4
    1.70 +#define TYPE_UINT32     5
    1.71 +#define TYPE_INT64      6
    1.72 +#define TYPE_UINT64     7
    1.73 +#define TYPE_STRING     8
    1.74 +#define TYPE_DOUBLE     9
    1.75 +#define TYPE_INTSTR     10
    1.76 +#define TYPE_WSTRING    11
    1.77 +#define TYPE_UNKNOWN    20
    1.78 +
    1.79 +#define FLAG_LEFT       0x1
    1.80 +#define FLAG_SIGNED     0x2
    1.81 +#define FLAG_SPACED     0x4
    1.82 +#define FLAG_ZEROS      0x8
    1.83 +#define FLAG_NEG        0x10
    1.84 +
    1.85 +inline int
    1.86 +generic_write(SprintfState *ss, const char *src, size_t srclen)
    1.87 +{
    1.88 +    return (*ss->stuff)(ss, src, srclen);
    1.89 +}
    1.90 +
    1.91 +inline int
    1.92 +generic_write(SprintfState *ss, const jschar *src, size_t srclen)
    1.93 +{
    1.94 +    const size_t CHUNK_SIZE = 64;
    1.95 +    char chunk[CHUNK_SIZE];
    1.96 +
    1.97 +    int rv = 0;
    1.98 +    size_t j = 0;
    1.99 +    size_t i = 0;
   1.100 +    while (i < srclen) {
   1.101 +        // FIXME: truncates characters to 8 bits
   1.102 +        chunk[j++] = char(src[i++]);
   1.103 +
   1.104 +        if (j == CHUNK_SIZE || i == srclen) {
   1.105 +            rv = (*ss->stuff)(ss, chunk, j);
   1.106 +            if (rv != 0)
   1.107 +                return rv;
   1.108 +            j = 0;
   1.109 +        }
   1.110 +    }
   1.111 +    return 0;
   1.112 +}
   1.113 +
   1.114 +// Fill into the buffer using the data in src
   1.115 +template <typename Char>
   1.116 +static int
   1.117 +fill2(SprintfState *ss, const Char *src, int srclen, int width, int flags)
   1.118 +{
   1.119 +    char space = ' ';
   1.120 +    int rv;
   1.121 +
   1.122 +    width -= srclen;
   1.123 +    if (width > 0 && (flags & FLAG_LEFT) == 0) {    // Right adjusting
   1.124 +        if (flags & FLAG_ZEROS)
   1.125 +            space = '0';
   1.126 +        while (--width >= 0) {
   1.127 +            rv = (*ss->stuff)(ss, &space, 1);
   1.128 +            if (rv < 0)
   1.129 +                return rv;
   1.130 +        }
   1.131 +    }
   1.132 +
   1.133 +    // Copy out the source data
   1.134 +    rv = generic_write(ss, src, srclen);
   1.135 +    if (rv < 0)
   1.136 +        return rv;
   1.137 +
   1.138 +    if (width > 0 && (flags & FLAG_LEFT) != 0) {    // Left adjusting
   1.139 +        while (--width >= 0) {
   1.140 +            rv = (*ss->stuff)(ss, &space, 1);
   1.141 +            if (rv < 0)
   1.142 +                return rv;
   1.143 +        }
   1.144 +    }
   1.145 +    return 0;
   1.146 +}
   1.147 +
   1.148 +/*
   1.149 + * Fill a number. The order is: optional-sign zero-filling conversion-digits
   1.150 + */
   1.151 +static int
   1.152 +fill_n(SprintfState *ss, const char *src, int srclen, int width, int prec, int type, int flags)
   1.153 +{
   1.154 +    int zerowidth = 0;
   1.155 +    int precwidth = 0;
   1.156 +    int signwidth = 0;
   1.157 +    int leftspaces = 0;
   1.158 +    int rightspaces = 0;
   1.159 +    int cvtwidth;
   1.160 +    int rv;
   1.161 +    char sign;
   1.162 +
   1.163 +    if ((type & 1) == 0) {
   1.164 +        if (flags & FLAG_NEG) {
   1.165 +            sign = '-';
   1.166 +            signwidth = 1;
   1.167 +        } else if (flags & FLAG_SIGNED) {
   1.168 +            sign = '+';
   1.169 +            signwidth = 1;
   1.170 +        } else if (flags & FLAG_SPACED) {
   1.171 +            sign = ' ';
   1.172 +            signwidth = 1;
   1.173 +        }
   1.174 +    }
   1.175 +    cvtwidth = signwidth + srclen;
   1.176 +
   1.177 +    if (prec > 0) {
   1.178 +        if (prec > srclen) {
   1.179 +            precwidth = prec - srclen;          // Need zero filling
   1.180 +            cvtwidth += precwidth;
   1.181 +        }
   1.182 +    }
   1.183 +
   1.184 +    if ((flags & FLAG_ZEROS) && (prec < 0)) {
   1.185 +        if (width > cvtwidth) {
   1.186 +            zerowidth = width - cvtwidth;       // Zero filling
   1.187 +            cvtwidth += zerowidth;
   1.188 +        }
   1.189 +    }
   1.190 +
   1.191 +    if (flags & FLAG_LEFT) {
   1.192 +        if (width > cvtwidth) {
   1.193 +            // Space filling on the right (i.e. left adjusting)
   1.194 +            rightspaces = width - cvtwidth;
   1.195 +        }
   1.196 +    } else {
   1.197 +        if (width > cvtwidth) {
   1.198 +            // Space filling on the left (i.e. right adjusting)
   1.199 +            leftspaces = width - cvtwidth;
   1.200 +        }
   1.201 +    }
   1.202 +    while (--leftspaces >= 0) {
   1.203 +        rv = (*ss->stuff)(ss, " ", 1);
   1.204 +        if (rv < 0) {
   1.205 +            return rv;
   1.206 +        }
   1.207 +    }
   1.208 +    if (signwidth) {
   1.209 +        rv = (*ss->stuff)(ss, &sign, 1);
   1.210 +        if (rv < 0) {
   1.211 +            return rv;
   1.212 +        }
   1.213 +    }
   1.214 +    while (--precwidth >= 0) {
   1.215 +        rv = (*ss->stuff)(ss, "0", 1);
   1.216 +        if (rv < 0) {
   1.217 +            return rv;
   1.218 +        }
   1.219 +    }
   1.220 +    while (--zerowidth >= 0) {
   1.221 +        rv = (*ss->stuff)(ss, "0", 1);
   1.222 +        if (rv < 0) {
   1.223 +            return rv;
   1.224 +        }
   1.225 +    }
   1.226 +    rv = (*ss->stuff)(ss, src, uint32_t(srclen));
   1.227 +    if (rv < 0) {
   1.228 +        return rv;
   1.229 +    }
   1.230 +    while (--rightspaces >= 0) {
   1.231 +        rv = (*ss->stuff)(ss, " ", 1);
   1.232 +        if (rv < 0) {
   1.233 +            return rv;
   1.234 +        }
   1.235 +    }
   1.236 +    return 0;
   1.237 +}
   1.238 +
   1.239 +/* Convert a long into its printable form. */
   1.240 +static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix,
   1.241 +                 int type, int flags, const char *hexp)
   1.242 +{
   1.243 +    char cvtbuf[100];
   1.244 +    char *cvt;
   1.245 +    int digits;
   1.246 +
   1.247 +    // according to the man page this needs to happen
   1.248 +    if ((prec == 0) && (num == 0)) {
   1.249 +        return 0;
   1.250 +    }
   1.251 +
   1.252 +    // Converting decimal is a little tricky. In the unsigned case we
   1.253 +    // need to stop when we hit 10 digits. In the signed case, we can
   1.254 +    // stop when the number is zero.
   1.255 +    cvt = cvtbuf + sizeof(cvtbuf);
   1.256 +    digits = 0;
   1.257 +    while (num) {
   1.258 +        int digit = (((unsigned long)num) % radix) & 0xF;
   1.259 +        *--cvt = hexp[digit];
   1.260 +        digits++;
   1.261 +        num = (long)(((unsigned long)num) / radix);
   1.262 +    }
   1.263 +    if (digits == 0) {
   1.264 +        *--cvt = '0';
   1.265 +        digits++;
   1.266 +    }
   1.267 +
   1.268 +    // Now that we have the number converted without its sign, deal with
   1.269 +    // the sign and zero padding.
   1.270 +    return fill_n(ss, cvt, digits, width, prec, type, flags);
   1.271 +}
   1.272 +
   1.273 +/* Convert a 64-bit integer into its printable form. */
   1.274 +static int cvt_ll(SprintfState *ss, int64_t num, int width, int prec, int radix,
   1.275 +                  int type, int flags, const char *hexp)
   1.276 +{
   1.277 +    // According to the man page, this needs to happen.
   1.278 +    if (prec == 0 && num == 0)
   1.279 +        return 0;
   1.280 +
   1.281 +    // Converting decimal is a little tricky. In the unsigned case we
   1.282 +    // need to stop when we hit 10 digits. In the signed case, we can
   1.283 +    // stop when the number is zero.
   1.284 +    int64_t rad = int64_t(radix);
   1.285 +    char cvtbuf[100];
   1.286 +    char *cvt = cvtbuf + sizeof(cvtbuf);
   1.287 +    int digits = 0;
   1.288 +    while (num != 0) {
   1.289 +        int64_t quot = uint64_t(num) / rad;
   1.290 +        int64_t rem = uint64_t(num) % rad;
   1.291 +        int32_t digit = int32_t(rem);
   1.292 +        *--cvt = hexp[digit & 0xf];
   1.293 +        digits++;
   1.294 +        num = quot;
   1.295 +    }
   1.296 +    if (digits == 0) {
   1.297 +        *--cvt = '0';
   1.298 +        digits++;
   1.299 +    }
   1.300 +
   1.301 +    // Now that we have the number converted without its sign, deal with
   1.302 +    // the sign and zero padding.
   1.303 +    return fill_n(ss, cvt, digits, width, prec, type, flags);
   1.304 +}
   1.305 +
   1.306 +/*
   1.307 + * Convert a double precision floating point number into its printable
   1.308 + * form.
   1.309 + *
   1.310 + * XXX stop using sprintf to convert floating point
   1.311 + */
   1.312 +static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1)
   1.313 +{
   1.314 +    char fin[20];
   1.315 +    char fout[300];
   1.316 +    int amount = fmt1 - fmt0;
   1.317 +
   1.318 +    JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
   1.319 +    if (amount >= (int)sizeof(fin)) {
   1.320 +        // Totally bogus % command to sprintf. Just ignore it
   1.321 +        return 0;
   1.322 +    }
   1.323 +    js_memcpy(fin, fmt0, (size_t)amount);
   1.324 +    fin[amount] = 0;
   1.325 +
   1.326 +    // Convert floating point using the native sprintf code
   1.327 +#ifdef DEBUG
   1.328 +    {
   1.329 +        const char *p = fin;
   1.330 +        while (*p) {
   1.331 +            JS_ASSERT(*p != 'L');
   1.332 +            p++;
   1.333 +        }
   1.334 +    }
   1.335 +#endif
   1.336 +    sprintf(fout, fin, d);
   1.337 +
   1.338 +    // This assert will catch overflow's of fout, when building with
   1.339 +    // debugging on. At least this way we can track down the evil piece
   1.340 +    // of calling code and fix it!
   1.341 +    JS_ASSERT(strlen(fout) < sizeof(fout));
   1.342 +
   1.343 +    return (*ss->stuff)(ss, fout, strlen(fout));
   1.344 +}
   1.345 +
   1.346 +static inline const char *generic_null_str(const char *) { return "(null)"; }
   1.347 +static inline const jschar *generic_null_str(const jschar *) { return MOZ_UTF16("(null)"); }
   1.348 +
   1.349 +static inline size_t generic_strlen(const char *s) { return strlen(s); }
   1.350 +static inline size_t generic_strlen(const jschar *s) { return js_strlen(s); }
   1.351 +
   1.352 +/*
   1.353 + * Convert a string into its printable form.  "width" is the output
   1.354 + * width. "prec" is the maximum number of characters of "s" to output,
   1.355 + * where -1 means until NUL.
   1.356 + */
   1.357 +template <typename Char>
   1.358 +static int
   1.359 +cvt_s(SprintfState *ss, const Char *s, int width, int prec, int flags)
   1.360 +{
   1.361 +    if (prec == 0)
   1.362 +        return 0;
   1.363 +    if (!s)
   1.364 +        s = generic_null_str(s);
   1.365 +
   1.366 +    // Limit string length by precision value
   1.367 +    int slen = int(generic_strlen(s));
   1.368 +    if (0 < prec && prec < slen)
   1.369 +        slen = prec;
   1.370 +
   1.371 +    // and away we go
   1.372 +    return fill2(ss, s, slen, width, flags);
   1.373 +}
   1.374 +
   1.375 +/*
   1.376 + * BuildArgArray stands for Numbered Argument list Sprintf
   1.377 + * for example,
   1.378 + *      fmp = "%4$i, %2$d, %3s, %1d";
   1.379 + * the number must start from 1, and no gap among them
   1.380 + */
   1.381 +static NumArgState *
   1.382 +BuildArgArray(const char *fmt, va_list ap, int *rv, NumArgState *nasArray)
   1.383 +{
   1.384 +    size_t number = 0, cn = 0, i;
   1.385 +    const char *p;
   1.386 +    char c;
   1.387 +    NumArgState *nas;
   1.388 +
   1.389 +
   1.390 +    // First pass:
   1.391 +    // Detemine how many legal % I have got, then allocate space.
   1.392 +
   1.393 +    p = fmt;
   1.394 +    *rv = 0;
   1.395 +    i = 0;
   1.396 +    while ((c = *p++) != 0) {
   1.397 +        if (c != '%')
   1.398 +            continue;
   1.399 +        if ((c = *p++) == '%')          // skip %% case
   1.400 +            continue;
   1.401 +
   1.402 +        while (c != 0) {
   1.403 +            if (c > '9' || c < '0') {
   1.404 +                if (c == '$') {         // numbered argument case
   1.405 +                    if (i > 0) {
   1.406 +                        *rv = -1;
   1.407 +                        return nullptr;
   1.408 +                    }
   1.409 +                    number++;
   1.410 +                } else {                // non-numbered argument case
   1.411 +                    if (number > 0) {
   1.412 +                        *rv = -1;
   1.413 +                        return nullptr;
   1.414 +                    }
   1.415 +                    i = 1;
   1.416 +                }
   1.417 +                break;
   1.418 +            }
   1.419 +
   1.420 +            c = *p++;
   1.421 +        }
   1.422 +    }
   1.423 +
   1.424 +    if (number == 0)
   1.425 +        return nullptr;
   1.426 +
   1.427 +    if (number > NAS_DEFAULT_NUM) {
   1.428 +        nas = (NumArgState *) js_malloc(number * sizeof(NumArgState));
   1.429 +        if (!nas) {
   1.430 +            *rv = -1;
   1.431 +            return nullptr;
   1.432 +        }
   1.433 +    } else {
   1.434 +        nas = nasArray;
   1.435 +    }
   1.436 +
   1.437 +    for (i = 0; i < number; i++)
   1.438 +        nas[i].type = TYPE_UNKNOWN;
   1.439 +
   1.440 +
   1.441 +    // Second pass:
   1.442 +    // Set nas[].type.
   1.443 +
   1.444 +    p = fmt;
   1.445 +    while ((c = *p++) != 0) {
   1.446 +        if (c != '%')
   1.447 +            continue;
   1.448 +        c = *p++;
   1.449 +        if (c == '%')
   1.450 +            continue;
   1.451 +
   1.452 +        cn = 0;
   1.453 +        while (c && c != '$') {     // should improve error check later
   1.454 +            cn = cn*10 + c - '0';
   1.455 +            c = *p++;
   1.456 +        }
   1.457 +
   1.458 +        if (!c || cn < 1 || cn > number) {
   1.459 +            *rv = -1;
   1.460 +            break;
   1.461 +        }
   1.462 +
   1.463 +        // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
   1.464 +        cn--;
   1.465 +        if (nas[cn].type != TYPE_UNKNOWN)
   1.466 +            continue;
   1.467 +
   1.468 +        c = *p++;
   1.469 +
   1.470 +        // width
   1.471 +        if (c == '*') {
   1.472 +            // not supported feature, for the argument is not numbered
   1.473 +            *rv = -1;
   1.474 +            break;
   1.475 +        }
   1.476 +
   1.477 +        while ((c >= '0') && (c <= '9')) {
   1.478 +            c = *p++;
   1.479 +        }
   1.480 +
   1.481 +        // precision
   1.482 +        if (c == '.') {
   1.483 +            c = *p++;
   1.484 +            if (c == '*') {
   1.485 +                // not supported feature, for the argument is not numbered
   1.486 +                *rv = -1;
   1.487 +                break;
   1.488 +            }
   1.489 +
   1.490 +            while ((c >= '0') && (c <= '9')) {
   1.491 +                c = *p++;
   1.492 +            }
   1.493 +        }
   1.494 +
   1.495 +        // size
   1.496 +        nas[cn].type = TYPE_INTN;
   1.497 +        if (c == 'h') {
   1.498 +            nas[cn].type = TYPE_INT16;
   1.499 +            c = *p++;
   1.500 +        } else if (c == 'L') {
   1.501 +            // XXX not quite sure here
   1.502 +            nas[cn].type = TYPE_INT64;
   1.503 +            c = *p++;
   1.504 +        } else if (c == 'l') {
   1.505 +            nas[cn].type = TYPE_INT32;
   1.506 +            c = *p++;
   1.507 +            if (c == 'l') {
   1.508 +                nas[cn].type = TYPE_INT64;
   1.509 +                c = *p++;
   1.510 +            }
   1.511 +        }
   1.512 +
   1.513 +        // format
   1.514 +        switch (c) {
   1.515 +        case 'd':
   1.516 +        case 'c':
   1.517 +        case 'i':
   1.518 +        case 'o':
   1.519 +        case 'u':
   1.520 +        case 'x':
   1.521 +        case 'X':
   1.522 +            break;
   1.523 +
   1.524 +        case 'e':
   1.525 +        case 'f':
   1.526 +        case 'g':
   1.527 +            nas[cn].type = TYPE_DOUBLE;
   1.528 +            break;
   1.529 +
   1.530 +        case 'p':
   1.531 +            // XXX should use cpp
   1.532 +            if (sizeof(void *) == sizeof(int32_t)) {
   1.533 +                nas[cn].type = TYPE_UINT32;
   1.534 +            } else if (sizeof(void *) == sizeof(int64_t)) {
   1.535 +                nas[cn].type = TYPE_UINT64;
   1.536 +            } else if (sizeof(void *) == sizeof(int)) {
   1.537 +                nas[cn].type = TYPE_UINTN;
   1.538 +            } else {
   1.539 +                nas[cn].type = TYPE_UNKNOWN;
   1.540 +            }
   1.541 +            break;
   1.542 +
   1.543 +        case 'C':
   1.544 +        case 'S':
   1.545 +        case 'E':
   1.546 +        case 'G':
   1.547 +            // XXX not supported I suppose
   1.548 +            JS_ASSERT(0);
   1.549 +            nas[cn].type = TYPE_UNKNOWN;
   1.550 +            break;
   1.551 +
   1.552 +        case 's':
   1.553 +            nas[cn].type = (nas[cn].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
   1.554 +            break;
   1.555 +
   1.556 +        case 'n':
   1.557 +            nas[cn].type = TYPE_INTSTR;
   1.558 +            break;
   1.559 +
   1.560 +        default:
   1.561 +            JS_ASSERT(0);
   1.562 +            nas[cn].type = TYPE_UNKNOWN;
   1.563 +            break;
   1.564 +        }
   1.565 +
   1.566 +        // get a legal para.
   1.567 +        if (nas[cn].type == TYPE_UNKNOWN) {
   1.568 +            *rv = -1;
   1.569 +            break;
   1.570 +        }
   1.571 +    }
   1.572 +
   1.573 +
   1.574 +    // Third pass:
   1.575 +    // Fill nas[].ap.
   1.576 +
   1.577 +    if (*rv < 0) {
   1.578 +        if (nas != nasArray)
   1.579 +            js_free(nas);
   1.580 +        return nullptr;
   1.581 +    }
   1.582 +
   1.583 +    cn = 0;
   1.584 +    while (cn < number) {
   1.585 +        if (nas[cn].type == TYPE_UNKNOWN) {
   1.586 +            cn++;
   1.587 +            continue;
   1.588 +        }
   1.589 +
   1.590 +        VARARGS_ASSIGN(nas[cn].ap, ap);
   1.591 +
   1.592 +        switch (nas[cn].type) {
   1.593 +        case TYPE_INT16:
   1.594 +        case TYPE_UINT16:
   1.595 +        case TYPE_INTN:
   1.596 +        case TYPE_UINTN:        (void) va_arg(ap, int);         break;
   1.597 +        case TYPE_INT32:        (void) va_arg(ap, int32_t);     break;
   1.598 +        case TYPE_UINT32:       (void) va_arg(ap, uint32_t);    break;
   1.599 +        case TYPE_INT64:        (void) va_arg(ap, int64_t);     break;
   1.600 +        case TYPE_UINT64:       (void) va_arg(ap, uint64_t);    break;
   1.601 +        case TYPE_STRING:       (void) va_arg(ap, char*);       break;
   1.602 +        case TYPE_WSTRING:      (void) va_arg(ap, jschar*);     break;
   1.603 +        case TYPE_INTSTR:       (void) va_arg(ap, int*);        break;
   1.604 +        case TYPE_DOUBLE:       (void) va_arg(ap, double);      break;
   1.605 +
   1.606 +        default:
   1.607 +            if (nas != nasArray)
   1.608 +                js_free(nas);
   1.609 +            *rv = -1;
   1.610 +            return nullptr;
   1.611 +        }
   1.612 +
   1.613 +        cn++;
   1.614 +    }
   1.615 +
   1.616 +
   1.617 +    return nas;
   1.618 +}
   1.619 +
   1.620 +/*
   1.621 + * The workhorse sprintf code.
   1.622 + */
   1.623 +static int
   1.624 +dosprintf(SprintfState *ss, const char *fmt, va_list ap)
   1.625 +{
   1.626 +    char c;
   1.627 +    int flags, width, prec, radix, type;
   1.628 +    union {
   1.629 +        char ch;
   1.630 +        jschar wch;
   1.631 +        int i;
   1.632 +        long l;
   1.633 +        int64_t ll;
   1.634 +        double d;
   1.635 +        const char *s;
   1.636 +        const jschar* ws;
   1.637 +        int *ip;
   1.638 +    } u;
   1.639 +    const char *fmt0;
   1.640 +    static const char hex[] = "0123456789abcdef";
   1.641 +    static const char HEX[] = "0123456789ABCDEF";
   1.642 +    const char *hexp;
   1.643 +    int rv, i;
   1.644 +    NumArgState *nas = nullptr;
   1.645 +    NumArgState nasArray[NAS_DEFAULT_NUM];
   1.646 +    char pattern[20];
   1.647 +    const char *dolPt = nullptr;  // in "%4$.2f", dolPt will point to '.'
   1.648 +
   1.649 +    // Build an argument array, IF the fmt is numbered argument
   1.650 +    // list style, to contain the Numbered Argument list pointers.
   1.651 +
   1.652 +    nas = BuildArgArray(fmt, ap, &rv, nasArray);
   1.653 +    if (rv < 0) {
   1.654 +        // the fmt contains error Numbered Argument format, jliu@netscape.com
   1.655 +        JS_ASSERT(0);
   1.656 +        return rv;
   1.657 +    }
   1.658 +
   1.659 +    while ((c = *fmt++) != 0) {
   1.660 +        if (c != '%') {
   1.661 +            rv = (*ss->stuff)(ss, fmt - 1, 1);
   1.662 +            if (rv < 0) {
   1.663 +                return rv;
   1.664 +            }
   1.665 +            continue;
   1.666 +        }
   1.667 +        fmt0 = fmt - 1;
   1.668 +
   1.669 +        // Gobble up the % format string. Hopefully we have handled all
   1.670 +        // of the strange cases!
   1.671 +        flags = 0;
   1.672 +        c = *fmt++;
   1.673 +        if (c == '%') {
   1.674 +            // quoting a % with %%
   1.675 +            rv = (*ss->stuff)(ss, fmt - 1, 1);
   1.676 +            if (rv < 0) {
   1.677 +                return rv;
   1.678 +            }
   1.679 +            continue;
   1.680 +        }
   1.681 +
   1.682 +        if (nas != nullptr) {
   1.683 +            // the fmt contains the Numbered Arguments feature
   1.684 +            i = 0;
   1.685 +            while (c && c != '$') {         // should improve error check later
   1.686 +                i = (i * 10) + (c - '0');
   1.687 +                c = *fmt++;
   1.688 +            }
   1.689 +
   1.690 +            if (nas[i-1].type == TYPE_UNKNOWN) {
   1.691 +                if (nas && nas != nasArray)
   1.692 +                    js_free(nas);
   1.693 +                return -1;
   1.694 +            }
   1.695 +
   1.696 +            ap = nas[i-1].ap;
   1.697 +            dolPt = fmt;
   1.698 +            c = *fmt++;
   1.699 +        }
   1.700 +
   1.701 +        // Examine optional flags.  Note that we do not implement the
   1.702 +        // '#' flag of sprintf().  The ANSI C spec. of the '#' flag is
   1.703 +        // somewhat ambiguous and not ideal, which is perhaps why
   1.704 +        // the various sprintf() implementations are inconsistent
   1.705 +        // on this feature.
   1.706 +        while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
   1.707 +            if (c == '-') flags |= FLAG_LEFT;
   1.708 +            if (c == '+') flags |= FLAG_SIGNED;
   1.709 +            if (c == ' ') flags |= FLAG_SPACED;
   1.710 +            if (c == '0') flags |= FLAG_ZEROS;
   1.711 +            c = *fmt++;
   1.712 +        }
   1.713 +        if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
   1.714 +        if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
   1.715 +
   1.716 +        // width
   1.717 +        if (c == '*') {
   1.718 +            c = *fmt++;
   1.719 +            width = va_arg(ap, int);
   1.720 +        } else {
   1.721 +            width = 0;
   1.722 +            while ((c >= '0') && (c <= '9')) {
   1.723 +                width = (width * 10) + (c - '0');
   1.724 +                c = *fmt++;
   1.725 +            }
   1.726 +        }
   1.727 +
   1.728 +        // precision
   1.729 +        prec = -1;
   1.730 +        if (c == '.') {
   1.731 +            c = *fmt++;
   1.732 +            if (c == '*') {
   1.733 +                c = *fmt++;
   1.734 +                prec = va_arg(ap, int);
   1.735 +            } else {
   1.736 +                prec = 0;
   1.737 +                while ((c >= '0') && (c <= '9')) {
   1.738 +                    prec = (prec * 10) + (c - '0');
   1.739 +                    c = *fmt++;
   1.740 +                }
   1.741 +            }
   1.742 +        }
   1.743 +
   1.744 +        // size
   1.745 +        type = TYPE_INTN;
   1.746 +        if (c == 'h') {
   1.747 +            type = TYPE_INT16;
   1.748 +            c = *fmt++;
   1.749 +        } else if (c == 'L') {
   1.750 +            // XXX not quite sure here
   1.751 +            type = TYPE_INT64;
   1.752 +            c = *fmt++;
   1.753 +        } else if (c == 'l') {
   1.754 +            type = TYPE_INT32;
   1.755 +            c = *fmt++;
   1.756 +            if (c == 'l') {
   1.757 +                type = TYPE_INT64;
   1.758 +                c = *fmt++;
   1.759 +            }
   1.760 +        }
   1.761 +
   1.762 +        // format
   1.763 +        hexp = hex;
   1.764 +        switch (c) {
   1.765 +          case 'd': case 'i':                   // decimal/integer
   1.766 +            radix = 10;
   1.767 +            goto fetch_and_convert;
   1.768 +
   1.769 +          case 'o':                             // octal
   1.770 +            radix = 8;
   1.771 +            type |= 1;
   1.772 +            goto fetch_and_convert;
   1.773 +
   1.774 +          case 'u':                             // unsigned decimal
   1.775 +            radix = 10;
   1.776 +            type |= 1;
   1.777 +            goto fetch_and_convert;
   1.778 +
   1.779 +          case 'x':                             // unsigned hex
   1.780 +            radix = 16;
   1.781 +            type |= 1;
   1.782 +            goto fetch_and_convert;
   1.783 +
   1.784 +          case 'X':                             // unsigned HEX
   1.785 +            radix = 16;
   1.786 +            hexp = HEX;
   1.787 +            type |= 1;
   1.788 +            goto fetch_and_convert;
   1.789 +
   1.790 +          fetch_and_convert:
   1.791 +            switch (type) {
   1.792 +              case TYPE_INT16:
   1.793 +                u.l = va_arg(ap, int);
   1.794 +                if (u.l < 0) {
   1.795 +                    u.l = -u.l;
   1.796 +                    flags |= FLAG_NEG;
   1.797 +                }
   1.798 +                goto do_long;
   1.799 +              case TYPE_UINT16:
   1.800 +                u.l = va_arg(ap, int) & 0xffff;
   1.801 +                goto do_long;
   1.802 +              case TYPE_INTN:
   1.803 +                u.l = va_arg(ap, int);
   1.804 +                if (u.l < 0) {
   1.805 +                    u.l = -u.l;
   1.806 +                    flags |= FLAG_NEG;
   1.807 +                }
   1.808 +                goto do_long;
   1.809 +              case TYPE_UINTN:
   1.810 +                u.l = (long)va_arg(ap, unsigned int);
   1.811 +                goto do_long;
   1.812 +
   1.813 +              case TYPE_INT32:
   1.814 +                u.l = va_arg(ap, int32_t);
   1.815 +                if (u.l < 0) {
   1.816 +                    u.l = -u.l;
   1.817 +                    flags |= FLAG_NEG;
   1.818 +                }
   1.819 +                goto do_long;
   1.820 +              case TYPE_UINT32:
   1.821 +                u.l = (long)va_arg(ap, uint32_t);
   1.822 +              do_long:
   1.823 +                rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp);
   1.824 +                if (rv < 0) {
   1.825 +                    return rv;
   1.826 +                }
   1.827 +                break;
   1.828 +
   1.829 +              case TYPE_INT64:
   1.830 +                u.ll = va_arg(ap, int64_t);
   1.831 +                if (u.ll < 0) {
   1.832 +                    u.ll = -u.ll;
   1.833 +                    flags |= FLAG_NEG;
   1.834 +                }
   1.835 +                goto do_longlong;
   1.836 +              case TYPE_UINT64:
   1.837 +                u.ll = va_arg(ap, uint64_t);
   1.838 +              do_longlong:
   1.839 +                rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp);
   1.840 +                if (rv < 0) {
   1.841 +                    return rv;
   1.842 +                }
   1.843 +                break;
   1.844 +            }
   1.845 +            break;
   1.846 +
   1.847 +          case 'e':
   1.848 +          case 'E':
   1.849 +          case 'f':
   1.850 +          case 'g':
   1.851 +            u.d = va_arg(ap, double);
   1.852 +            if (nas != nullptr) {
   1.853 +                i = fmt - dolPt;
   1.854 +                if (i < int(sizeof(pattern))) {
   1.855 +                    pattern[0] = '%';
   1.856 +                    js_memcpy(&pattern[1], dolPt, size_t(i));
   1.857 +                    rv = cvt_f(ss, u.d, pattern, &pattern[i + 1]);
   1.858 +                }
   1.859 +            } else
   1.860 +                rv = cvt_f(ss, u.d, fmt0, fmt);
   1.861 +
   1.862 +            if (rv < 0) {
   1.863 +                return rv;
   1.864 +            }
   1.865 +            break;
   1.866 +
   1.867 +          case 'c':
   1.868 +            if ((flags & FLAG_LEFT) == 0) {
   1.869 +                while (width-- > 1) {
   1.870 +                    rv = (*ss->stuff)(ss, " ", 1);
   1.871 +                    if (rv < 0) {
   1.872 +                        return rv;
   1.873 +                    }
   1.874 +                }
   1.875 +            }
   1.876 +            switch (type) {
   1.877 +              case TYPE_INT16:
   1.878 +              case TYPE_INTN:
   1.879 +                u.ch = va_arg(ap, int);
   1.880 +                rv = (*ss->stuff)(ss, &u.ch, 1);
   1.881 +                break;
   1.882 +            }
   1.883 +            if (rv < 0) {
   1.884 +                return rv;
   1.885 +            }
   1.886 +            if (flags & FLAG_LEFT) {
   1.887 +                while (width-- > 1) {
   1.888 +                    rv = (*ss->stuff)(ss, " ", 1);
   1.889 +                    if (rv < 0) {
   1.890 +                        return rv;
   1.891 +                    }
   1.892 +                }
   1.893 +            }
   1.894 +            break;
   1.895 +
   1.896 +          case 'p':
   1.897 +            if (sizeof(void *) == sizeof(int32_t)) {
   1.898 +                type = TYPE_UINT32;
   1.899 +            } else if (sizeof(void *) == sizeof(int64_t)) {
   1.900 +                type = TYPE_UINT64;
   1.901 +            } else if (sizeof(void *) == sizeof(int)) {
   1.902 +                type = TYPE_UINTN;
   1.903 +            } else {
   1.904 +                JS_ASSERT(0);
   1.905 +                break;
   1.906 +            }
   1.907 +            radix = 16;
   1.908 +            goto fetch_and_convert;
   1.909 +
   1.910 +#if 0
   1.911 +          case 'C':
   1.912 +          case 'S':
   1.913 +          case 'E':
   1.914 +          case 'G':
   1.915 +            // XXX not supported I suppose
   1.916 +            JS_ASSERT(0);
   1.917 +            break;
   1.918 +#endif
   1.919 +
   1.920 +          case 's':
   1.921 +            if(type == TYPE_INT16) {
   1.922 +                u.ws = va_arg(ap, const jschar*);
   1.923 +                rv = cvt_s(ss, u.ws, width, prec, flags);
   1.924 +            } else {
   1.925 +                u.s = va_arg(ap, const char*);
   1.926 +                rv = cvt_s(ss, u.s, width, prec, flags);
   1.927 +            }
   1.928 +            if (rv < 0) {
   1.929 +                return rv;
   1.930 +            }
   1.931 +            break;
   1.932 +
   1.933 +          case 'n':
   1.934 +            u.ip = va_arg(ap, int*);
   1.935 +            if (u.ip) {
   1.936 +                *u.ip = ss->cur - ss->base;
   1.937 +            }
   1.938 +            break;
   1.939 +
   1.940 +          default:
   1.941 +            // Not a % token after all... skip it
   1.942 +#if 0
   1.943 +            JS_ASSERT(0);
   1.944 +#endif
   1.945 +            rv = (*ss->stuff)(ss, "%", 1);
   1.946 +            if (rv < 0) {
   1.947 +                return rv;
   1.948 +            }
   1.949 +            rv = (*ss->stuff)(ss, fmt - 1, 1);
   1.950 +            if (rv < 0) {
   1.951 +                return rv;
   1.952 +            }
   1.953 +        }
   1.954 +    }
   1.955 +
   1.956 +    // Stuff trailing NUL
   1.957 +    rv = (*ss->stuff)(ss, "\0", 1);
   1.958 +
   1.959 +    if (nas && nas != nasArray)
   1.960 +        js_free(nas);
   1.961 +
   1.962 +    return rv;
   1.963 +}
   1.964 +
   1.965 +/************************************************************************/
   1.966 +
   1.967 +/*
   1.968 + * Stuff routine that automatically grows the js_malloc'd output buffer
   1.969 + * before it overflows.
   1.970 + */
   1.971 +static int
   1.972 +GrowStuff(SprintfState *ss, const char *sp, size_t len)
   1.973 +{
   1.974 +    ptrdiff_t off;
   1.975 +    char *newbase;
   1.976 +    size_t newlen;
   1.977 +
   1.978 +    off = ss->cur - ss->base;
   1.979 +    if (off + len >= ss->maxlen) {
   1.980 +        /* Grow the buffer */
   1.981 +        newlen = ss->maxlen + ((len > 32) ? len : 32);
   1.982 +        newbase = static_cast<char *>(js_realloc(ss->base, newlen));
   1.983 +        if (!newbase) {
   1.984 +            /* Ran out of memory */
   1.985 +            return -1;
   1.986 +        }
   1.987 +        ss->base = newbase;
   1.988 +        ss->maxlen = newlen;
   1.989 +        ss->cur = ss->base + off;
   1.990 +    }
   1.991 +
   1.992 +    /* Copy data */
   1.993 +    while (len) {
   1.994 +        --len;
   1.995 +        *ss->cur++ = *sp++;
   1.996 +    }
   1.997 +    MOZ_ASSERT(size_t(ss->cur - ss->base) <= ss->maxlen);
   1.998 +    return 0;
   1.999 +}
  1.1000 +
  1.1001 +/*
  1.1002 + * sprintf into a js_malloc'd buffer
  1.1003 + */
  1.1004 +JS_PUBLIC_API(char *)
  1.1005 +JS_smprintf(const char *fmt, ...)
  1.1006 +{
  1.1007 +    va_list ap;
  1.1008 +    char *rv;
  1.1009 +
  1.1010 +    va_start(ap, fmt);
  1.1011 +    rv = JS_vsmprintf(fmt, ap);
  1.1012 +    va_end(ap);
  1.1013 +    return rv;
  1.1014 +}
  1.1015 +
  1.1016 +/*
  1.1017 + * Free memory allocated, for the caller, by JS_smprintf
  1.1018 + */
  1.1019 +JS_PUBLIC_API(void)
  1.1020 +JS_smprintf_free(char *mem)
  1.1021 +{
  1.1022 +    js_free(mem);
  1.1023 +}
  1.1024 +
  1.1025 +JS_PUBLIC_API(char *)
  1.1026 +JS_vsmprintf(const char *fmt, va_list ap)
  1.1027 +{
  1.1028 +    SprintfState ss;
  1.1029 +    int rv;
  1.1030 +
  1.1031 +    ss.stuff = GrowStuff;
  1.1032 +    ss.base = 0;
  1.1033 +    ss.cur = 0;
  1.1034 +    ss.maxlen = 0;
  1.1035 +    rv = dosprintf(&ss, fmt, ap);
  1.1036 +    if (rv < 0) {
  1.1037 +        js_free(ss.base);
  1.1038 +        return 0;
  1.1039 +    }
  1.1040 +    return ss.base;
  1.1041 +}
  1.1042 +
  1.1043 +/*
  1.1044 + * Stuff routine that discards overflow data
  1.1045 + */
  1.1046 +static int
  1.1047 +LimitStuff(SprintfState *ss, const char *sp, size_t len)
  1.1048 +{
  1.1049 +    size_t limit = ss->maxlen - (ss->cur - ss->base);
  1.1050 +
  1.1051 +    if (len > limit)
  1.1052 +        len = limit;
  1.1053 +    while (len) {
  1.1054 +        --len;
  1.1055 +        *ss->cur++ = *sp++;
  1.1056 +    }
  1.1057 +    return 0;
  1.1058 +}
  1.1059 +
  1.1060 +/*
  1.1061 + * sprintf into a fixed size buffer. Make sure there is a NUL at the end
  1.1062 + * when finished.
  1.1063 + */
  1.1064 +JS_PUBLIC_API(uint32_t)
  1.1065 +JS_snprintf(char *out, uint32_t outlen, const char *fmt, ...)
  1.1066 +{
  1.1067 +    va_list ap;
  1.1068 +    int rv;
  1.1069 +
  1.1070 +    JS_ASSERT(int32_t(outlen) > 0);
  1.1071 +    if (int32_t(outlen) <= 0)
  1.1072 +        return 0;
  1.1073 +
  1.1074 +    va_start(ap, fmt);
  1.1075 +    rv = JS_vsnprintf(out, outlen, fmt, ap);
  1.1076 +    va_end(ap);
  1.1077 +    return rv;
  1.1078 +}
  1.1079 +
  1.1080 +JS_PUBLIC_API(uint32_t)
  1.1081 +JS_vsnprintf(char *out, uint32_t outlen, const char *fmt, va_list ap)
  1.1082 +{
  1.1083 +    SprintfState ss;
  1.1084 +    uint32_t n;
  1.1085 +
  1.1086 +    JS_ASSERT(int32_t(outlen) > 0);
  1.1087 +    if (int32_t(outlen) <= 0) {
  1.1088 +        return 0;
  1.1089 +    }
  1.1090 +
  1.1091 +    ss.stuff = LimitStuff;
  1.1092 +    ss.base = out;
  1.1093 +    ss.cur = out;
  1.1094 +    ss.maxlen = outlen;
  1.1095 +    (void) dosprintf(&ss, fmt, ap);
  1.1096 +
  1.1097 +    /* If we added chars, and we didn't append a null, do it now. */
  1.1098 +    if (ss.cur != ss.base && ss.cur[-1] != '\0')
  1.1099 +        ss.cur[-1] = '\0';
  1.1100 +
  1.1101 +    n = ss.cur - ss.base;
  1.1102 +    return n ? n - 1 : n;
  1.1103 +}
  1.1104 +
  1.1105 +JS_PUBLIC_API(char *)
  1.1106 +JS_sprintf_append(char *last, const char *fmt, ...)
  1.1107 +{
  1.1108 +    va_list ap;
  1.1109 +    char *rv;
  1.1110 +
  1.1111 +    va_start(ap, fmt);
  1.1112 +    rv = JS_vsprintf_append(last, fmt, ap);
  1.1113 +    va_end(ap);
  1.1114 +    return rv;
  1.1115 +}
  1.1116 +
  1.1117 +JS_PUBLIC_API(char *)
  1.1118 +JS_vsprintf_append(char *last, const char *fmt, va_list ap)
  1.1119 +{
  1.1120 +    SprintfState ss;
  1.1121 +    int rv;
  1.1122 +
  1.1123 +    ss.stuff = GrowStuff;
  1.1124 +    if (last) {
  1.1125 +        size_t lastlen = strlen(last);
  1.1126 +        ss.base = last;
  1.1127 +        ss.cur = last + lastlen;
  1.1128 +        ss.maxlen = lastlen;
  1.1129 +    } else {
  1.1130 +        ss.base = 0;
  1.1131 +        ss.cur = 0;
  1.1132 +        ss.maxlen = 0;
  1.1133 +    }
  1.1134 +    rv = dosprintf(&ss, fmt, ap);
  1.1135 +    if (rv < 0) {
  1.1136 +        js_free(ss.base);
  1.1137 +        return 0;
  1.1138 +    }
  1.1139 +    return ss.base;
  1.1140 +}
  1.1141 +
  1.1142 +#undef TYPE_INT16
  1.1143 +#undef TYPE_UINT16
  1.1144 +#undef TYPE_INTN
  1.1145 +#undef TYPE_UINTN
  1.1146 +#undef TYPE_INT32
  1.1147 +#undef TYPE_UINT32
  1.1148 +#undef TYPE_INT64
  1.1149 +#undef TYPE_UINT64
  1.1150 +#undef TYPE_STRING
  1.1151 +#undef TYPE_DOUBLE
  1.1152 +#undef TYPE_INTSTR
  1.1153 +#undef TYPE_WSTRING
  1.1154 +#undef TYPE_UNKNOWN
  1.1155 +
  1.1156 +#undef FLAG_LEFT
  1.1157 +#undef FLAG_SIGNED
  1.1158 +#undef FLAG_SPACED
  1.1159 +#undef FLAG_ZEROS
  1.1160 +#undef FLAG_NEG

mercurial