1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
8 * Portable safe sprintf code.
10 * Author: Kipp E.B. Hickman
13 #include "double-conversion/double-to-string.h"
14 #include "mozilla/AllocPolicy.h"
15 #include "mozilla/Printf.h"
16 #include "mozilla/UniquePtrExtensions.h"
17 #include "mozilla/Vector.h"
28 using double_conversion::DoubleToStringConverter
;
29 using DTSC
= DoubleToStringConverter
;
32 * Numbered Argument State
35 int type
; // type of the current ap
36 va_list ap
; // point to the corresponding position on ap
39 using NumArgStateVector
=
40 mozilla::Vector
<NumArgState
, 20, mozilla::MallocAllocPolicy
>;
42 // For values up to and including TYPE_DOUBLE, the lowest bit indicates
43 // whether the type is signed (0) or unsigned (1).
50 #define TYPE_LONGLONG 6
51 #define TYPE_ULONGLONG 7
54 #define TYPE_INTSTR 10
55 #define TYPE_POINTER 11
57 # define TYPE_WSTRING 12
61 #define TYPE_UNKNOWN 20
64 #define FLAG_SIGNED 0x2
65 #define FLAG_SPACED 0x4
66 #define FLAG_ZEROS 0x8
69 static const char hex
[] = "0123456789abcdef";
70 static const char HEX
[] = "0123456789ABCDEF";
72 // Fill into the buffer using the data in src
73 bool mozilla::PrintfTarget::fill2(const char* src
, int srclen
, int width
,
78 if (width
> 0 && (flags
& FLAG_LEFT
) == 0) { // Right adjusting
79 if (flags
& FLAG_ZEROS
) {
82 while (--width
>= 0) {
83 if (!emit(&space
, 1)) {
89 // Copy out the source data
90 if (!emit(src
, srclen
)) {
94 if (width
> 0 && (flags
& FLAG_LEFT
) != 0) { // Left adjusting
95 while (--width
>= 0) {
96 if (!emit(&space
, 1)) {
105 * Fill a number. The order is: optional-sign zero-filling conversion-digits
107 bool mozilla::PrintfTarget::fill_n(const char* src
, int srclen
, int width
,
108 int prec
, int type
, int flags
) {
116 if ((type
& 1) == 0) {
117 if (flags
& FLAG_NEG
) {
119 } else if (flags
& FLAG_SIGNED
) {
121 } else if (flags
& FLAG_SPACED
) {
125 cvtwidth
= (sign
? 1 : 0) + srclen
;
127 if (prec
> 0 && (type
!= TYPE_DOUBLE
)) {
129 precwidth
= prec
- srclen
; // Need zero filling
130 cvtwidth
+= precwidth
;
134 if ((flags
& FLAG_ZEROS
) && ((type
== TYPE_DOUBLE
) || (prec
< 0))) {
135 if (width
> cvtwidth
) {
136 zerowidth
= width
- cvtwidth
; // Zero filling
137 cvtwidth
+= zerowidth
;
141 if (flags
& FLAG_LEFT
) {
142 if (width
> cvtwidth
) {
143 // Space filling on the right (i.e. left adjusting)
144 rightspaces
= width
- cvtwidth
;
147 if (width
> cvtwidth
) {
148 // Space filling on the left (i.e. right adjusting)
149 leftspaces
= width
- cvtwidth
;
152 while (--leftspaces
>= 0) {
158 if (!emit(&sign
, 1)) {
162 while (--precwidth
>= 0) {
167 while (--zerowidth
>= 0) {
172 if (!emit(src
, uint32_t(srclen
))) {
175 while (--rightspaces
>= 0) {
183 // All that the cvt_* functions care about as far as the TYPE_* constants is
184 // that the low bit is set to indicate unsigned, or unset to indicate signed.
185 // So we don't try to hard to ensure that the passed TYPE_* constant lines
186 // up with the actual size of the number being printed here. The main printf
187 // code, below, does have to care so that the correct bits are extracted from
189 bool mozilla::PrintfTarget::appendIntDec(int32_t num
) {
196 return cvt_l(n
, -1, -1, 10, TYPE_INTN
, flags
, hex
);
199 bool mozilla::PrintfTarget::appendIntDec(uint32_t num
) {
200 return cvt_l(num
, -1, -1, 10, TYPE_UINTN
, 0, hex
);
203 bool mozilla::PrintfTarget::appendIntOct(uint32_t num
) {
204 return cvt_l(num
, -1, -1, 8, TYPE_UINTN
, 0, hex
);
207 bool mozilla::PrintfTarget::appendIntHex(uint32_t num
) {
208 return cvt_l(num
, -1, -1, 16, TYPE_UINTN
, 0, hex
);
211 bool mozilla::PrintfTarget::appendIntDec(int64_t num
) {
217 return cvt_ll(num
, -1, -1, 10, TYPE_INTN
, flags
, hex
);
220 bool mozilla::PrintfTarget::appendIntDec(uint64_t num
) {
221 return cvt_ll(num
, -1, -1, 10, TYPE_UINTN
, 0, hex
);
224 bool mozilla::PrintfTarget::appendIntOct(uint64_t num
) {
225 return cvt_ll(num
, -1, -1, 8, TYPE_UINTN
, 0, hex
);
228 bool mozilla::PrintfTarget::appendIntHex(uint64_t num
) {
229 return cvt_ll(num
, -1, -1, 16, TYPE_UINTN
, 0, hex
);
232 /* Convert a long into its printable form. */
233 bool mozilla::PrintfTarget::cvt_l(long num
, int width
, int prec
, int radix
,
234 int type
, int flags
, const char* hexp
) {
239 // according to the man page this needs to happen
240 if ((prec
== 0) && (num
== 0)) {
241 return fill_n("", 0, width
, prec
, type
, flags
);
244 // Converting decimal is a little tricky. In the unsigned case we
245 // need to stop when we hit 10 digits. In the signed case, we can
246 // stop when the number is zero.
247 cvt
= cvtbuf
+ sizeof(cvtbuf
);
250 int digit
= (((unsigned long)num
) % radix
) & 0xF;
251 *--cvt
= hexp
[digit
];
253 num
= (long)(((unsigned long)num
) / radix
);
260 // Now that we have the number converted without its sign, deal with
261 // the sign and zero padding.
262 return fill_n(cvt
, digits
, width
, prec
, type
, flags
);
265 /* Convert a 64-bit integer into its printable form. */
266 bool mozilla::PrintfTarget::cvt_ll(int64_t num
, int width
, int prec
, int radix
,
267 int type
, int flags
, const char* hexp
) {
268 // According to the man page, this needs to happen.
269 if (prec
== 0 && num
== 0) {
270 return fill_n("", 0, width
, prec
, type
, flags
);
273 // Converting decimal is a little tricky. In the unsigned case we
274 // need to stop when we hit 10 digits. In the signed case, we can
275 // stop when the number is zero.
276 int64_t rad
= int64_t(radix
);
278 char* cvt
= cvtbuf
+ sizeof(cvtbuf
);
281 int64_t quot
= uint64_t(num
) / rad
;
282 int64_t rem
= uint64_t(num
) % rad
;
283 int32_t digit
= int32_t(rem
);
284 *--cvt
= hexp
[digit
& 0xf];
293 // Now that we have the number converted without its sign, deal with
294 // the sign and zero padding.
295 return fill_n(cvt
, digits
, width
, prec
, type
, flags
);
299 constexpr static size_t lengthof(const char (&)[N
]) {
303 // Longest possible output from ToFixed for positive numbers:
304 // [0-9]{kMaxFixedDigitsBeforePoint}\.[0-9]{kMaxFixedDigitsAfterPoint}?
305 constexpr int FIXED_MAX_CHARS
=
306 DTSC::kMaxFixedDigitsBeforePoint
+ 1 + DTSC::kMaxFixedDigitsAfterPoint
;
308 // Longest possible output from ToExponential:
309 // [1-9]\.[0-9]{kMaxExponentialDigits}e[+-][0-9]{1,3}
310 // (std::numeric_limits<double>::max() has exponent 308).
311 constexpr int EXPONENTIAL_MAX_CHARS
=
312 lengthof("1.") + DTSC::kMaxExponentialDigits
+ lengthof("e+999");
314 // Longest possible output from ToPrecise:
315 // [0-9\.]{kMaxPrecisionDigits+1} or
316 // [1-9]\.[0-9]{kMaxPrecisionDigits-1}e[+-][0-9]{1,3}
317 constexpr int PRECISE_MAX_CHARS
=
318 lengthof("1.") + DTSC::kMaxPrecisionDigits
- 1 + lengthof("e+999");
320 constexpr int DTSC_MAX_CHARS
=
321 std::max({FIXED_MAX_CHARS
, EXPONENTIAL_MAX_CHARS
, PRECISE_MAX_CHARS
});
324 * Convert a double precision floating point number into its printable
327 bool mozilla::PrintfTarget::cvt_f(double d
, char c
, int width
, int prec
,
329 bool lower
= islower(c
);
330 const char* inf
= lower
? "inf" : "INF";
331 const char* nan
= lower
? "nan" : "NAN";
332 char e
= lower
? 'e' : 'E';
333 DoubleToStringConverter
converter(DTSC::UNIQUE_ZERO
| DTSC::NO_TRAILING_ZERO
|
334 DTSC::EMIT_POSITIVE_EXPONENT_SIGN
,
335 inf
, nan
, e
, 0, 0, 4, 0, 2);
336 // Longest of the above cases, plus space for a terminal nul character.
337 char buf
[DTSC_MAX_CHARS
+ 1];
338 double_conversion::StringBuilder
builder(buf
, sizeof(buf
));
339 bool success
= false;
340 if (std::signbit(d
)) {
344 if (!std::isfinite(d
)) {
345 flags
&= ~FLAG_ZEROS
;
347 // "If the precision is missing, it shall be taken as 6."
354 success
= converter
.ToExponential(d
, prec
, &builder
);
358 success
= converter
.ToFixed(d
, prec
, &builder
);
362 // "If an explicit precision is zero, it shall be taken as 1."
363 success
= converter
.ToPrecision(d
, prec
? prec
: 1, &builder
);
369 int len
= builder
.position();
370 char* cvt
= builder
.Finalize();
371 return fill_n(cvt
, len
, width
, prec
, TYPE_DOUBLE
, flags
);
375 * Convert a string into its printable form. "width" is the output
376 * width. "prec" is the maximum number of characters of "s" to output,
377 * where -1 means until NUL.
379 bool mozilla::PrintfTarget::cvt_s(const char* s
, int width
, int prec
,
388 // Limit string length by precision value
389 int slen
= int(strlen(s
));
390 if (0 < prec
&& prec
< slen
) {
395 return fill2(s
, slen
, width
, flags
);
399 * BuildArgArray stands for Numbered Argument list Sprintf
401 * fmp = "%4$i, %2$d, %3s, %1d";
402 * the number must start from 1, and no gap among them
404 static bool BuildArgArray(const char* fmt
, va_list ap
, NumArgStateVector
& nas
) {
405 size_t number
= 0, cn
= 0, i
;
410 // Detemine how many legal % I have got, then allocate space.
414 while ((c
= *p
++) != 0) {
418 if ((c
= *p
++) == '%') { // skip %% case
423 if (c
> '9' || c
< '0') {
424 if (c
== '$') { // numbered argument case
426 MOZ_CRASH("Bad format string");
429 } else { // non-numbered argument case
431 MOZ_CRASH("Bad format string");
446 // Only allow a limited number of arguments.
447 MOZ_RELEASE_ASSERT(number
<= 20);
449 if (!nas
.growByUninitialized(number
)) {
453 for (i
= 0; i
< number
; i
++) {
454 nas
[i
].type
= TYPE_UNKNOWN
;
461 while ((c
= *p
++) != 0) {
471 while (c
&& c
!= '$') { // should improve error check later
472 cn
= cn
* 10 + c
- '0';
476 if (!c
|| cn
< 1 || cn
> number
) {
477 MOZ_CRASH("Bad format string");
480 // nas[cn] starts from 0, and make sure nas[cn].type is not assigned.
482 if (nas
[cn
].type
!= TYPE_UNKNOWN
) {
489 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
495 // not supported feature, for the argument is not numbered
496 MOZ_CRASH("Bad format string");
499 while ((c
>= '0') && (c
<= '9')) {
507 // not supported feature, for the argument is not numbered
508 MOZ_CRASH("Bad format string");
511 while ((c
>= '0') && (c
<= '9')) {
517 nas
[cn
].type
= TYPE_INTN
;
519 nas
[cn
].type
= TYPE_SHORT
;
522 nas
[cn
].type
= TYPE_SCHAR
;
525 } else if (c
== 'L') {
526 nas
[cn
].type
= TYPE_LONGLONG
;
528 } else if (c
== 'l') {
529 nas
[cn
].type
= TYPE_LONG
;
532 nas
[cn
].type
= TYPE_LONGLONG
;
535 } else if (c
== 'z' || c
== 'I') {
536 static_assert(sizeof(size_t) == sizeof(int) ||
537 sizeof(size_t) == sizeof(long) ||
538 sizeof(size_t) == sizeof(long long),
539 "size_t is not one of the expected sizes");
540 nas
[cn
].type
= sizeof(size_t) == sizeof(int) ? TYPE_INTN
541 : sizeof(size_t) == sizeof(long) ? TYPE_LONG
544 } else if (c
== 't') {
545 static_assert(sizeof(ptrdiff_t) == sizeof(int) ||
546 sizeof(ptrdiff_t) == sizeof(long) ||
547 sizeof(ptrdiff_t) == sizeof(long long),
548 "ptrdiff_t is not one of the expected sizes");
549 nas
[cn
].type
= sizeof(ptrdiff_t) == sizeof(int) ? TYPE_INTN
550 : sizeof(ptrdiff_t) == sizeof(long) ? TYPE_LONG
553 } else if (c
== 'j') {
554 static_assert(sizeof(intmax_t) == sizeof(int) ||
555 sizeof(intmax_t) == sizeof(long) ||
556 sizeof(intmax_t) == sizeof(long long),
557 "intmax_t is not one of the expected sizes");
558 nas
[cn
].type
= sizeof(intmax_t) == sizeof(int) ? TYPE_INTN
559 : sizeof(intmax_t) == sizeof(long) ? TYPE_LONG
575 // Mark as unsigned type.
585 nas
[cn
].type
= TYPE_DOUBLE
;
589 nas
[cn
].type
= TYPE_POINTER
;
594 nas
[cn
].type
= TYPE_WSTRING
;
597 nas
[cn
].type
= TYPE_UNKNOWN
;
603 if (nas
[cn
].type
== TYPE_LONG
) {
604 nas
[cn
].type
= TYPE_WSTRING
;
608 // Other type sizes are not supported here.
609 MOZ_ASSERT(nas
[cn
].type
== TYPE_INTN
);
610 nas
[cn
].type
= TYPE_STRING
;
614 nas
[cn
].type
= TYPE_INTSTR
;
619 nas
[cn
].type
= TYPE_UNKNOWN
;
624 if (nas
[cn
].type
== TYPE_UNKNOWN
) {
625 MOZ_CRASH("Bad format string");
633 while (cn
< number
) {
634 // A TYPE_UNKNOWN here means that the format asked for a
635 // positional argument without specifying the meaning of some
637 MOZ_ASSERT(nas
[cn
].type
!= TYPE_UNKNOWN
);
639 va_copy(nas
[cn
].ap
, ap
);
641 switch (nas
[cn
].type
) {
648 (void)va_arg(ap
, int);
651 (void)va_arg(ap
, long);
654 (void)va_arg(ap
, unsigned long);
657 (void)va_arg(ap
, long long);
660 (void)va_arg(ap
, unsigned long long);
663 (void)va_arg(ap
, char*);
666 (void)va_arg(ap
, int*);
669 (void)va_arg(ap
, double);
672 (void)va_arg(ap
, void*);
676 (void)va_arg(ap
, wchar_t*);
690 mozilla::PrintfTarget::PrintfTarget() : mEmitted(0) {}
692 bool mozilla::PrintfTarget::vprint(const char* fmt
, va_list ap
) {
694 int flags
, width
, prec
, radix
, type
;
711 // Build an argument array, IF the fmt is numbered argument
712 // list style, to contain the Numbered Argument list pointers.
714 NumArgStateVector nas
;
715 if (!BuildArgArray(fmt
, ap
, nas
)) {
716 // the fmt contains error Numbered Argument format, jliu@netscape.com
717 MOZ_CRASH("Bad format string");
720 while ((c
= *fmt
++) != 0) {
722 if (!emit(fmt
- 1, 1)) {
729 // Gobble up the % format string. Hopefully we have handled all
730 // of the strange cases!
734 // quoting a % with %%
735 if (!emit(fmt
- 1, 1)) {
743 // the fmt contains the Numbered Arguments feature
745 while (c
&& c
!= '$') { // should improve error check later
746 i
= (i
* 10) + (c
- '0');
750 if (nas
[i
- 1].type
== TYPE_UNKNOWN
) {
751 MOZ_CRASH("Bad format string");
758 // Examine optional flags. Note that we do not implement the
759 // '#' flag of sprintf(). The ANSI C spec. of the '#' flag is
760 // somewhat ambiguous and not ideal, which is perhaps why
761 // the various sprintf() implementations are inconsistent
763 while ((c
== '-') || (c
== '+') || (c
== ' ') || (c
== '0')) {
768 flags
|= FLAG_SIGNED
;
771 flags
|= FLAG_SPACED
;
778 if (flags
& FLAG_SIGNED
) {
779 flags
&= ~FLAG_SPACED
;
781 if (flags
& FLAG_LEFT
) {
782 flags
&= ~FLAG_ZEROS
;
788 width
= va_arg(ap
, int);
792 flags
&= ~FLAG_ZEROS
;
796 while ((c
>= '0') && (c
<= '9')) {
797 width
= (width
* 10) + (c
- '0');
808 prec
= va_arg(ap
, int);
811 while ((c
>= '0') && (c
<= '9')) {
812 prec
= (prec
* 10) + (c
- '0');
827 } else if (c
== 'L') {
828 type
= TYPE_LONGLONG
;
830 } else if (c
== 'l') {
834 type
= TYPE_LONGLONG
;
837 } else if (c
== 'z' || c
== 'I') {
838 static_assert(sizeof(size_t) == sizeof(int) ||
839 sizeof(size_t) == sizeof(long) ||
840 sizeof(size_t) == sizeof(long long),
841 "size_t is not one of the expected sizes");
842 type
= sizeof(size_t) == sizeof(int) ? TYPE_INTN
843 : sizeof(size_t) == sizeof(long) ? TYPE_LONG
846 } else if (c
== 't') {
847 static_assert(sizeof(ptrdiff_t) == sizeof(int) ||
848 sizeof(ptrdiff_t) == sizeof(long) ||
849 sizeof(ptrdiff_t) == sizeof(long long),
850 "ptrdiff_t is not one of the expected sizes");
851 type
= sizeof(ptrdiff_t) == sizeof(int) ? TYPE_INTN
852 : sizeof(ptrdiff_t) == sizeof(long) ? TYPE_LONG
855 } else if (c
== 'j') {
856 static_assert(sizeof(intmax_t) == sizeof(int) ||
857 sizeof(intmax_t) == sizeof(long) ||
858 sizeof(intmax_t) == sizeof(long long),
859 "intmax_t is not one of the expected sizes");
860 type
= sizeof(intmax_t) == sizeof(int) ? TYPE_INTN
861 : sizeof(intmax_t) == sizeof(long) ? TYPE_LONG
870 case 'i': // decimal/integer
872 goto fetch_and_convert
;
877 goto fetch_and_convert
;
879 case 'u': // unsigned decimal
882 goto fetch_and_convert
;
884 case 'x': // unsigned hex
887 goto fetch_and_convert
;
889 case 'X': // unsigned HEX
893 goto fetch_and_convert
;
898 u
.l
= (signed char)va_arg(ap
, int);
905 u
.l
= (unsigned char)va_arg(ap
, unsigned int);
908 u
.l
= (short)va_arg(ap
, int);
915 u
.l
= (unsigned short)va_arg(ap
, unsigned int);
918 u
.l
= va_arg(ap
, int);
925 u
.l
= (long)va_arg(ap
, unsigned int);
929 u
.l
= va_arg(ap
, long);
936 u
.l
= (long)va_arg(ap
, unsigned long);
938 if (!cvt_l(u
.l
, width
, prec
, radix
, type
, flags
, hexp
)) {
945 u
.ll
= va_arg(ap
, long long);
952 u
.ll
= (uintptr_t)va_arg(ap
, void*);
955 u
.ll
= va_arg(ap
, unsigned long long);
957 if (!cvt_ll(u
.ll
, width
, prec
, radix
, type
, flags
, hexp
)) {
971 u
.d
= va_arg(ap
, double);
972 if (!cvt_f(u
.d
, c
, width
, prec
, flags
)) {
979 if ((flags
& FLAG_LEFT
) == 0) {
980 while (width
-- > 1) {
989 u
.ch
= va_arg(ap
, int);
990 if (!emit(&u
.ch
, 1)) {
995 if (flags
& FLAG_LEFT
) {
996 while (width
-- > 1) {
1005 type
= TYPE_POINTER
;
1007 goto fetch_and_convert
;
1010 if (type
== TYPE_INTN
) {
1011 u
.s
= va_arg(ap
, const char*);
1012 if (!cvt_s(u
.s
, width
, prec
, flags
)) {
1017 MOZ_ASSERT(type
== TYPE_LONG
);
1022 u
.ws
= va_arg(ap
, const wchar_t*);
1024 int rv
= WideCharToMultiByte(CP_ACP
, 0, u
.ws
, -1, NULL
, 0, NULL
, NULL
);
1025 if (rv
== 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION
) {
1026 if (!cvt_s("<unicode errors in string>", width
, prec
, flags
)) {
1033 UniqueFreePtr
<char[]> buf((char*)malloc(rv
));
1034 WideCharToMultiByte(CP_ACP
, 0, u
.ws
, -1, buf
.get(), rv
, NULL
, NULL
);
1037 if (!cvt_s(buf
.get(), width
, prec
, flags
)) {
1043 // Not supported here.
1049 u
.ip
= va_arg(ap
, int*);
1056 // Not a % token after all... skip it
1057 if (!emit("%", 1)) {
1060 if (!emit(fmt
- 1, 1)) {
1069 /************************************************************************/
1071 bool mozilla::PrintfTarget::print(const char* format
, ...) {
1074 va_start(ap
, format
);
1075 bool result
= vprint(format
, ap
);
1086 #undef TYPE_LONGLONG
1087 #undef TYPE_ULONGLONG