Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsprf.cpp
blob418206ca5abbbd14e927e13f61b03e88b908c2db
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Portable safe sprintf code.
10 * Author: Kipp E.B. Hickman
13 #include "jsprf.h"
15 #include "mozilla/Vector.h"
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
22 #include "jsalloc.h"
23 #include "jspubtd.h"
24 #include "jsstr.h"
25 #include "jsutil.h"
27 using namespace js;
30 * Note: on some platforms va_list is defined as an array,
31 * and requires array notation.
33 #ifdef HAVE_VA_COPY
34 #define VARARGS_ASSIGN(foo, bar) VA_COPY(foo, bar)
35 #elif defined(HAVE_VA_LIST_AS_ARRAY)
36 #define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0]
37 #else
38 #define VARARGS_ASSIGN(foo, bar) (foo) = (bar)
39 #endif
41 struct SprintfState
43 bool (*stuff)(SprintfState* ss, const char* sp, size_t len);
45 char* base;
46 char* cur;
47 size_t maxlen;
49 int (*func)(void* arg, const char* sp, uint32_t len);
50 void* arg;
54 * Numbered Argument State
56 struct NumArgState
58 int type; // type of the current ap
59 va_list ap; // point to the corresponding position on ap
62 typedef mozilla::Vector<NumArgState, 20, js::SystemAllocPolicy> NumArgStateVector;
65 #define TYPE_INT16 0
66 #define TYPE_UINT16 1
67 #define TYPE_INTN 2
68 #define TYPE_UINTN 3
69 #define TYPE_INT32 4
70 #define TYPE_UINT32 5
71 #define TYPE_INT64 6
72 #define TYPE_UINT64 7
73 #define TYPE_STRING 8
74 #define TYPE_DOUBLE 9
75 #define TYPE_INTSTR 10
76 #define TYPE_WSTRING 11
77 #define TYPE_UNKNOWN 20
79 #define FLAG_LEFT 0x1
80 #define FLAG_SIGNED 0x2
81 #define FLAG_SPACED 0x4
82 #define FLAG_ZEROS 0x8
83 #define FLAG_NEG 0x10
85 inline bool
86 generic_write(SprintfState* ss, const char* src, size_t srclen)
88 return (*ss->stuff)(ss, src, srclen);
91 inline bool
92 generic_write(SprintfState* ss, const char16_t* src, size_t srclen)
94 const size_t CHUNK_SIZE = 64;
95 char chunk[CHUNK_SIZE];
97 size_t j = 0;
98 size_t i = 0;
99 while (i < srclen) {
100 // FIXME: truncates characters to 8 bits
101 chunk[j++] = char(src[i++]);
103 if (j == CHUNK_SIZE || i == srclen) {
104 if (!(*ss->stuff)(ss, chunk, j))
105 return false;
106 j = 0;
109 return true;
112 // Fill into the buffer using the data in src
113 template <typename Char>
114 static bool
115 fill2(SprintfState* ss, const Char* src, int srclen, int width, int flags)
117 char space = ' ';
119 width -= srclen;
120 if (width > 0 && (flags & FLAG_LEFT) == 0) { // Right adjusting
121 if (flags & FLAG_ZEROS)
122 space = '0';
123 while (--width >= 0) {
124 if (!(*ss->stuff)(ss, &space, 1))
125 return false;
129 // Copy out the source data
130 if (!generic_write(ss, src, srclen))
131 return false;
133 if (width > 0 && (flags & FLAG_LEFT) != 0) { // Left adjusting
134 while (--width >= 0) {
135 if (!(*ss->stuff)(ss, &space, 1))
136 return false;
139 return true;
143 * Fill a number. The order is: optional-sign zero-filling conversion-digits
145 static bool
146 fill_n(SprintfState* ss, const char* src, int srclen, int width, int prec, int type, int flags)
148 int zerowidth = 0;
149 int precwidth = 0;
150 int signwidth = 0;
151 int leftspaces = 0;
152 int rightspaces = 0;
153 int cvtwidth;
154 char sign;
156 if ((type & 1) == 0) {
157 if (flags & FLAG_NEG) {
158 sign = '-';
159 signwidth = 1;
160 } else if (flags & FLAG_SIGNED) {
161 sign = '+';
162 signwidth = 1;
163 } else if (flags & FLAG_SPACED) {
164 sign = ' ';
165 signwidth = 1;
168 cvtwidth = signwidth + srclen;
170 if (prec > 0) {
171 if (prec > srclen) {
172 precwidth = prec - srclen; // Need zero filling
173 cvtwidth += precwidth;
177 if ((flags & FLAG_ZEROS) && (prec < 0)) {
178 if (width > cvtwidth) {
179 zerowidth = width - cvtwidth; // Zero filling
180 cvtwidth += zerowidth;
184 if (flags & FLAG_LEFT) {
185 if (width > cvtwidth) {
186 // Space filling on the right (i.e. left adjusting)
187 rightspaces = width - cvtwidth;
189 } else {
190 if (width > cvtwidth) {
191 // Space filling on the left (i.e. right adjusting)
192 leftspaces = width - cvtwidth;
195 while (--leftspaces >= 0) {
196 if (!(*ss->stuff)(ss, " ", 1))
197 return false;
199 if (signwidth) {
200 if (!(*ss->stuff)(ss, &sign, 1))
201 return false;
203 while (--precwidth >= 0) {
204 if (!(*ss->stuff)(ss, "0", 1))
205 return false;
207 while (--zerowidth >= 0) {
208 if (!(*ss->stuff)(ss, "0", 1))
209 return false;
211 if (!(*ss->stuff)(ss, src, uint32_t(srclen)))
212 return false;
213 while (--rightspaces >= 0) {
214 if (!(*ss->stuff)(ss, " ", 1))
215 return false;
217 return true;
220 /* Convert a long into its printable form. */
221 static bool cvt_l(SprintfState* ss, long num, int width, int prec, int radix,
222 int type, int flags, const char* hexp)
224 char cvtbuf[100];
225 char* cvt;
226 int digits;
228 // according to the man page this needs to happen
229 if ((prec == 0) && (num == 0))
230 return true;
232 // Converting decimal is a little tricky. In the unsigned case we
233 // need to stop when we hit 10 digits. In the signed case, we can
234 // stop when the number is zero.
235 cvt = cvtbuf + sizeof(cvtbuf);
236 digits = 0;
237 while (num) {
238 int digit = (((unsigned long)num) % radix) & 0xF;
239 *--cvt = hexp[digit];
240 digits++;
241 num = (long)(((unsigned long)num) / radix);
243 if (digits == 0) {
244 *--cvt = '0';
245 digits++;
248 // Now that we have the number converted without its sign, deal with
249 // the sign and zero padding.
250 return fill_n(ss, cvt, digits, width, prec, type, flags);
253 /* Convert a 64-bit integer into its printable form. */
254 static bool cvt_ll(SprintfState* ss, int64_t num, int width, int prec, int radix,
255 int type, int flags, const char* hexp)
257 // According to the man page, this needs to happen.
258 if (prec == 0 && num == 0)
259 return true;
261 // Converting decimal is a little tricky. In the unsigned case we
262 // need to stop when we hit 10 digits. In the signed case, we can
263 // stop when the number is zero.
264 int64_t rad = int64_t(radix);
265 char cvtbuf[100];
266 char* cvt = cvtbuf + sizeof(cvtbuf);
267 int digits = 0;
268 while (num != 0) {
269 int64_t quot = uint64_t(num) / rad;
270 int64_t rem = uint64_t(num) % rad;
271 int32_t digit = int32_t(rem);
272 *--cvt = hexp[digit & 0xf];
273 digits++;
274 num = quot;
276 if (digits == 0) {
277 *--cvt = '0';
278 digits++;
281 // Now that we have the number converted without its sign, deal with
282 // the sign and zero padding.
283 return fill_n(ss, cvt, digits, width, prec, type, flags);
287 * Convert a double precision floating point number into its printable
288 * form.
290 * XXX stop using sprintf to convert floating point
292 static bool cvt_f(SprintfState* ss, double d, const char* fmt0, const char* fmt1)
294 char fin[20];
295 char fout[300];
296 int amount = fmt1 - fmt0;
298 MOZ_ASSERT((amount > 0) && (amount < (int)sizeof(fin)));
299 if (amount >= (int)sizeof(fin)) {
300 // Totally bogus % command to sprintf. Just ignore it
301 return true;
303 js_memcpy(fin, fmt0, (size_t)amount);
304 fin[amount] = 0;
306 // Convert floating point using the native sprintf code
307 #ifdef DEBUG
309 const char* p = fin;
310 while (*p) {
311 MOZ_ASSERT(*p != 'L');
312 p++;
315 #endif
316 sprintf(fout, fin, d);
318 // This assert will catch overflow's of fout, when building with
319 // debugging on. At least this way we can track down the evil piece
320 // of calling code and fix it!
321 MOZ_ASSERT(strlen(fout) < sizeof(fout));
323 return (*ss->stuff)(ss, fout, strlen(fout));
326 static inline const char* generic_null_str(const char*) { return "(null)"; }
327 static inline const char16_t* generic_null_str(const char16_t*) { return MOZ_UTF16("(null)"); }
329 static inline size_t generic_strlen(const char* s) { return strlen(s); }
330 static inline size_t generic_strlen(const char16_t* s) { return js_strlen(s); }
333 * Convert a string into its printable form. "width" is the output
334 * width. "prec" is the maximum number of characters of "s" to output,
335 * where -1 means until NUL.
337 template <typename Char>
338 static bool
339 cvt_s(SprintfState* ss, const Char* s, int width, int prec, int flags)
341 if (prec == 0)
342 return true;
343 if (!s)
344 s = generic_null_str(s);
346 // Limit string length by precision value
347 int slen = int(generic_strlen(s));
348 if (0 < prec && prec < slen)
349 slen = prec;
351 // and away we go
352 return fill2(ss, s, slen, width, flags);
356 * BuildArgArray stands for Numbered Argument list Sprintf
357 * for example,
358 * fmp = "%4$i, %2$d, %3s, %1d";
359 * the number must start from 1, and no gap among them
361 static bool
362 BuildArgArray(const char* fmt, va_list ap, NumArgStateVector& nas)
364 size_t number = 0, cn = 0, i;
365 const char* p;
366 char c;
369 // First pass:
370 // Detemine how many legal % I have got, then allocate space.
372 p = fmt;
373 i = 0;
374 while ((c = *p++) != 0) {
375 if (c != '%')
376 continue;
377 if ((c = *p++) == '%') // skip %% case
378 continue;
380 while (c != 0) {
381 if (c > '9' || c < '0') {
382 if (c == '$') { // numbered argument case
383 if (i > 0)
384 return false;
385 number++;
386 } else { // non-numbered argument case
387 if (number > 0)
388 return false;
389 i = 1;
391 break;
394 c = *p++;
398 if (number == 0)
399 return true;
401 if (!nas.growByUninitialized(number))
402 return false;
404 for (i = 0; i < number; i++)
405 nas[i].type = TYPE_UNKNOWN;
408 // Second pass:
409 // Set nas[].type.
411 p = fmt;
412 while ((c = *p++) != 0) {
413 if (c != '%')
414 continue;
415 c = *p++;
416 if (c == '%')
417 continue;
419 cn = 0;
420 while (c && c != '$') { // should improve error check later
421 cn = cn*10 + c - '0';
422 c = *p++;
425 if (!c || cn < 1 || cn > number)
426 return false;
428 // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
429 cn--;
430 if (nas[cn].type != TYPE_UNKNOWN)
431 continue;
433 c = *p++;
435 // width
436 if (c == '*') {
437 // not supported feature, for the argument is not numbered
438 return false;
441 while ((c >= '0') && (c <= '9')) {
442 c = *p++;
445 // precision
446 if (c == '.') {
447 c = *p++;
448 if (c == '*') {
449 // not supported feature, for the argument is not numbered
450 return false;
453 while ((c >= '0') && (c <= '9')) {
454 c = *p++;
458 // size
459 nas[cn].type = TYPE_INTN;
460 if (c == 'h') {
461 nas[cn].type = TYPE_INT16;
462 c = *p++;
463 } else if (c == 'L') {
464 // XXX not quite sure here
465 nas[cn].type = TYPE_INT64;
466 c = *p++;
467 } else if (c == 'l') {
468 nas[cn].type = TYPE_INT32;
469 c = *p++;
470 if (c == 'l') {
471 nas[cn].type = TYPE_INT64;
472 c = *p++;
476 // format
477 switch (c) {
478 case 'd':
479 case 'c':
480 case 'i':
481 case 'o':
482 case 'u':
483 case 'x':
484 case 'X':
485 break;
487 case 'e':
488 case 'f':
489 case 'g':
490 nas[cn].type = TYPE_DOUBLE;
491 break;
493 case 'p':
494 // XXX should use cpp
495 if (sizeof(void*) == sizeof(int32_t)) {
496 nas[cn].type = TYPE_UINT32;
497 } else if (sizeof(void*) == sizeof(int64_t)) {
498 nas[cn].type = TYPE_UINT64;
499 } else if (sizeof(void*) == sizeof(int)) {
500 nas[cn].type = TYPE_UINTN;
501 } else {
502 nas[cn].type = TYPE_UNKNOWN;
504 break;
506 case 'C':
507 case 'S':
508 case 'E':
509 case 'G':
510 // XXX not supported I suppose
511 MOZ_ASSERT(0);
512 nas[cn].type = TYPE_UNKNOWN;
513 break;
515 case 's':
516 nas[cn].type = (nas[cn].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING;
517 break;
519 case 'n':
520 nas[cn].type = TYPE_INTSTR;
521 break;
523 default:
524 MOZ_ASSERT(0);
525 nas[cn].type = TYPE_UNKNOWN;
526 break;
529 // get a legal para.
530 if (nas[cn].type == TYPE_UNKNOWN)
531 return false;
535 // Third pass:
536 // Fill nas[].ap.
538 cn = 0;
539 while (cn < number) {
540 if (nas[cn].type == TYPE_UNKNOWN) {
541 cn++;
542 continue;
545 VARARGS_ASSIGN(nas[cn].ap, ap);
547 switch (nas[cn].type) {
548 case TYPE_INT16:
549 case TYPE_UINT16:
550 case TYPE_INTN:
551 case TYPE_UINTN: (void) va_arg(ap, int); break;
552 case TYPE_INT32: (void) va_arg(ap, int32_t); break;
553 case TYPE_UINT32: (void) va_arg(ap, uint32_t); break;
554 case TYPE_INT64: (void) va_arg(ap, int64_t); break;
555 case TYPE_UINT64: (void) va_arg(ap, uint64_t); break;
556 case TYPE_STRING: (void) va_arg(ap, char*); break;
557 case TYPE_WSTRING: (void) va_arg(ap, char16_t*); break;
558 case TYPE_INTSTR: (void) va_arg(ap, int*); break;
559 case TYPE_DOUBLE: (void) va_arg(ap, double); break;
561 default: return false;
564 cn++;
567 return true;
571 * The workhorse sprintf code.
573 static bool
574 dosprintf(SprintfState* ss, const char* fmt, va_list ap)
576 char c;
577 int flags, width, prec, radix, type;
578 union {
579 char ch;
580 char16_t wch;
581 int i;
582 long l;
583 int64_t ll;
584 double d;
585 const char* s;
586 const char16_t* ws;
587 int* ip;
588 } u;
589 const char* fmt0;
590 static const char hex[] = "0123456789abcdef";
591 static const char HEX[] = "0123456789ABCDEF";
592 const char* hexp;
593 int i;
594 char pattern[20];
595 const char* dolPt = nullptr; // in "%4$.2f", dolPt will point to '.'
597 // Build an argument array, IF the fmt is numbered argument
598 // list style, to contain the Numbered Argument list pointers.
600 NumArgStateVector nas;
601 if (!BuildArgArray(fmt, ap, nas)) {
602 // the fmt contains error Numbered Argument format, jliu@netscape.com
603 MOZ_ASSERT(0);
604 return false;
607 while ((c = *fmt++) != 0) {
608 if (c != '%') {
609 if (!(*ss->stuff)(ss, fmt - 1, 1))
610 return false;
612 continue;
614 fmt0 = fmt - 1;
616 // Gobble up the % format string. Hopefully we have handled all
617 // of the strange cases!
618 flags = 0;
619 c = *fmt++;
620 if (c == '%') {
621 // quoting a % with %%
622 if (!(*ss->stuff)(ss, fmt - 1, 1))
623 return false;
625 continue;
628 if (!nas.empty()) {
629 // the fmt contains the Numbered Arguments feature
630 i = 0;
631 while (c && c != '$') { // should improve error check later
632 i = (i * 10) + (c - '0');
633 c = *fmt++;
636 if (nas[i - 1].type == TYPE_UNKNOWN)
637 return false;
639 ap = nas[i - 1].ap;
640 dolPt = fmt;
641 c = *fmt++;
644 // Examine optional flags. Note that we do not implement the
645 // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
646 // somewhat ambiguous and not ideal, which is perhaps why
647 // the various sprintf() implementations are inconsistent
648 // on this feature.
649 while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) {
650 if (c == '-') flags |= FLAG_LEFT;
651 if (c == '+') flags |= FLAG_SIGNED;
652 if (c == ' ') flags |= FLAG_SPACED;
653 if (c == '0') flags |= FLAG_ZEROS;
654 c = *fmt++;
656 if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED;
657 if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS;
659 // width
660 if (c == '*') {
661 c = *fmt++;
662 width = va_arg(ap, int);
663 } else {
664 width = 0;
665 while ((c >= '0') && (c <= '9')) {
666 width = (width * 10) + (c - '0');
667 c = *fmt++;
671 // precision
672 prec = -1;
673 if (c == '.') {
674 c = *fmt++;
675 if (c == '*') {
676 c = *fmt++;
677 prec = va_arg(ap, int);
678 } else {
679 prec = 0;
680 while ((c >= '0') && (c <= '9')) {
681 prec = (prec * 10) + (c - '0');
682 c = *fmt++;
687 // size
688 type = TYPE_INTN;
689 if (c == 'h') {
690 type = TYPE_INT16;
691 c = *fmt++;
692 } else if (c == 'L') {
693 // XXX not quite sure here
694 type = TYPE_INT64;
695 c = *fmt++;
696 } else if (c == 'l') {
697 type = TYPE_INT32;
698 c = *fmt++;
699 if (c == 'l') {
700 type = TYPE_INT64;
701 c = *fmt++;
705 // format
706 hexp = hex;
707 switch (c) {
708 case 'd': case 'i': // decimal/integer
709 radix = 10;
710 goto fetch_and_convert;
712 case 'o': // octal
713 radix = 8;
714 type |= 1;
715 goto fetch_and_convert;
717 case 'u': // unsigned decimal
718 radix = 10;
719 type |= 1;
720 goto fetch_and_convert;
722 case 'x': // unsigned hex
723 radix = 16;
724 type |= 1;
725 goto fetch_and_convert;
727 case 'X': // unsigned HEX
728 radix = 16;
729 hexp = HEX;
730 type |= 1;
731 goto fetch_and_convert;
733 fetch_and_convert:
734 switch (type) {
735 case TYPE_INT16:
736 u.l = va_arg(ap, int);
737 if (u.l < 0) {
738 u.l = -u.l;
739 flags |= FLAG_NEG;
741 goto do_long;
742 case TYPE_UINT16:
743 u.l = va_arg(ap, int) & 0xffff;
744 goto do_long;
745 case TYPE_INTN:
746 u.l = va_arg(ap, int);
747 if (u.l < 0) {
748 u.l = -u.l;
749 flags |= FLAG_NEG;
751 goto do_long;
752 case TYPE_UINTN:
753 u.l = (long)va_arg(ap, unsigned int);
754 goto do_long;
756 case TYPE_INT32:
757 u.l = va_arg(ap, int32_t);
758 if (u.l < 0) {
759 u.l = -u.l;
760 flags |= FLAG_NEG;
762 goto do_long;
763 case TYPE_UINT32:
764 u.l = (long)va_arg(ap, uint32_t);
765 do_long:
766 if (!cvt_l(ss, u.l, width, prec, radix, type, flags, hexp))
767 return false;
769 break;
771 case TYPE_INT64:
772 u.ll = va_arg(ap, int64_t);
773 if (u.ll < 0) {
774 u.ll = -u.ll;
775 flags |= FLAG_NEG;
777 goto do_longlong;
778 case TYPE_UINT64:
779 u.ll = va_arg(ap, uint64_t);
780 do_longlong:
781 if (!cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp))
782 return false;
784 break;
786 break;
788 case 'e':
789 case 'E':
790 case 'f':
791 case 'g':
792 u.d = va_arg(ap, double);
793 if (!nas.empty()) {
794 i = fmt - dolPt;
795 if (i < int(sizeof(pattern))) {
796 pattern[0] = '%';
797 js_memcpy(&pattern[1], dolPt, size_t(i));
798 if (!cvt_f(ss, u.d, pattern, &pattern[i + 1]))
799 return false;
801 } else {
802 if (!cvt_f(ss, u.d, fmt0, fmt))
803 return false;
806 break;
808 case 'c':
809 if ((flags & FLAG_LEFT) == 0) {
810 while (width-- > 1) {
811 if (!(*ss->stuff)(ss, " ", 1))
812 return false;
815 switch (type) {
816 case TYPE_INT16:
817 case TYPE_INTN:
818 u.ch = va_arg(ap, int);
819 if (!(*ss->stuff)(ss, &u.ch, 1))
820 return false;
821 break;
823 if (flags & FLAG_LEFT) {
824 while (width-- > 1) {
825 if (!(*ss->stuff)(ss, " ", 1))
826 return false;
829 break;
831 case 'p':
832 if (sizeof(void*) == sizeof(int32_t)) {
833 type = TYPE_UINT32;
834 } else if (sizeof(void*) == sizeof(int64_t)) {
835 type = TYPE_UINT64;
836 } else if (sizeof(void*) == sizeof(int)) {
837 type = TYPE_UINTN;
838 } else {
839 MOZ_ASSERT(0);
840 break;
842 radix = 16;
843 goto fetch_and_convert;
845 #if 0
846 case 'C':
847 case 'S':
848 case 'E':
849 case 'G':
850 // XXX not supported I suppose
851 MOZ_ASSERT(0);
852 break;
853 #endif
855 case 's':
856 if(type == TYPE_INT16) {
857 u.ws = va_arg(ap, const char16_t*);
858 if (!cvt_s(ss, u.ws, width, prec, flags))
859 return false;
860 } else {
861 u.s = va_arg(ap, const char*);
862 if (!cvt_s(ss, u.s, width, prec, flags))
863 return false;
865 break;
867 case 'n':
868 u.ip = va_arg(ap, int*);
869 if (u.ip) {
870 *u.ip = ss->cur - ss->base;
872 break;
874 default:
875 // Not a % token after all... skip it
876 #if 0
877 MOZ_ASSERT(0);
878 #endif
879 if (!(*ss->stuff)(ss, "%", 1))
880 return false;
881 if (!(*ss->stuff)(ss, fmt - 1, 1))
882 return false;
886 // Stuff trailing NUL
887 if (!(*ss->stuff)(ss, "\0", 1))
888 return false;
890 return true;
893 /************************************************************************/
896 * Stuff routine that automatically grows the js_malloc'd output buffer
897 * before it overflows.
899 static bool
900 GrowStuff(SprintfState* ss, const char* sp, size_t len)
902 ptrdiff_t off;
903 char* newbase;
904 size_t newlen;
906 off = ss->cur - ss->base;
907 if (off + len >= ss->maxlen) {
908 /* Grow the buffer */
909 newlen = ss->maxlen + ((len > 32) ? len : 32);
910 newbase = static_cast<char*>(js_realloc(ss->base, newlen));
911 if (!newbase) {
912 /* Ran out of memory */
913 return false;
915 ss->base = newbase;
916 ss->maxlen = newlen;
917 ss->cur = ss->base + off;
920 /* Copy data */
921 while (len) {
922 --len;
923 *ss->cur++ = *sp++;
925 MOZ_ASSERT(size_t(ss->cur - ss->base) <= ss->maxlen);
926 return true;
930 * sprintf into a js_malloc'd buffer
932 JS_PUBLIC_API(char*)
933 JS_smprintf(const char* fmt, ...)
935 va_list ap;
936 char* rv;
938 va_start(ap, fmt);
939 rv = JS_vsmprintf(fmt, ap);
940 va_end(ap);
941 return rv;
945 * Free memory allocated, for the caller, by JS_smprintf
947 JS_PUBLIC_API(void)
948 JS_smprintf_free(char* mem)
950 js_free(mem);
953 JS_PUBLIC_API(char*)
954 JS_vsmprintf(const char* fmt, va_list ap)
956 SprintfState ss;
958 ss.stuff = GrowStuff;
959 ss.base = 0;
960 ss.cur = 0;
961 ss.maxlen = 0;
962 if (!dosprintf(&ss, fmt, ap)) {
963 js_free(ss.base);
964 return 0;
966 return ss.base;
970 * Stuff routine that discards overflow data
972 static bool
973 LimitStuff(SprintfState* ss, const char* sp, size_t len)
975 size_t limit = ss->maxlen - (ss->cur - ss->base);
977 if (len > limit)
978 len = limit;
979 while (len) {
980 --len;
981 *ss->cur++ = *sp++;
983 return true;
987 * sprintf into a fixed size buffer. Make sure there is a NUL at the end
988 * when finished.
990 JS_PUBLIC_API(uint32_t)
991 JS_snprintf(char* out, uint32_t outlen, const char* fmt, ...)
993 va_list ap;
994 int rv;
996 MOZ_ASSERT(int32_t(outlen) > 0);
997 if (int32_t(outlen) <= 0)
998 return 0;
1000 va_start(ap, fmt);
1001 rv = JS_vsnprintf(out, outlen, fmt, ap);
1002 va_end(ap);
1003 return rv;
1006 JS_PUBLIC_API(uint32_t)
1007 JS_vsnprintf(char* out, uint32_t outlen, const char* fmt, va_list ap)
1009 SprintfState ss;
1010 uint32_t n;
1012 MOZ_ASSERT(int32_t(outlen) > 0);
1013 if (int32_t(outlen) <= 0)
1014 return 0;
1016 ss.stuff = LimitStuff;
1017 ss.base = out;
1018 ss.cur = out;
1019 ss.maxlen = outlen;
1020 (void) dosprintf(&ss, fmt, ap);
1022 /* If we added chars, and we didn't append a null, do it now. */
1023 if (ss.cur != ss.base && ss.cur[-1] != '\0')
1024 ss.cur[-1] = '\0';
1026 n = ss.cur - ss.base;
1027 return n ? n - 1 : n;
1030 JS_PUBLIC_API(char*)
1031 JS_sprintf_append(char* last, const char* fmt, ...)
1033 va_list ap;
1034 char* rv;
1036 va_start(ap, fmt);
1037 rv = JS_vsprintf_append(last, fmt, ap);
1038 va_end(ap);
1039 return rv;
1042 JS_PUBLIC_API(char*)
1043 JS_vsprintf_append(char* last, const char* fmt, va_list ap)
1045 SprintfState ss;
1047 ss.stuff = GrowStuff;
1048 if (last) {
1049 size_t lastlen = strlen(last);
1050 ss.base = last;
1051 ss.cur = last + lastlen;
1052 ss.maxlen = lastlen;
1053 } else {
1054 ss.base = 0;
1055 ss.cur = 0;
1056 ss.maxlen = 0;
1058 if (!dosprintf(&ss, fmt, ap)) {
1059 js_free(ss.base);
1060 return 0;
1062 return ss.base;
1065 #undef TYPE_INT16
1066 #undef TYPE_UINT16
1067 #undef TYPE_INTN
1068 #undef TYPE_UINTN
1069 #undef TYPE_INT32
1070 #undef TYPE_UINT32
1071 #undef TYPE_INT64
1072 #undef TYPE_UINT64
1073 #undef TYPE_STRING
1074 #undef TYPE_DOUBLE
1075 #undef TYPE_INTSTR
1076 #undef TYPE_WSTRING
1077 #undef TYPE_UNKNOWN
1079 #undef FLAG_LEFT
1080 #undef FLAG_SIGNED
1081 #undef FLAG_SPACED
1082 #undef FLAG_ZEROS
1083 #undef FLAG_NEG