/* PARSING TEST */ #define va_arg(va, type) (*(type*)(va)) #define va_start(va, arg) #define va_end(va) #define const #define signed #define volatile typedef void *va_list; typedef unsigned long size_t; typedef long ptrdiff_t; #define STB_SPRINTF_IMPLEMENTATION #ifndef STB_SPRINTF_H_INCLUDE #define STB_SPRINTF_H_INCLUDE /* Single file sprintf replacement. Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. Hereby placed in public domain. This is a full sprintf replacement that supports everything that the C runtime sprintfs support, including float/double, 64-bit integers, hex floats, field parameters (%*.*d stuff), length reads backs, etc. Why would you need this if sprintf already exists? Well, first off, it's *much* faster (see below). It's also much smaller than the CRT versions code-space-wise. We've also added some simple improvements that are super handy (commas in thousands, callbacks at buffer full, for example). Finally, the format strings for MSVC and GCC differ for 64-bit integers (among other small things), so this lets you use the same format strings in cross platform code. It uses the standard single file trick of being both the header file and the source itself. If you just include it normally, you just get the header file function definitions. To get the code, you include it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. It only uses va_args macros from the C runtime to do it's work. It does cast doubles to S64s and shifts and divides U64s, which does drag in CRT code on most platforms. It compiles to roughly 8K with float support, and 4K without. As a comparison, when using MSVC static libs, calling sprintf drags in 16K. API: ==== int stbsp_sprintf( char * buf, char const * fmt, ... ) int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) Convert an arg list into a buffer. stbsp_snprintf always returns a zero-terminated string (unlike regular snprintf). int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns a zero-terminated string (unlike regular snprintf). int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); Convert into a buffer, calling back every STB_SPRINTF_MIN chars. Your callback can then copy the chars out, print them or whatever. This function is actually the workhorse for everything else. The buffer you pass in must hold at least STB_SPRINTF_MIN characters. void stbsp_set_separators( char comma, char period ) Set the comma and period characters to use. FLOATS/DOUBLES: =============== This code uses a internal float->ascii conversion method that uses doubles with error correction (double-doubles, for ~105 bits of precision). This conversion is round-trip perfect - that is, an atof of the values output here will give you the bit-exact double back. One difference is that our insignificant digits will be different than with MSVC or GCC (but they don't match each other either). We also don't attempt to find the minimum length matching float (pre-MSVC15 doesn't either). If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT and you'll save 4K of code space. 64-BIT INTS: ============ This library also supports 64-bit integers and you can use MSVC style or GCC style indicators (%I64d or %lld). It supports the C99 specifiers for size_t and ptr_diff_t (%jd %zd) as well. EXTRAS: ======= Like some GCCs, for integers and floats, you can use a ' (single quote) specifier and commas will be inserted on the thousands: "%'d" on 12345 would print 12,345. For integers and floats, you can use a "$" specifier and the number will be converted to float and then divided to get kilo, mega, giga or tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is "2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three $:s: "%$$$d" -> "2.42 M". To remove the space between the number and the suffix, add "_" specifier: "%_$d" -> "2.53M". In addition to octal and hexadecimal conversions, you can print integers in binary: "%b" for 256 would print 100. PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): =================================================================== "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) "%24d" across all 32-bit ints (4.5x/4.2x faster) "%x" across all 32-bit ints (4.5x/3.8x faster) "%08x" across all 32-bit ints (4.3x/3.8x faster) "%f" across e-10 to e+10 floats (7.3x/6.0x faster) "%e" across e-10 to e+10 floats (8.1x/6.0x faster) "%g" across e-10 to e+10 floats (10.0x/7.1x faster) "%f" for values near e-300 (7.9x/6.5x faster) "%f" for values near e+300 (10.0x/9.1x faster) "%e" for values near e-300 (10.1x/7.0x faster) "%e" for values near e+300 (9.2x/6.0x faster) "%.320f" for values near e-300 (12.6x/11.2x faster) "%a" for random values (8.6x/4.3x faster) "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) "%s%s%s" for 64 char strings (7.1x/7.3x faster) "...512 char string..." ( 35.0x/32.5x faster!) */ #if defined(__clang__) #if defined(__has_feature) && defined(__has_attribute) #if __has_feature(address_sanitizer) #if __has_attribute(__no_sanitize__) #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) #elif __has_attribute(__no_sanitize_address__) #define STBSP__ASAN __attribute__((__no_sanitize_address__)) #elif __has_attribute(__no_address_safety_analysis__) #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) #endif #endif #endif #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ #define STBSP__ASAN __attribute__((__no_sanitize_address__)) #endif #endif #ifndef STBSP__ASAN #define STBSP__ASAN #endif #ifdef STB_SPRINTF_STATIC #define STBSP__PUBLICDEC static #define STBSP__PUBLICDEF static STBSP__ASAN #else #ifdef __cplusplus #define STBSP__PUBLICDEC extern "C" #define STBSP__PUBLICDEF extern "C" STBSP__ASAN #else #define STBSP__PUBLICDEC extern #define STBSP__PUBLICDEF STBSP__ASAN #endif #endif #if defined(__has_attribute) #if __has_attribute(format) #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) #endif #endif #ifndef STBSP__ATTRIBUTE_FORMAT #define STBSP__ATTRIBUTE_FORMAT(fmt,va) #endif #ifdef _MSC_VER #define STBSP__NOTUSED(v) (void)(v) #else #define STBSP__NOTUSED(v) (void)sizeof(v) #endif #ifndef STB_SPRINTF_MIN #define STB_SPRINTF_MIN 512 #endif typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); #ifndef STB_SPRINTF_DECORATE #define STB_SPRINTF_DECORATE(name) stbsp_##name #endif STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); #endif #ifdef STB_SPRINTF_IMPLEMENTATION #define stbsp__uint32 unsigned int #define stbsp__int32 signed int #ifdef _MSC_VER #define stbsp__uint64 unsigned __int64 #define stbsp__int64 signed __int64 #else #define stbsp__uint64 unsigned long #define stbsp__int64 signed long #endif #define stbsp__uint16 unsigned short #ifndef stbsp__uintptr #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) #define stbsp__uintptr stbsp__uint64 #else #define stbsp__uintptr stbsp__uint32 #endif #endif #ifndef STB_SPRINTF_MSVC_MODE #if defined(_MSC_VER) && (_MSC_VER < 1900) #define STB_SPRINTF_MSVC_MODE #endif #endif #ifdef STB_SPRINTF_NOUNALIGNED #define STBSP__UNALIGNED(code) #else #define STBSP__UNALIGNED(code) code #endif #ifndef STB_SPRINTF_NOFLOAT static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); #define STBSP__SPECIAL 0x7000 #endif static char stbsp__period = '.'; static char stbsp__comma = ','; static struct { short temp; char pair[201]; } stbsp__digitpair = { 0, "00010203040506070809101112131415161718192021222324" "25262728293031323334353637383940414243444546474849" "50515253545556575859606162636465666768697071727374" "75767778798081828384858687888990919293949596979899" }; STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) { stbsp__period = pperiod; stbsp__comma = pcomma; } #define STBSP__LEFTJUST 1 #define STBSP__LEADINGPLUS 2 #define STBSP__LEADINGSPACE 4 #define STBSP__LEADING_0X 8 #define STBSP__LEADINGZERO 16 #define STBSP__INTMAX 32 #define STBSP__TRIPLET_COMMA 64 #define STBSP__NEGATIVE 128 #define STBSP__METRIC_SUFFIX 256 #define STBSP__HALFWIDTH 512 #define STBSP__METRIC_NOSPACE 1024 #define STBSP__METRIC_1024 2048 #define STBSP__METRIC_JEDEC 4096 static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { sign[0] = 0; if (fl & STBSP__NEGATIVE) { sign[0] = 1; sign[1] = '-'; } else if (fl & STBSP__LEADINGSPACE) { sign[0] = 1; sign[1] = ' '; } else if (fl & STBSP__LEADINGPLUS) { sign[0] = 1; sign[1] = '+'; } } static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) { char const * sn = s; for (;;) { if (((stbsp__uintptr)sn & 3) == 0) break; if (!limit || *sn == 0) return (stbsp__uint32)(sn - s); ++sn; --limit; } while (limit >= 4) { stbsp__uint32 v = *(stbsp__uint32 *)sn; if ((v - 0x01010101) & (~v) & 0x80808080UL) break; sn += 4; limit -= 4; } while (limit && *sn) { ++sn; --limit; } return (stbsp__uint32)(sn - s); } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { static char hex[] = "0123456789abcdefxp"; static char hexu[] = "0123456789ABCDEFXP"; char *bf; char const *f; int tlen = 0; bf = buf; f = fmt; for (;;) { stbsp__int32 fw, pr, tz; stbsp__uint32 fl; #define stbsp__chk_cb_bufL(bytes) \ { \ int len = (int)(bf - buf); \ if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ tlen += len; \ if (0 == (bf = buf = callback(buf, user, len))) \ goto done; \ } \ } #define stbsp__chk_cb_buf(bytes) \ { \ if (callback) { \ stbsp__chk_cb_bufL(bytes); \ } \ } #define stbsp__flush_cb() \ { \ stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ } #define stbsp__cb_buf_clamp(cl, v) \ cl = v; \ if (callback) { \ int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ if (cl > lg) \ cl = lg; \ } for (;;) { while (((stbsp__uintptr)f) & 3) { schk1: if (f[0] == '%') goto scandd; schk2: if (f[0] == 0) goto endfmt; stbsp__chk_cb_buf(1); *bf++ = f[0]; ++f; } for (;;) { stbsp__uint32 v, c; v = *(stbsp__uint32 *)f; c = (~v) & 0x80808080; if (((v ^ 0x25252525) - 0x01010101) & c) goto schk1; if ((v - 0x01010101) & c) goto schk2; if (callback) if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) goto schk1; #ifdef STB_SPRINTF_NOUNALIGNED if(((stbsp__uintptr)bf) & 3) { bf[0] = f[0]; bf[1] = f[1]; bf[2] = f[2]; bf[3] = f[3]; } else #endif { *(stbsp__uint32 *)bf = v; } bf += 4; f += 4; } } scandd: ++f; fw = 0; pr = -1; fl = 0; tz = 0; for (;;) { switch (f[0]) { case '-': fl |= STBSP__LEFTJUST; ++f; continue; case '+': fl |= STBSP__LEADINGPLUS; ++f; continue; case ' ': fl |= STBSP__LEADINGSPACE; ++f; continue; case '#': fl |= STBSP__LEADING_0X; ++f; continue; case '\'': fl |= STBSP__TRIPLET_COMMA; ++f; continue; case '$': if (fl & STBSP__METRIC_SUFFIX) { if (fl & STBSP__METRIC_1024) { fl |= STBSP__METRIC_JEDEC; } else { fl |= STBSP__METRIC_1024; } } else { fl |= STBSP__METRIC_SUFFIX; } ++f; continue; case '_': fl |= STBSP__METRIC_NOSPACE; ++f; continue; case '0': fl |= STBSP__LEADINGZERO; ++f; goto flags_done; default: goto flags_done; } } flags_done: if (f[0] == '*') { fw = va_arg(va, stbsp__uint32); ++f; } else { while ((f[0] >= '0') && (f[0] <= '9')) { fw = fw * 10 + f[0] - '0'; f++; } } if (f[0] == '.') { ++f; if (f[0] == '*') { pr = va_arg(va, stbsp__uint32); ++f; } else { pr = 0; while ((f[0] >= '0') && (f[0] <= '9')) { pr = pr * 10 + f[0] - '0'; f++; } } } switch (f[0]) { case 'h': fl |= STBSP__HALFWIDTH; ++f; if (f[0] == 'h') ++f; break; case 'l': fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); ++f; if (f[0] == 'l') { fl |= STBSP__INTMAX; ++f; } break; case 'j': fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; ++f; break; case 'z': fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; ++f; break; case 't': fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; ++f; break; case 'I': if ((f[1] == '6') && (f[2] == '4')) { fl |= STBSP__INTMAX; f += 3; } else if ((f[1] == '3') && (f[2] == '2')) { f += 3; } else { fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); ++f; } break; default: break; } switch (f[0]) { #define STBSP__NUMSZ 512 char num[STBSP__NUMSZ]; char lead[8]; char tail[8]; char *s; char const *h; stbsp__uint32 l, n, cs; stbsp__uint64 n64; #ifndef STB_SPRINTF_NOFLOAT double fv; #endif stbsp__int32 dp; char const *sn; case 's': s = va_arg(va, char *); if (s == 0) s = (char *)"null"; l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); lead[0] = 0; tail[0] = 0; pr = 0; dp = 0; cs = 0; goto scopy; case 'c': s = num + STBSP__NUMSZ - 1; *s = (char)va_arg(va, int); l = 1; lead[0] = 0; tail[0] = 0; pr = 0; dp = 0; cs = 0; goto scopy; case 'n': { int *d = va_arg(va, int *); *d = tlen + (int)(bf - buf); } break; #ifdef STB_SPRINTF_NOFLOAT case 'A': case 'a': case 'G': case 'g': case 'E': case 'e': case 'f': va_arg(va, double); s = (char *)"No float"; l = 8; lead[0] = 0; tail[0] = 0; pr = 0; cs = 0; STBSP__NOTUSED(dp); goto scopy; #else case 'A': case 'a': h = (f[0] == 'A') ? hexu : hex; fv = va_arg(va, double); if (pr == -1) pr = 6; if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) fl |= STBSP__NEGATIVE; s = num + 64; stbsp__lead_sign(fl, lead); if (dp == -1023) dp = (n64) ? -1022 : 0; else n64 |= (((stbsp__uint64)1) << 52); n64 <<= (64 - 56); if (pr < 15) n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); #ifdef STB_SPRINTF_MSVC_MODE *s++ = '0'; *s++ = 'x'; #else lead[1 + lead[0]] = '0'; lead[2 + lead[0]] = 'x'; lead[0] += 2; #endif *s++ = h[(n64 >> 60) & 15]; n64 <<= 4; if (pr) *s++ = stbsp__period; sn = s; n = pr; if (n > 13) n = 13; if (pr > (stbsp__int32)n) tz = pr - n; pr = 0; while (n--) { *s++ = h[(n64 >> 60) & 15]; n64 <<= 4; } tail[1] = h[17]; if (dp < 0) { tail[2] = '-'; dp = -dp; } else tail[2] = '+'; n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); tail[0] = (char)n; for (;;) { tail[n] = '0' + dp % 10; if (n <= 3) break; --n; dp /= 10; } dp = (int)(s - sn); l = (int)(s - (num + 64)); s = num + 64; cs = 1 + (3 << 24); goto scopy; case 'G': case 'g': h = (f[0] == 'G') ? hexu : hex; fv = va_arg(va, double); if (pr == -1) pr = 6; else if (pr == 0) pr = 1; if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) fl |= STBSP__NEGATIVE; n = pr; if (l > (stbsp__uint32)pr) l = pr; while ((l > 1) && (pr) && (sn[l - 1] == '0')) { --pr; --l; } if ((dp <= -4) || (dp > (stbsp__int32)n)) { if (pr > (stbsp__int32)l) pr = l - 1; else if (pr) --pr; goto doexpfromg; } if (dp > 0) { pr = (dp < (stbsp__int32)l) ? l - dp : 0; } else { pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); } goto dofloatfromg; case 'E': case 'e': h = (f[0] == 'E') ? hexu : hex; fv = va_arg(va, double); if (pr == -1) pr = 6; if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) fl |= STBSP__NEGATIVE; doexpfromg: tail[0] = 0; stbsp__lead_sign(fl, lead); if (dp == STBSP__SPECIAL) { s = (char *)sn; cs = 0; pr = 0; goto scopy; } s = num + 64; *s++ = sn[0]; if (pr) *s++ = stbsp__period; if ((l - 1) > (stbsp__uint32)pr) l = pr + 1; for (n = 1; n < l; n++) *s++ = sn[n]; tz = pr - (l - 1); pr = 0; tail[1] = h[0xe]; dp -= 1; if (dp < 0) { tail[2] = '-'; dp = -dp; } else tail[2] = '+'; #ifdef STB_SPRINTF_MSVC_MODE n = 5; #else n = (dp >= 100) ? 5 : 4; #endif tail[0] = (char)n; for (;;) { tail[n] = '0' + dp % 10; if (n <= 3) break; --n; dp /= 10; } cs = 1 + (3 << 24); goto flt_lead; case 'f': fv = va_arg(va, double); doafloat: if (fl & STBSP__METRIC_SUFFIX) { double divisor; divisor = 1000.0f; if (fl & STBSP__METRIC_1024) divisor = 1024.0; while (fl < 0x4000000) { if ((fv < divisor) && (fv > -divisor)) break; fv /= divisor; fl += 0x1000000; } } if (pr == -1) pr = 6; if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) fl |= STBSP__NEGATIVE; dofloatfromg: tail[0] = 0; stbsp__lead_sign(fl, lead); if (dp == STBSP__SPECIAL) { s = (char *)sn; cs = 0; pr = 0; goto scopy; } s = num + 64; if (dp <= 0) { stbsp__int32 i; *s++ = '0'; if (pr) *s++ = stbsp__period; n = -dp; if ((stbsp__int32)n > pr) n = pr; i = n; while (i) { if ((((stbsp__uintptr)s) & 3) == 0) break; *s++ = '0'; --i; } while (i >= 4) { *(stbsp__uint32 *)s = 0x30303030; s += 4; i -= 4; } while (i) { *s++ = '0'; --i; } if ((stbsp__int32)(l + n) > pr) l = pr - n; i = l; while (i) { *s++ = *sn++; --i; } tz = pr - (n + l); cs = 1 + (3 << 24); } else { cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; if ((stbsp__uint32)dp >= l) { n = 0; for (;;) { if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { cs = 0; *s++ = stbsp__comma; } else { *s++ = sn[n]; ++n; if (n >= l) break; } } if (n < (stbsp__uint32)dp) { n = dp - n; if ((fl & STBSP__TRIPLET_COMMA) == 0) { while (n) { if ((((stbsp__uintptr)s) & 3) == 0) break; *s++ = '0'; --n; } while (n >= 4) { *(stbsp__uint32 *)s = 0x30303030; s += 4; n -= 4; } } while (n) { if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { cs = 0; *s++ = stbsp__comma; } else { *s++ = '0'; --n; } } } cs = (int)(s - (num + 64)) + (3 << 24); if (pr) { *s++ = stbsp__period; tz = pr; } } else { n = 0; for (;;) { if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { cs = 0; *s++ = stbsp__comma; } else { *s++ = sn[n]; ++n; if (n >= (stbsp__uint32)dp) break; } } cs = (int)(s - (num + 64)) + (3 << 24); if (pr) *s++ = stbsp__period; if ((l - dp) > (stbsp__uint32)pr) l = pr + dp; while (n < l) { *s++ = sn[n]; ++n; } tz = pr - (l - dp); } } pr = 0; if (fl & STBSP__METRIC_SUFFIX) { char idx; idx = 1; if (fl & STBSP__METRIC_NOSPACE) idx = 0; tail[0] = idx; tail[1] = ' '; { if (fl >> 24) { if (fl & STBSP__METRIC_1024) tail[idx + 1] = "_KMGT"[fl >> 24]; else tail[idx + 1] = "_kMGT"[fl >> 24]; idx++; if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { tail[idx + 1] = 'i'; idx++; } tail[0] = idx; } } }; flt_lead: l = (stbsp__uint32)(s - (num + 64)); s = num + 64; goto scopy; #endif case 'B': case 'b': h = (f[0] == 'B') ? hexu : hex; lead[0] = 0; if (fl & STBSP__LEADING_0X) { lead[0] = 2; lead[1] = '0'; lead[2] = h[0xb]; } l = (8 << 4) | (1 << 8); goto radixnum; case 'o': h = hexu; lead[0] = 0; if (fl & STBSP__LEADING_0X) { lead[0] = 1; lead[1] = '0'; } l = (3 << 4) | (3 << 8); goto radixnum; case 'p': fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; pr = sizeof(void *) * 2; fl &= ~STBSP__LEADINGZERO; case 'X': case 'x': h = (f[0] == 'X') ? hexu : hex; l = (4 << 4) | (4 << 8); lead[0] = 0; if (fl & STBSP__LEADING_0X) { lead[0] = 2; lead[1] = '0'; lead[2] = h[16]; } radixnum: if (fl & STBSP__INTMAX) n64 = va_arg(va, stbsp__uint64); else n64 = va_arg(va, stbsp__uint32); s = num + STBSP__NUMSZ; dp = 0; tail[0] = 0; if (n64 == 0) { lead[0] = 0; if (pr == 0) { l = 0; cs = 0; goto scopy; } } for (;;) { *--s = h[n64 & ((1 << (l >> 8)) - 1)]; n64 >>= (l >> 8); if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) break; if (fl & STBSP__TRIPLET_COMMA) { ++l; if ((l & 15) == ((l >> 4) & 15)) { l &= ~15; *--s = stbsp__comma; } } }; cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); goto scopy; case 'u': case 'i': case 'd': if (fl & STBSP__INTMAX) { stbsp__int64 i64 = va_arg(va, stbsp__int64); n64 = (stbsp__uint64)i64; if ((f[0] != 'u') && (i64 < 0)) { n64 = (stbsp__uint64)-i64; fl |= STBSP__NEGATIVE; } } else { stbsp__int32 i = va_arg(va, stbsp__int32); n64 = (stbsp__uint32)i; if ((f[0] != 'u') && (i < 0)) { n64 = (stbsp__uint32)-i; fl |= STBSP__NEGATIVE; } } #ifndef STB_SPRINTF_NOFLOAT if (fl & STBSP__METRIC_SUFFIX) { if (n64 < 1024) pr = 0; else if (pr == -1) pr = 1; fv = (double)(stbsp__int64)n64; goto doafloat; } #endif s = num + STBSP__NUMSZ; l = 0; for (;;) { char *o = s - 8; if (n64 >= 100000000) { n = (stbsp__uint32)(n64 % 100000000); n64 /= 100000000; } else { n = (stbsp__uint32)n64; n64 = 0; } if ((fl & STBSP__TRIPLET_COMMA) == 0) { do { s -= 2; *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; n /= 100; } while (n); } while (n) { if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { l = 0; *--s = stbsp__comma; --o; } else { *--s = (char)(n % 10) + '0'; n /= 10; } } if (n64 == 0) { if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) ++s; break; } while (s != o) if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { l = 0; *--s = stbsp__comma; --o; } else { *--s = '0'; } } tail[0] = 0; stbsp__lead_sign(fl, lead); l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); if (l == 0) { *--s = '0'; l = 1; } cs = l + (3 << 24); if (pr < 0) pr = 0; scopy: if (pr < (stbsp__int32)l) pr = l; n = pr + lead[0] + tail[0] + tz; if (fw < (stbsp__int32)n) fw = n; fw -= n; pr -= l; if ((fl & STBSP__LEFTJUST) == 0) { if (fl & STBSP__LEADINGZERO) { pr = (fw > pr) ? fw : pr; fw = 0; } else { fl &= ~STBSP__TRIPLET_COMMA; } } if (fw + pr) { stbsp__int32 i; stbsp__uint32 c; if ((fl & STBSP__LEFTJUST) == 0) while (fw > 0) { stbsp__cb_buf_clamp(i, fw); fw -= i; while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = ' '; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x20202020; bf += 4; i -= 4; } while (i) { *bf++ = ' '; --i; } stbsp__chk_cb_buf(1); } sn = lead + 1; while (lead[0]) { stbsp__cb_buf_clamp(i, lead[0]); lead[0] -= (char)i; while (i) { *bf++ = *sn++; --i; } stbsp__chk_cb_buf(1); } c = cs >> 24; cs &= 0xffffff; cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; while (pr > 0) { stbsp__cb_buf_clamp(i, pr); pr -= i; if ((fl & STBSP__TRIPLET_COMMA) == 0) { while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = '0'; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x30303030; bf += 4; i -= 4; } } while (i) { if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { cs = 0; *bf++ = stbsp__comma; } else *bf++ = '0'; --i; } stbsp__chk_cb_buf(1); } } sn = lead + 1; while (lead[0]) { stbsp__int32 i; stbsp__cb_buf_clamp(i, lead[0]); lead[0] -= (char)i; while (i) { *bf++ = *sn++; --i; } stbsp__chk_cb_buf(1); } n = l; while (n) { stbsp__int32 i; stbsp__cb_buf_clamp(i, n); n -= i; STBSP__UNALIGNED(while (i >= 4) { *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; bf += 4; s += 4; i -= 4; }) while (i) { *bf++ = *s++; --i; } stbsp__chk_cb_buf(1); } while (tz) { stbsp__int32 i; stbsp__cb_buf_clamp(i, tz); tz -= i; while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = '0'; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x30303030; bf += 4; i -= 4; } while (i) { *bf++ = '0'; --i; } stbsp__chk_cb_buf(1); } sn = tail + 1; while (tail[0]) { stbsp__int32 i; stbsp__cb_buf_clamp(i, tail[0]); tail[0] -= (char)i; while (i) { *bf++ = *sn++; --i; } stbsp__chk_cb_buf(1); } if (fl & STBSP__LEFTJUST) if (fw > 0) { while (fw) { stbsp__int32 i; stbsp__cb_buf_clamp(i, fw); fw -= i; while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = ' '; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x20202020; bf += 4; i -= 4; } while (i--) *bf++ = ' '; stbsp__chk_cb_buf(1); } } break; default: s = num + STBSP__NUMSZ - 1; *s = f[0]; l = 1; fw = fl = 0; lead[0] = 0; tail[0] = 0; pr = 0; dp = 0; cs = 0; goto scopy; } ++f; } endfmt: if (!callback) *bf = 0; else stbsp__flush_cb(); done: return tlen + (int)(bf - buf); } #undef STBSP__LEFTJUST #undef STBSP__LEADINGPLUS #undef STBSP__LEADINGSPACE #undef STBSP__LEADING_0X #undef STBSP__LEADINGZERO #undef STBSP__INTMAX #undef STBSP__TRIPLET_COMMA #undef STBSP__NEGATIVE #undef STBSP__METRIC_SUFFIX #undef STBSP__NUMSZ #undef stbsp__chk_cb_bufL #undef stbsp__chk_cb_buf #undef stbsp__flush_cb #undef stbsp__cb_buf_clamp STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) { int result; va_list va; va_start(va, fmt); result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); va_end(va); return result; } typedef struct stbsp__context { char *buf; int count; int length; char tmp[STB_SPRINTF_MIN]; } stbsp__context; static char *stbsp__clamp_callback(const char *buf, void *user, int len) { stbsp__context *c = (stbsp__context *)user; c->length += len; if (len > c->count) len = c->count; if (len) { if (buf != c->buf) { const char *s, *se; char *d; d = c->buf; s = buf; se = buf + len; do { *d++ = *s++; } while (s < se); } c->buf += len; c->count -= len; } if (c->count <= 0) return c->tmp; return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; } static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) { stbsp__context * c = (stbsp__context*)user; (void) sizeof(buf); c->length += len; return c->tmp; } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) { stbsp__context c; if ( (count == 0) && !buf ) { c.length = 0; STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); } else { int l; c.buf = buf; c.count = count; c.length = 0; STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); l = (int)( c.buf - buf ); if ( l >= count ) l = count - 1; buf[l] = 0; } return c.length; } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) { int result; va_list va; va_start(va, fmt); result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); va_end(va); return result; } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) { return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); } #ifndef STB_SPRINTF_NOFLOAT #define STBSP__COPYFP(dest, src) \ { \ int cn; \ for (cn = 0; cn < 8; cn++) \ ((char *)&dest)[cn] = ((char *)&src)[cn]; \ } static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) { double d; stbsp__int64 b = 0; d = value; STBSP__COPYFP(b, d); *bits = b & ((((stbsp__uint64)1) << 52) - 1); *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); return (stbsp__int32)((stbsp__uint64) b >> 63); } static double const stbsp__bot[23] = { /* 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, */ /* 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 */ 0 }; static double const stbsp__negbot[22] = { 0 /* 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, */ /* 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 */ }; static double const stbsp__negboterr[22] = { 0 /* -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, */ /* 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, */ /* -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, */ /* 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 */ }; static double const stbsp__top[13] = { 0 /* 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 */ }; static double const stbsp__negtop[13] = { 0 /* 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 */ }; static double const stbsp__toperr[13] = { 0 /* 8388608, */ /* 6.8601809640529717e+028, */ /* -7.253143638152921e+052, */ /* -4.3377296974619174e+075, */ /* -1.5559416129466825e+098, */ /* -3.2841562489204913e+121, */ /* -3.7745893248228135e+144, */ /* -1.7356668416969134e+167, */ /* -3.8893577551088374e+190, */ /* -9.9566444326005119e+213, */ /* 6.3641293062232429e+236, */ /* -5.2069140800249813e+259, */ /* -5.2504760255204387e+282 */ }; static double const stbsp__negtoperr[13] = { 0 /* 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, */ /* -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, */ /* 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, */ /* 8.0970921678014997e-317 */ }; #if defined(_MSC_VER) && (_MSC_VER <= 1200) static stbsp__uint64 const stbsp__powten[20] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000U }; #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) #else static stbsp__uint64 const stbsp__powten[20] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000ULL, 100000000000ULL, 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, 10000000000000000000ULL }; #define stbsp__tento19th (1000000000000000000ULL) #endif #define stbsp__ddmulthi(oh, ol, xh, yh) \ { \ double ahi = 0, alo, bhi = 0, blo; \ stbsp__int64 bt; \ oh = xh * yh; \ STBSP__COPYFP(bt, xh); \ bt &= ((~(stbsp__uint64)0) << 27); \ STBSP__COPYFP(ahi, bt); \ alo = xh - ahi; \ STBSP__COPYFP(bt, yh); \ bt &= ((~(stbsp__uint64)0) << 27); \ STBSP__COPYFP(bhi, bt); \ blo = yh - bhi; \ ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ } #define stbsp__ddtoS64(ob, xh, xl) \ { \ double ahi = 0, alo, vh, t; \ ob = (stbsp__int64)xh; \ vh = (double)ob; \ ahi = (xh - vh); \ t = (ahi - xh); \ alo = (xh - (ahi - t)) - (vh + t); \ ob += (stbsp__int64)(ahi + alo + xl); \ } #define stbsp__ddrenorm(oh, ol) \ { \ double s; \ s = oh + ol; \ ol = ol - (s - oh); \ oh = s; \ } #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) { double ph, pl; if ((power >= 0) && (power <= 22)) { stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); } else { stbsp__int32 e, et, eb; double p2h, p2l; e = power; if (power < 0) e = -e; et = (e * 0x2c9) >> 14; /* %23 */ if (et > 13) et = 13; eb = e - (et * 23); ph = d; pl = 0.0; if (power < 0) { if (eb) { --eb; stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); } if (et) { stbsp__ddrenorm(ph, pl); --et; stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); ph = p2h; pl = p2l; } } else { if (eb) { e = eb; if (eb > 22) eb = 22; e -= eb; stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); if (e) { stbsp__ddrenorm(ph, pl); stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); ph = p2h; pl = p2l; } } if (et) { stbsp__ddrenorm(ph, pl); --et; stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); ph = p2h; pl = p2l; } } } stbsp__ddrenorm(ph, pl); *ohi = ph; *olo = pl; } static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) { double d; stbsp__int64 bits = 0; stbsp__int32 expo, e, ng, tens; d = value; STBSP__COPYFP(bits, d); expo = (stbsp__int32)((bits >> 52) & 2047); ng = (stbsp__int32)((stbsp__uint64) bits >> 63); if (ng) d = -d; if (expo == 2047) { *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; *decimal_pos = STBSP__SPECIAL; *len = 3; return ng; } if (expo == 0) { if (((stbsp__uint64) bits << 1) == 0) { *decimal_pos = 1; *start = out; out[0] = '0'; *len = 1; return ng; } { stbsp__int64 v = ((stbsp__uint64)1) << 51; while ((bits & v) == 0) { --expo; v >>= 1; } } } { double ph, pl; tens = expo - 1023; tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); stbsp__ddtoS64(bits, ph, pl); if (((stbsp__uint64)bits) >= stbsp__tento19th) ++tens; } frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); if ((frac_digits < 24)) { stbsp__uint32 dg = 1; if ((stbsp__uint64)bits >= stbsp__powten[9]) dg = 10; while ((stbsp__uint64)bits >= stbsp__powten[dg]) { ++dg; if (dg == 20) goto noround; } if (frac_digits < dg) { stbsp__uint64 r; e = dg - frac_digits; if ((stbsp__uint32)e >= 24) goto noround; r = stbsp__powten[e]; bits = bits + (r / 2); if ((stbsp__uint64)bits >= stbsp__powten[dg]) ++tens; bits /= r; } noround:; } if (bits) { stbsp__uint32 n; for (;;) { if (bits <= 0xffffffff) break; if (bits % 1000) goto donez; bits /= 1000; } n = (stbsp__uint32)bits; while ((n % 1000) == 0) n /= 1000; bits = n; donez:; } out += 64; e = 0; for (;;) { stbsp__uint32 n; char *o = out - 8; if (bits >= 100000000) { n = (stbsp__uint32)(bits % 100000000); bits /= 100000000; } else { n = (stbsp__uint32)bits; bits = 0; } while (n) { out -= 2; *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; n /= 100; e += 2; } if (bits == 0) { if ((e) && (out[0] == '0')) { ++out; --e; } break; } while (out != o) { *--out = '0'; ++e; } } *decimal_pos = tens; *start = out; *len = e; return ng; } #undef stbsp__ddmulthi #undef stbsp__ddrenorm #undef stbsp__ddmultlo #undef stbsp__ddmultlos #undef STBSP__SPECIAL #undef STBSP__COPYFP #endif #undef stbsp__uint16 #undef stbsp__uint32 #undef stbsp__int32 #undef stbsp__uint64 #undef stbsp__int64 #undef STBSP__UNALIGNED #endif /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */