sgdk
|
00001 // stb_sprintf - v1.06 - public domain snprintf() implementation 00002 // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 00003 // http://github.com/nothings/stb 00004 // 00005 // allowed types: sc uidBboXx p AaGgEef n 00006 // lengths : h ll j z t I64 I32 I 00007 // 00008 // Contributors: 00009 // Fabian "ryg" Giesen (reformatting) 00010 // 00011 // Contributors (bugfixes): 00012 // github:d26435 00013 // github:trex78 00014 // github:account-login 00015 // Jari Komppa (SI suffixes) 00016 // Rohit Nirmal 00017 // Marcin Wojdyr 00018 // Leonard Ritter 00019 // Stefano Zanotti 00020 // Adam Allison 00021 // 00022 // LICENSE: 00023 // 00024 // See end of file for license information. 00025 00026 #ifndef STB_SPRINTF_H_INCLUDE 00027 #define STB_SPRINTF_H_INCLUDE 00028 00029 /* 00030 Single file sprintf replacement. 00031 00032 Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. 00033 Hereby placed in public domain. 00034 00035 This is a full sprintf replacement that supports everything that 00036 the C runtime sprintfs support, including float/double, 64-bit integers, 00037 hex floats, field parameters (%*.*d stuff), length reads backs, etc. 00038 00039 Why would you need this if sprintf already exists? Well, first off, 00040 it's *much* faster (see below). It's also much smaller than the CRT 00041 versions code-space-wise. We've also added some simple improvements 00042 that are super handy (commas in thousands, callbacks at buffer full, 00043 for example). Finally, the format strings for MSVC and GCC differ 00044 for 64-bit integers (among other small things), so this lets you use 00045 the same format strings in cross platform code. 00046 00047 It uses the standard single file trick of being both the header file 00048 and the source itself. If you just include it normally, you just get 00049 the header file function definitions. To get the code, you include 00050 it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. 00051 00052 It only uses va_args macros from the C runtime to do it's work. It 00053 does cast doubles to S64s and shifts and divides U64s, which does 00054 drag in CRT code on most platforms. 00055 00056 It compiles to roughly 8K with float support, and 4K without. 00057 As a comparison, when using MSVC static libs, calling sprintf drags 00058 in 16K. 00059 00060 API: 00061 ==== 00062 int stbsp_sprintf( char * buf, char const * fmt, ... ) 00063 int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) 00064 Convert an arg list into a buffer. stbsp_snprintf always returns 00065 a zero-terminated string (unlike regular snprintf). 00066 00067 int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) 00068 int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) 00069 Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns 00070 a zero-terminated string (unlike regular snprintf). 00071 00072 int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) 00073 typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); 00074 Convert into a buffer, calling back every STB_SPRINTF_MIN chars. 00075 Your callback can then copy the chars out, print them or whatever. 00076 This function is actually the workhorse for everything else. 00077 The buffer you pass in must hold at least STB_SPRINTF_MIN characters. 00078 // you return the next buffer to use or 0 to stop converting 00079 00080 void stbsp_set_separators( char comma, char period ) 00081 Set the comma and period characters to use. 00082 00083 FLOATS/DOUBLES: 00084 =============== 00085 This code uses a internal float->ascii conversion method that uses 00086 doubles with error correction (double-doubles, for ~105 bits of 00087 precision). This conversion is round-trip perfect - that is, an atof 00088 of the values output here will give you the bit-exact double back. 00089 00090 One difference is that our insignificant digits will be different than 00091 with MSVC or GCC (but they don't match each other either). We also 00092 don't attempt to find the minimum length matching float (pre-MSVC15 00093 doesn't either). 00094 00095 If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT 00096 and you'll save 4K of code space. 00097 00098 64-BIT INTS: 00099 ============ 00100 This library also supports 64-bit integers and you can use MSVC style or 00101 GCC style indicators (%I64d or %lld). It supports the C99 specifiers 00102 for size_t and ptr_diff_t (%jd %zd) as well. 00103 00104 EXTRAS: 00105 ======= 00106 Like some GCCs, for integers and floats, you can use a ' (single quote) 00107 specifier and commas will be inserted on the thousands: "%'d" on 12345 00108 would print 12,345. 00109 00110 For integers and floats, you can use a "$" specifier and the number 00111 will be converted to float and then divided to get kilo, mega, giga or 00112 tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is 00113 "2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 00114 2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three 00115 $:s: "%$$$d" -> "2.42 M". To remove the space between the number and the 00116 suffix, add "_" specifier: "%_$d" -> "2.53M". 00117 00118 In addition to octal and hexadecimal conversions, you can print 00119 integers in binary: "%b" for 256 would print 100. 00120 00121 PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): 00122 =================================================================== 00123 "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) 00124 "%24d" across all 32-bit ints (4.5x/4.2x faster) 00125 "%x" across all 32-bit ints (4.5x/3.8x faster) 00126 "%08x" across all 32-bit ints (4.3x/3.8x faster) 00127 "%f" across e-10 to e+10 floats (7.3x/6.0x faster) 00128 "%e" across e-10 to e+10 floats (8.1x/6.0x faster) 00129 "%g" across e-10 to e+10 floats (10.0x/7.1x faster) 00130 "%f" for values near e-300 (7.9x/6.5x faster) 00131 "%f" for values near e+300 (10.0x/9.1x faster) 00132 "%e" for values near e-300 (10.1x/7.0x faster) 00133 "%e" for values near e+300 (9.2x/6.0x faster) 00134 "%.320f" for values near e-300 (12.6x/11.2x faster) 00135 "%a" for random values (8.6x/4.3x faster) 00136 "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) 00137 "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) 00138 "%s%s%s" for 64 char strings (7.1x/7.3x faster) 00139 "...512 char string..." ( 35.0x/32.5x faster!) 00140 */ 00141 00142 #if defined(__has_feature) 00143 #if __has_feature(address_sanitizer) 00144 #define STBI__ASAN __attribute__((no_sanitize("address"))) 00145 #endif 00146 #endif 00147 #ifndef STBI__ASAN 00148 #define STBI__ASAN 00149 #endif 00150 00151 #ifdef STB_SPRINTF_STATIC 00152 #define STBSP__PUBLICDEC static 00153 #define STBSP__PUBLICDEF static STBI__ASAN 00154 #else 00155 #ifdef __cplusplus 00156 #define STBSP__PUBLICDEC extern "C" 00157 #define STBSP__PUBLICDEF extern "C" STBI__ASAN 00158 #else 00159 #define STBSP__PUBLICDEC extern 00160 #define STBSP__PUBLICDEF STBI__ASAN 00161 #endif 00162 #endif 00163 00164 #ifndef STB_SPRINTF_NOSTD 00165 #include <stdarg.h> // for va_list() 00166 #include <stddef.h> // size_t, ptrdiff_t 00167 #endif 00168 00169 #ifndef STB_SPRINTF_MIN 00170 #define STB_SPRINTF_MIN 512 // how many characters per callback 00171 #endif 00172 typedef char *STBSP_SPRINTFCB(char *buf, void *user, int len); 00173 00174 #ifndef STB_SPRINTF_DECORATE 00175 #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names 00176 #endif 00177 00178 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); 00179 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); 00180 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...); 00181 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...); 00182 00183 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); 00184 STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); 00185 00186 #endif // STB_SPRINTF_H_INCLUDE 00187 00188 #ifdef STB_SPRINTF_IMPLEMENTATION 00189 00190 #ifndef STB_SPRINTF_NOSTD 00191 #include <stdlib.h> // for va_arg() 00192 #endif 00193 00194 #define stbsp__uint32 unsigned int 00195 #define stbsp__int32 signed int 00196 00197 #ifdef _MSC_VER 00198 #define stbsp__uint64 unsigned __int64 00199 #define stbsp__int64 signed __int64 00200 #else 00201 #define stbsp__uint64 unsigned long long 00202 #define stbsp__int64 signed long long 00203 #endif 00204 #define stbsp__uint16 unsigned short 00205 00206 #ifndef stbsp__uintptr 00207 #if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) 00208 #define stbsp__uintptr stbsp__uint64 00209 #else 00210 #define stbsp__uintptr stbsp__uint32 00211 #endif 00212 #endif 00213 00214 #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) 00215 #if defined(_MSC_VER) && (_MSC_VER < 1900) 00216 #define STB_SPRINTF_MSVC_MODE 00217 #endif 00218 #endif 00219 00220 #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses 00221 #define STBSP__UNALIGNED(code) 00222 #else 00223 #define STBSP__UNALIGNED(code) code 00224 #endif 00225 00226 #ifndef STB_SPRINTF_NOFLOAT 00227 // internal float utility functions 00228 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); 00229 static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); 00230 #define STBSP__SPECIAL 0x7000 00231 #endif 00232 00233 static char stbsp__period = '.'; 00234 static char stbsp__comma = ','; 00235 static struct 00236 { 00237 short temp; // force next field to be 2-byte aligned 00238 char pair[201]; 00239 } stbsp__digitpair = 00240 { 00241 0, 00242 "00010203040506070809101112131415161718192021222324" 00243 "25262728293031323334353637383940414243444546474849" 00244 "50515253545556575859606162636465666768697071727374" 00245 "75767778798081828384858687888990919293949596979899" 00246 }; 00247 00248 STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) 00249 { 00250 stbsp__period = pperiod; 00251 stbsp__comma = pcomma; 00252 } 00253 00254 #define STBSP__LEFTJUST 1 00255 #define STBSP__LEADINGPLUS 2 00256 #define STBSP__LEADINGSPACE 4 00257 #define STBSP__LEADING_0X 8 00258 #define STBSP__LEADINGZERO 16 00259 #define STBSP__INTMAX 32 00260 #define STBSP__TRIPLET_COMMA 64 00261 #define STBSP__NEGATIVE 128 00262 #define STBSP__METRIC_SUFFIX 256 00263 #define STBSP__HALFWIDTH 512 00264 #define STBSP__METRIC_NOSPACE 1024 00265 #define STBSP__METRIC_1024 2048 00266 #define STBSP__METRIC_JEDEC 4096 00267 00268 static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) 00269 { 00270 sign[0] = 0; 00271 if (fl & STBSP__NEGATIVE) { 00272 sign[0] = 1; 00273 sign[1] = '-'; 00274 } else if (fl & STBSP__LEADINGSPACE) { 00275 sign[0] = 1; 00276 sign[1] = ' '; 00277 } else if (fl & STBSP__LEADINGPLUS) { 00278 sign[0] = 1; 00279 sign[1] = '+'; 00280 } 00281 } 00282 00283 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) 00284 { 00285 static char hex[] = "0123456789abcdefxp"; 00286 static char hexu[] = "0123456789ABCDEFXP"; 00287 char *bf; 00288 char const *f; 00289 int tlen = 0; 00290 00291 bf = buf; 00292 f = fmt; 00293 for (;;) { 00294 stbsp__int32 fw, pr, tz; 00295 stbsp__uint32 fl; 00296 00297 // macros for the callback buffer stuff 00298 #define stbsp__chk_cb_bufL(bytes) \ 00299 { \ 00300 int len = (int)(bf - buf); \ 00301 if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ 00302 tlen += len; \ 00303 if (0 == (bf = buf = callback(buf, user, len))) \ 00304 goto done; \ 00305 } \ 00306 } 00307 #define stbsp__chk_cb_buf(bytes) \ 00308 { \ 00309 if (callback) { \ 00310 stbsp__chk_cb_bufL(bytes); \ 00311 } \ 00312 } 00313 #define stbsp__flush_cb() \ 00314 { \ 00315 stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ 00316 } // flush if there is even one byte in the buffer 00317 #define stbsp__cb_buf_clamp(cl, v) \ 00318 cl = v; \ 00319 if (callback) { \ 00320 int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ 00321 if (cl > lg) \ 00322 cl = lg; \ 00323 } 00324 00325 // fast copy everything up to the next % (or end of string) 00326 for (;;) { 00327 while (((stbsp__uintptr)f) & 3) { 00328 schk1: 00329 if (f[0] == '%') 00330 goto scandd; 00331 schk2: 00332 if (f[0] == 0) 00333 goto endfmt; 00334 stbsp__chk_cb_buf(1); 00335 *bf++ = f[0]; 00336 ++f; 00337 } 00338 for (;;) { 00339 // Check if the next 4 bytes contain %(0x25) or end of string. 00340 // Using the 'hasless' trick: 00341 // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord 00342 stbsp__uint32 v, c; 00343 v = *(stbsp__uint32 *)f; 00344 c = (~v) & 0x80808080; 00345 if (((v ^ 0x25252525) - 0x01010101) & c) 00346 goto schk1; 00347 if ((v - 0x01010101) & c) 00348 goto schk2; 00349 if (callback) 00350 if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) 00351 goto schk1; 00352 #ifdef STB_SPRINTF_NOUNALIGNED 00353 if(((stbsp__uintptr)bf) & 3) { 00354 bf[0] = f[0]; 00355 bf[1] = f[1]; 00356 bf[2] = f[2]; 00357 bf[3] = f[3]; 00358 } else 00359 #endif 00360 { 00361 *(stbsp__uint32 *)bf = v; 00362 } 00363 bf += 4; 00364 f += 4; 00365 } 00366 } 00367 scandd: 00368 00369 ++f; 00370 00371 // ok, we have a percent, read the modifiers first 00372 fw = 0; 00373 pr = -1; 00374 fl = 0; 00375 tz = 0; 00376 00377 // flags 00378 for (;;) { 00379 switch (f[0]) { 00380 // if we have left justify 00381 case '-': 00382 fl |= STBSP__LEFTJUST; 00383 ++f; 00384 continue; 00385 // if we have leading plus 00386 case '+': 00387 fl |= STBSP__LEADINGPLUS; 00388 ++f; 00389 continue; 00390 // if we have leading space 00391 case ' ': 00392 fl |= STBSP__LEADINGSPACE; 00393 ++f; 00394 continue; 00395 // if we have leading 0x 00396 case '#': 00397 fl |= STBSP__LEADING_0X; 00398 ++f; 00399 continue; 00400 // if we have thousand commas 00401 case '\'': 00402 fl |= STBSP__TRIPLET_COMMA; 00403 ++f; 00404 continue; 00405 // if we have kilo marker (none->kilo->kibi->jedec) 00406 case '$': 00407 if (fl & STBSP__METRIC_SUFFIX) { 00408 if (fl & STBSP__METRIC_1024) { 00409 fl |= STBSP__METRIC_JEDEC; 00410 } else { 00411 fl |= STBSP__METRIC_1024; 00412 } 00413 } else { 00414 fl |= STBSP__METRIC_SUFFIX; 00415 } 00416 ++f; 00417 continue; 00418 // if we don't want space between metric suffix and number 00419 case '_': 00420 fl |= STBSP__METRIC_NOSPACE; 00421 ++f; 00422 continue; 00423 // if we have leading zero 00424 case '0': 00425 fl |= STBSP__LEADINGZERO; 00426 ++f; 00427 goto flags_done; 00428 default: goto flags_done; 00429 } 00430 } 00431 flags_done: 00432 00433 // get the field width 00434 if (f[0] == '*') { 00435 fw = va_arg(va, stbsp__uint32); 00436 ++f; 00437 } else { 00438 while ((f[0] >= '0') && (f[0] <= '9')) { 00439 fw = fw * 10 + f[0] - '0'; 00440 f++; 00441 } 00442 } 00443 // get the precision 00444 if (f[0] == '.') { 00445 ++f; 00446 if (f[0] == '*') { 00447 pr = va_arg(va, stbsp__uint32); 00448 ++f; 00449 } else { 00450 pr = 0; 00451 while ((f[0] >= '0') && (f[0] <= '9')) { 00452 pr = pr * 10 + f[0] - '0'; 00453 f++; 00454 } 00455 } 00456 } 00457 00458 // handle integer size overrides 00459 switch (f[0]) { 00460 // are we halfwidth? 00461 case 'h': 00462 fl |= STBSP__HALFWIDTH; 00463 ++f; 00464 break; 00465 // are we 64-bit (unix style) 00466 case 'l': 00467 fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); 00468 ++f; 00469 if (f[0] == 'l') { 00470 fl |= STBSP__INTMAX; 00471 ++f; 00472 } 00473 break; 00474 // are we 64-bit on intmax? (c99) 00475 case 'j': 00476 fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; 00477 ++f; 00478 break; 00479 // are we 64-bit on size_t or ptrdiff_t? (c99) 00480 case 'z': 00481 fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 00482 ++f; 00483 break; 00484 case 't': 00485 fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 00486 ++f; 00487 break; 00488 // are we 64-bit (msft style) 00489 case 'I': 00490 if ((f[1] == '6') && (f[2] == '4')) { 00491 fl |= STBSP__INTMAX; 00492 f += 3; 00493 } else if ((f[1] == '3') && (f[2] == '2')) { 00494 f += 3; 00495 } else { 00496 fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); 00497 ++f; 00498 } 00499 break; 00500 default: break; 00501 } 00502 00503 // handle each replacement 00504 switch (f[0]) { 00505 #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 00506 char num[STBSP__NUMSZ]; 00507 char lead[8]; 00508 char tail[8]; 00509 char *s; 00510 char const *h; 00511 stbsp__uint32 l, n, cs; 00512 stbsp__uint64 n64; 00513 #ifndef STB_SPRINTF_NOFLOAT 00514 double fv; 00515 #endif 00516 stbsp__int32 dp; 00517 char const *sn; 00518 00519 case 's': 00520 // get the string 00521 s = va_arg(va, char *); 00522 if (s == 0) 00523 s = (char *)"null"; 00524 // get the length 00525 sn = s; 00526 for (;;) { 00527 if ((((stbsp__uintptr)sn) & 3) == 0) 00528 break; 00529 lchk: 00530 if (sn[0] == 0) 00531 goto ld; 00532 ++sn; 00533 } 00534 n = 0xffffffff; 00535 if (pr >= 0) { 00536 n = (stbsp__uint32)(sn - s); 00537 if (n >= (stbsp__uint32)pr) 00538 goto ld; 00539 n = ((stbsp__uint32)(pr - n)) >> 2; 00540 } 00541 while (n) { 00542 stbsp__uint32 v = *(stbsp__uint32 *)sn; 00543 if ((v - 0x01010101) & (~v) & 0x80808080UL) 00544 goto lchk; 00545 sn += 4; 00546 --n; 00547 } 00548 goto lchk; 00549 ld: 00550 00551 l = (stbsp__uint32)(sn - s); 00552 // clamp to precision 00553 if (l > (stbsp__uint32)pr) 00554 l = pr; 00555 lead[0] = 0; 00556 tail[0] = 0; 00557 pr = 0; 00558 dp = 0; 00559 cs = 0; 00560 // copy the string in 00561 goto scopy; 00562 00563 case 'c': // char 00564 // get the character 00565 s = num + STBSP__NUMSZ - 1; 00566 *s = (char)va_arg(va, int); 00567 l = 1; 00568 lead[0] = 0; 00569 tail[0] = 0; 00570 pr = 0; 00571 dp = 0; 00572 cs = 0; 00573 goto scopy; 00574 00575 case 'n': // weird write-bytes specifier 00576 { 00577 int *d = va_arg(va, int *); 00578 *d = tlen + (int)(bf - buf); 00579 } break; 00580 00581 #ifdef STB_SPRINTF_NOFLOAT 00582 case 'A': // float 00583 case 'a': // hex float 00584 case 'G': // float 00585 case 'g': // float 00586 case 'E': // float 00587 case 'e': // float 00588 case 'f': // float 00589 va_arg(va, double); // eat it 00590 s = (char *)"No float"; 00591 l = 8; 00592 lead[0] = 0; 00593 tail[0] = 0; 00594 pr = 0; 00595 dp = 0; 00596 cs = 0; 00597 goto scopy; 00598 #else 00599 case 'A': // hex float 00600 case 'a': // hex float 00601 h = (f[0] == 'A') ? hexu : hex; 00602 fv = va_arg(va, double); 00603 if (pr == -1) 00604 pr = 6; // default is 6 00605 // read the double into a string 00606 if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) 00607 fl |= STBSP__NEGATIVE; 00608 00609 s = num + 64; 00610 00611 stbsp__lead_sign(fl, lead); 00612 00613 if (dp == -1023) 00614 dp = (n64) ? -1022 : 0; 00615 else 00616 n64 |= (((stbsp__uint64)1) << 52); 00617 n64 <<= (64 - 56); 00618 if (pr < 15) 00619 n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); 00620 // add leading chars 00621 00622 #ifdef STB_SPRINTF_MSVC_MODE 00623 *s++ = '0'; 00624 *s++ = 'x'; 00625 #else 00626 lead[1 + lead[0]] = '0'; 00627 lead[2 + lead[0]] = 'x'; 00628 lead[0] += 2; 00629 #endif 00630 *s++ = h[(n64 >> 60) & 15]; 00631 n64 <<= 4; 00632 if (pr) 00633 *s++ = stbsp__period; 00634 sn = s; 00635 00636 // print the bits 00637 n = pr; 00638 if (n > 13) 00639 n = 13; 00640 if (pr > (stbsp__int32)n) 00641 tz = pr - n; 00642 pr = 0; 00643 while (n--) { 00644 *s++ = h[(n64 >> 60) & 15]; 00645 n64 <<= 4; 00646 } 00647 00648 // print the expo 00649 tail[1] = h[17]; 00650 if (dp < 0) { 00651 tail[2] = '-'; 00652 dp = -dp; 00653 } else 00654 tail[2] = '+'; 00655 n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); 00656 tail[0] = (char)n; 00657 for (;;) { 00658 tail[n] = '0' + dp % 10; 00659 if (n <= 3) 00660 break; 00661 --n; 00662 dp /= 10; 00663 } 00664 00665 dp = (int)(s - sn); 00666 l = (int)(s - (num + 64)); 00667 s = num + 64; 00668 cs = 1 + (3 << 24); 00669 goto scopy; 00670 00671 case 'G': // float 00672 case 'g': // float 00673 h = (f[0] == 'G') ? hexu : hex; 00674 fv = va_arg(va, double); 00675 if (pr == -1) 00676 pr = 6; 00677 else if (pr == 0) 00678 pr = 1; // default is 6 00679 // read the double into a string 00680 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) 00681 fl |= STBSP__NEGATIVE; 00682 00683 // clamp the precision and delete extra zeros after clamp 00684 n = pr; 00685 if (l > (stbsp__uint32)pr) 00686 l = pr; 00687 while ((l > 1) && (pr) && (sn[l - 1] == '0')) { 00688 --pr; 00689 --l; 00690 } 00691 00692 // should we use %e 00693 if ((dp <= -4) || (dp > (stbsp__int32)n)) { 00694 if (pr > (stbsp__int32)l) 00695 pr = l - 1; 00696 else if (pr) 00697 --pr; // when using %e, there is one digit before the decimal 00698 goto doexpfromg; 00699 } 00700 // this is the insane action to get the pr to match %g semantics for %f 00701 if (dp > 0) { 00702 pr = (dp < (stbsp__int32)l) ? l - dp : 0; 00703 } else { 00704 pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); 00705 } 00706 goto dofloatfromg; 00707 00708 case 'E': // float 00709 case 'e': // float 00710 h = (f[0] == 'E') ? hexu : hex; 00711 fv = va_arg(va, double); 00712 if (pr == -1) 00713 pr = 6; // default is 6 00714 // read the double into a string 00715 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) 00716 fl |= STBSP__NEGATIVE; 00717 doexpfromg: 00718 tail[0] = 0; 00719 stbsp__lead_sign(fl, lead); 00720 if (dp == STBSP__SPECIAL) { 00721 s = (char *)sn; 00722 cs = 0; 00723 pr = 0; 00724 goto scopy; 00725 } 00726 s = num + 64; 00727 // handle leading chars 00728 *s++ = sn[0]; 00729 00730 if (pr) 00731 *s++ = stbsp__period; 00732 00733 // handle after decimal 00734 if ((l - 1) > (stbsp__uint32)pr) 00735 l = pr + 1; 00736 for (n = 1; n < l; n++) 00737 *s++ = sn[n]; 00738 // trailing zeros 00739 tz = pr - (l - 1); 00740 pr = 0; 00741 // dump expo 00742 tail[1] = h[0xe]; 00743 dp -= 1; 00744 if (dp < 0) { 00745 tail[2] = '-'; 00746 dp = -dp; 00747 } else 00748 tail[2] = '+'; 00749 #ifdef STB_SPRINTF_MSVC_MODE 00750 n = 5; 00751 #else 00752 n = (dp >= 100) ? 5 : 4; 00753 #endif 00754 tail[0] = (char)n; 00755 for (;;) { 00756 tail[n] = '0' + dp % 10; 00757 if (n <= 3) 00758 break; 00759 --n; 00760 dp /= 10; 00761 } 00762 cs = 1 + (3 << 24); // how many tens 00763 goto flt_lead; 00764 00765 case 'f': // float 00766 fv = va_arg(va, double); 00767 doafloat: 00768 // do kilos 00769 if (fl & STBSP__METRIC_SUFFIX) { 00770 double divisor; 00771 divisor = 1000.0f; 00772 if (fl & STBSP__METRIC_1024) 00773 divisor = 1024.0; 00774 while (fl < 0x4000000) { 00775 if ((fv < divisor) && (fv > -divisor)) 00776 break; 00777 fv /= divisor; 00778 fl += 0x1000000; 00779 } 00780 } 00781 if (pr == -1) 00782 pr = 6; // default is 6 00783 // read the double into a string 00784 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) 00785 fl |= STBSP__NEGATIVE; 00786 dofloatfromg: 00787 tail[0] = 0; 00788 stbsp__lead_sign(fl, lead); 00789 if (dp == STBSP__SPECIAL) { 00790 s = (char *)sn; 00791 cs = 0; 00792 pr = 0; 00793 goto scopy; 00794 } 00795 s = num + 64; 00796 00797 // handle the three decimal varieties 00798 if (dp <= 0) { 00799 stbsp__int32 i; 00800 // handle 0.000*000xxxx 00801 *s++ = '0'; 00802 if (pr) 00803 *s++ = stbsp__period; 00804 n = -dp; 00805 if ((stbsp__int32)n > pr) 00806 n = pr; 00807 i = n; 00808 while (i) { 00809 if ((((stbsp__uintptr)s) & 3) == 0) 00810 break; 00811 *s++ = '0'; 00812 --i; 00813 } 00814 while (i >= 4) { 00815 *(stbsp__uint32 *)s = 0x30303030; 00816 s += 4; 00817 i -= 4; 00818 } 00819 while (i) { 00820 *s++ = '0'; 00821 --i; 00822 } 00823 if ((stbsp__int32)(l + n) > pr) 00824 l = pr - n; 00825 i = l; 00826 while (i) { 00827 *s++ = *sn++; 00828 --i; 00829 } 00830 tz = pr - (n + l); 00831 cs = 1 + (3 << 24); // how many tens did we write (for commas below) 00832 } else { 00833 cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; 00834 if ((stbsp__uint32)dp >= l) { 00835 // handle xxxx000*000.0 00836 n = 0; 00837 for (;;) { 00838 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 00839 cs = 0; 00840 *s++ = stbsp__comma; 00841 } else { 00842 *s++ = sn[n]; 00843 ++n; 00844 if (n >= l) 00845 break; 00846 } 00847 } 00848 if (n < (stbsp__uint32)dp) { 00849 n = dp - n; 00850 if ((fl & STBSP__TRIPLET_COMMA) == 0) { 00851 while (n) { 00852 if ((((stbsp__uintptr)s) & 3) == 0) 00853 break; 00854 *s++ = '0'; 00855 --n; 00856 } 00857 while (n >= 4) { 00858 *(stbsp__uint32 *)s = 0x30303030; 00859 s += 4; 00860 n -= 4; 00861 } 00862 } 00863 while (n) { 00864 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 00865 cs = 0; 00866 *s++ = stbsp__comma; 00867 } else { 00868 *s++ = '0'; 00869 --n; 00870 } 00871 } 00872 } 00873 cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 00874 if (pr) { 00875 *s++ = stbsp__period; 00876 tz = pr; 00877 } 00878 } else { 00879 // handle xxxxx.xxxx000*000 00880 n = 0; 00881 for (;;) { 00882 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 00883 cs = 0; 00884 *s++ = stbsp__comma; 00885 } else { 00886 *s++ = sn[n]; 00887 ++n; 00888 if (n >= (stbsp__uint32)dp) 00889 break; 00890 } 00891 } 00892 cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 00893 if (pr) 00894 *s++ = stbsp__period; 00895 if ((l - dp) > (stbsp__uint32)pr) 00896 l = pr + dp; 00897 while (n < l) { 00898 *s++ = sn[n]; 00899 ++n; 00900 } 00901 tz = pr - (l - dp); 00902 } 00903 } 00904 pr = 0; 00905 00906 // handle k,m,g,t 00907 if (fl & STBSP__METRIC_SUFFIX) { 00908 char idx; 00909 idx = 1; 00910 if (fl & STBSP__METRIC_NOSPACE) 00911 idx = 0; 00912 tail[0] = idx; 00913 tail[1] = ' '; 00914 { 00915 if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. 00916 if (fl & STBSP__METRIC_1024) 00917 tail[idx + 1] = "_KMGT"[fl >> 24]; 00918 else 00919 tail[idx + 1] = "_kMGT"[fl >> 24]; 00920 idx++; 00921 // If printing kibits and not in jedec, add the 'i'. 00922 if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { 00923 tail[idx + 1] = 'i'; 00924 idx++; 00925 } 00926 tail[0] = idx; 00927 } 00928 } 00929 }; 00930 00931 flt_lead: 00932 // get the length that we copied 00933 l = (stbsp__uint32)(s - (num + 64)); 00934 s = num + 64; 00935 goto scopy; 00936 #endif 00937 00938 case 'B': // upper binary 00939 case 'b': // lower binary 00940 h = (f[0] == 'B') ? hexu : hex; 00941 lead[0] = 0; 00942 if (fl & STBSP__LEADING_0X) { 00943 lead[0] = 2; 00944 lead[1] = '0'; 00945 lead[2] = h[0xb]; 00946 } 00947 l = (8 << 4) | (1 << 8); 00948 goto radixnum; 00949 00950 case 'o': // octal 00951 h = hexu; 00952 lead[0] = 0; 00953 if (fl & STBSP__LEADING_0X) { 00954 lead[0] = 1; 00955 lead[1] = '0'; 00956 } 00957 l = (3 << 4) | (3 << 8); 00958 goto radixnum; 00959 00960 case 'p': // pointer 00961 fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; 00962 pr = sizeof(void *) * 2; 00963 fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros 00964 // fall through - to X 00965 00966 case 'X': // upper hex 00967 case 'x': // lower hex 00968 h = (f[0] == 'X') ? hexu : hex; 00969 l = (4 << 4) | (4 << 8); 00970 lead[0] = 0; 00971 if (fl & STBSP__LEADING_0X) { 00972 lead[0] = 2; 00973 lead[1] = '0'; 00974 lead[2] = h[16]; 00975 } 00976 radixnum: 00977 // get the number 00978 if (fl & STBSP__INTMAX) 00979 n64 = va_arg(va, stbsp__uint64); 00980 else 00981 n64 = va_arg(va, stbsp__uint32); 00982 00983 s = num + STBSP__NUMSZ; 00984 dp = 0; 00985 // clear tail, and clear leading if value is zero 00986 tail[0] = 0; 00987 if (n64 == 0) { 00988 lead[0] = 0; 00989 if (pr == 0) { 00990 l = 0; 00991 cs = (((l >> 4) & 15)) << 24; 00992 goto scopy; 00993 } 00994 } 00995 // convert to string 00996 for (;;) { 00997 *--s = h[n64 & ((1 << (l >> 8)) - 1)]; 00998 n64 >>= (l >> 8); 00999 if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) 01000 break; 01001 if (fl & STBSP__TRIPLET_COMMA) { 01002 ++l; 01003 if ((l & 15) == ((l >> 4) & 15)) { 01004 l &= ~15; 01005 *--s = stbsp__comma; 01006 } 01007 } 01008 }; 01009 // get the tens and the comma pos 01010 cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); 01011 // get the length that we copied 01012 l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 01013 // copy it 01014 goto scopy; 01015 01016 case 'u': // unsigned 01017 case 'i': 01018 case 'd': // integer 01019 // get the integer and abs it 01020 if (fl & STBSP__INTMAX) { 01021 stbsp__int64 i64 = va_arg(va, stbsp__int64); 01022 n64 = (stbsp__uint64)i64; 01023 if ((f[0] != 'u') && (i64 < 0)) { 01024 n64 = (stbsp__uint64)-i64; 01025 fl |= STBSP__NEGATIVE; 01026 } 01027 } else { 01028 stbsp__int32 i = va_arg(va, stbsp__int32); 01029 n64 = (stbsp__uint32)i; 01030 if ((f[0] != 'u') && (i < 0)) { 01031 n64 = (stbsp__uint32)-i; 01032 fl |= STBSP__NEGATIVE; 01033 } 01034 } 01035 01036 #ifndef STB_SPRINTF_NOFLOAT 01037 if (fl & STBSP__METRIC_SUFFIX) { 01038 if (n64 < 1024) 01039 pr = 0; 01040 else if (pr == -1) 01041 pr = 1; 01042 fv = (double)(stbsp__int64)n64; 01043 goto doafloat; 01044 } 01045 #endif 01046 01047 // convert to string 01048 s = num + STBSP__NUMSZ; 01049 l = 0; 01050 01051 for (;;) { 01052 // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) 01053 char *o = s - 8; 01054 if (n64 >= 100000000) { 01055 n = (stbsp__uint32)(n64 % 100000000); 01056 n64 /= 100000000; 01057 } else { 01058 n = (stbsp__uint32)n64; 01059 n64 = 0; 01060 } 01061 if ((fl & STBSP__TRIPLET_COMMA) == 0) { 01062 do { 01063 s -= 2; 01064 *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 01065 n /= 100; 01066 } while (n); 01067 } 01068 while (n) { 01069 if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 01070 l = 0; 01071 *--s = stbsp__comma; 01072 --o; 01073 } else { 01074 *--s = (char)(n % 10) + '0'; 01075 n /= 10; 01076 } 01077 } 01078 if (n64 == 0) { 01079 if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) 01080 ++s; 01081 break; 01082 } 01083 while (s != o) 01084 if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 01085 l = 0; 01086 *--s = stbsp__comma; 01087 --o; 01088 } else { 01089 *--s = '0'; 01090 } 01091 } 01092 01093 tail[0] = 0; 01094 stbsp__lead_sign(fl, lead); 01095 01096 // get the length that we copied 01097 l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 01098 if (l == 0) { 01099 *--s = '0'; 01100 l = 1; 01101 } 01102 cs = l + (3 << 24); 01103 if (pr < 0) 01104 pr = 0; 01105 01106 scopy: 01107 // get fw=leading/trailing space, pr=leading zeros 01108 if (pr < (stbsp__int32)l) 01109 pr = l; 01110 n = pr + lead[0] + tail[0] + tz; 01111 if (fw < (stbsp__int32)n) 01112 fw = n; 01113 fw -= n; 01114 pr -= l; 01115 01116 // handle right justify and leading zeros 01117 if ((fl & STBSP__LEFTJUST) == 0) { 01118 if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr 01119 { 01120 pr = (fw > pr) ? fw : pr; 01121 fw = 0; 01122 } else { 01123 fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas 01124 } 01125 } 01126 01127 // copy the spaces and/or zeros 01128 if (fw + pr) { 01129 stbsp__int32 i; 01130 stbsp__uint32 c; 01131 01132 // copy leading spaces (or when doing %8.4d stuff) 01133 if ((fl & STBSP__LEFTJUST) == 0) 01134 while (fw > 0) { 01135 stbsp__cb_buf_clamp(i, fw); 01136 fw -= i; 01137 while (i) { 01138 if ((((stbsp__uintptr)bf) & 3) == 0) 01139 break; 01140 *bf++ = ' '; 01141 --i; 01142 } 01143 while (i >= 4) { 01144 *(stbsp__uint32 *)bf = 0x20202020; 01145 bf += 4; 01146 i -= 4; 01147 } 01148 while (i) { 01149 *bf++ = ' '; 01150 --i; 01151 } 01152 stbsp__chk_cb_buf(1); 01153 } 01154 01155 // copy leader 01156 sn = lead + 1; 01157 while (lead[0]) { 01158 stbsp__cb_buf_clamp(i, lead[0]); 01159 lead[0] -= (char)i; 01160 while (i) { 01161 *bf++ = *sn++; 01162 --i; 01163 } 01164 stbsp__chk_cb_buf(1); 01165 } 01166 01167 // copy leading zeros 01168 c = cs >> 24; 01169 cs &= 0xffffff; 01170 cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; 01171 while (pr > 0) { 01172 stbsp__cb_buf_clamp(i, pr); 01173 pr -= i; 01174 if ((fl & STBSP__TRIPLET_COMMA) == 0) { 01175 while (i) { 01176 if ((((stbsp__uintptr)bf) & 3) == 0) 01177 break; 01178 *bf++ = '0'; 01179 --i; 01180 } 01181 while (i >= 4) { 01182 *(stbsp__uint32 *)bf = 0x30303030; 01183 bf += 4; 01184 i -= 4; 01185 } 01186 } 01187 while (i) { 01188 if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { 01189 cs = 0; 01190 *bf++ = stbsp__comma; 01191 } else 01192 *bf++ = '0'; 01193 --i; 01194 } 01195 stbsp__chk_cb_buf(1); 01196 } 01197 } 01198 01199 // copy leader if there is still one 01200 sn = lead + 1; 01201 while (lead[0]) { 01202 stbsp__int32 i; 01203 stbsp__cb_buf_clamp(i, lead[0]); 01204 lead[0] -= (char)i; 01205 while (i) { 01206 *bf++ = *sn++; 01207 --i; 01208 } 01209 stbsp__chk_cb_buf(1); 01210 } 01211 01212 // copy the string 01213 n = l; 01214 while (n) { 01215 stbsp__int32 i; 01216 stbsp__cb_buf_clamp(i, n); 01217 n -= i; 01218 STBSP__UNALIGNED(while (i >= 4) { 01219 *(stbsp__uint32 *)bf = *(stbsp__uint32 *)s; 01220 bf += 4; 01221 s += 4; 01222 i -= 4; 01223 }) 01224 while (i) { 01225 *bf++ = *s++; 01226 --i; 01227 } 01228 stbsp__chk_cb_buf(1); 01229 } 01230 01231 // copy trailing zeros 01232 while (tz) { 01233 stbsp__int32 i; 01234 stbsp__cb_buf_clamp(i, tz); 01235 tz -= i; 01236 while (i) { 01237 if ((((stbsp__uintptr)bf) & 3) == 0) 01238 break; 01239 *bf++ = '0'; 01240 --i; 01241 } 01242 while (i >= 4) { 01243 *(stbsp__uint32 *)bf = 0x30303030; 01244 bf += 4; 01245 i -= 4; 01246 } 01247 while (i) { 01248 *bf++ = '0'; 01249 --i; 01250 } 01251 stbsp__chk_cb_buf(1); 01252 } 01253 01254 // copy tail if there is one 01255 sn = tail + 1; 01256 while (tail[0]) { 01257 stbsp__int32 i; 01258 stbsp__cb_buf_clamp(i, tail[0]); 01259 tail[0] -= (char)i; 01260 while (i) { 01261 *bf++ = *sn++; 01262 --i; 01263 } 01264 stbsp__chk_cb_buf(1); 01265 } 01266 01267 // handle the left justify 01268 if (fl & STBSP__LEFTJUST) 01269 if (fw > 0) { 01270 while (fw) { 01271 stbsp__int32 i; 01272 stbsp__cb_buf_clamp(i, fw); 01273 fw -= i; 01274 while (i) { 01275 if ((((stbsp__uintptr)bf) & 3) == 0) 01276 break; 01277 *bf++ = ' '; 01278 --i; 01279 } 01280 while (i >= 4) { 01281 *(stbsp__uint32 *)bf = 0x20202020; 01282 bf += 4; 01283 i -= 4; 01284 } 01285 while (i--) 01286 *bf++ = ' '; 01287 stbsp__chk_cb_buf(1); 01288 } 01289 } 01290 break; 01291 01292 default: // unknown, just copy code 01293 s = num + STBSP__NUMSZ - 1; 01294 *s = f[0]; 01295 l = 1; 01296 fw = fl = 0; 01297 lead[0] = 0; 01298 tail[0] = 0; 01299 pr = 0; 01300 dp = 0; 01301 cs = 0; 01302 goto scopy; 01303 } 01304 ++f; 01305 } 01306 endfmt: 01307 01308 if (!callback) 01309 *bf = 0; 01310 else 01311 stbsp__flush_cb(); 01312 01313 done: 01314 return tlen + (int)(bf - buf); 01315 } 01316 01317 // cleanup 01318 #undef STBSP__LEFTJUST 01319 #undef STBSP__LEADINGPLUS 01320 #undef STBSP__LEADINGSPACE 01321 #undef STBSP__LEADING_0X 01322 #undef STBSP__LEADINGZERO 01323 #undef STBSP__INTMAX 01324 #undef STBSP__TRIPLET_COMMA 01325 #undef STBSP__NEGATIVE 01326 #undef STBSP__METRIC_SUFFIX 01327 #undef STBSP__NUMSZ 01328 #undef stbsp__chk_cb_bufL 01329 #undef stbsp__chk_cb_buf 01330 #undef stbsp__flush_cb 01331 #undef stbsp__cb_buf_clamp 01332 01333 // ============================================================================ 01334 // wrapper functions 01335 01336 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) 01337 { 01338 int result; 01339 va_list va; 01340 va_start(va, fmt); 01341 result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 01342 va_end(va); 01343 return result; 01344 } 01345 01346 typedef struct stbsp__context { 01347 char *buf; 01348 int count; 01349 char tmp[STB_SPRINTF_MIN]; 01350 } stbsp__context; 01351 01352 static char *stbsp__clamp_callback(char *buf, void *user, int len) 01353 { 01354 stbsp__context *c = (stbsp__context *)user; 01355 01356 if (len > c->count) 01357 len = c->count; 01358 01359 if (len) { 01360 if (buf != c->buf) { 01361 char *s, *d, *se; 01362 d = c->buf; 01363 s = buf; 01364 se = buf + len; 01365 do { 01366 *d++ = *s++; 01367 } while (s < se); 01368 } 01369 c->buf += len; 01370 c->count -= len; 01371 } 01372 01373 if (c->count <= 0) 01374 return 0; 01375 return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can 01376 } 01377 01378 static char * stbsp__count_clamp_callback( char * buf, void * user, int len ) 01379 { 01380 stbsp__context * c = (stbsp__context*)user; 01381 01382 c->count += len; 01383 return c->tmp; // go direct into buffer if you can 01384 } 01385 01386 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) 01387 { 01388 stbsp__context c; 01389 int l; 01390 01391 if ( (count == 0) && !buf ) 01392 { 01393 c.count = 0; 01394 01395 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); 01396 l = c.count; 01397 } 01398 else 01399 { 01400 if ( count == 0 ) 01401 return 0; 01402 01403 c.buf = buf; 01404 c.count = count; 01405 01406 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); 01407 01408 // zero-terminate 01409 l = (int)( c.buf - buf ); 01410 if ( l >= count ) // should never be greater, only equal (or less) than count 01411 l = count - 1; 01412 buf[l] = 0; 01413 } 01414 01415 return l; 01416 } 01417 01418 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) 01419 { 01420 int result; 01421 va_list va; 01422 va_start(va, fmt); 01423 01424 result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); 01425 va_end(va); 01426 01427 return result; 01428 } 01429 01430 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) 01431 { 01432 return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 01433 } 01434 01435 // ======================================================================= 01436 // low level float utility functions 01437 01438 #ifndef STB_SPRINTF_NOFLOAT 01439 01440 // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) 01441 #define STBSP__COPYFP(dest, src) \ 01442 { \ 01443 int cn; \ 01444 for (cn = 0; cn < 8; cn++) \ 01445 ((char *)&dest)[cn] = ((char *)&src)[cn]; \ 01446 } 01447 01448 // get float info 01449 static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) 01450 { 01451 double d; 01452 stbsp__int64 b = 0; 01453 01454 // load value and round at the frac_digits 01455 d = value; 01456 01457 STBSP__COPYFP(b, d); 01458 01459 *bits = b & ((((stbsp__uint64)1) << 52) - 1); 01460 *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); 01461 01462 return (stbsp__int32)((stbsp__uint64) b >> 63); 01463 } 01464 01465 static double const stbsp__bot[23] = { 01466 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, 01467 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 01468 }; 01469 static double const stbsp__negbot[22] = { 01470 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, 01471 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 01472 }; 01473 static double const stbsp__negboterr[22] = { 01474 -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, 01475 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, 01476 -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, 01477 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 01478 }; 01479 static double const stbsp__top[13] = { 01480 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 01481 }; 01482 static double const stbsp__negtop[13] = { 01483 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 01484 }; 01485 static double const stbsp__toperr[13] = { 01486 8388608, 01487 6.8601809640529717e+028, 01488 -7.253143638152921e+052, 01489 -4.3377296974619174e+075, 01490 -1.5559416129466825e+098, 01491 -3.2841562489204913e+121, 01492 -3.7745893248228135e+144, 01493 -1.7356668416969134e+167, 01494 -3.8893577551088374e+190, 01495 -9.9566444326005119e+213, 01496 6.3641293062232429e+236, 01497 -5.2069140800249813e+259, 01498 -5.2504760255204387e+282 01499 }; 01500 static double const stbsp__negtoperr[13] = { 01501 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, 01502 -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, 01503 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, 01504 8.0970921678014997e-317 01505 }; 01506 01507 #if defined(_MSC_VER) && (_MSC_VER <= 1200) 01508 static stbsp__uint64 const stbsp__powten[20] = { 01509 1, 01510 10, 01511 100, 01512 1000, 01513 10000, 01514 100000, 01515 1000000, 01516 10000000, 01517 100000000, 01518 1000000000, 01519 10000000000, 01520 100000000000, 01521 1000000000000, 01522 10000000000000, 01523 100000000000000, 01524 1000000000000000, 01525 10000000000000000, 01526 100000000000000000, 01527 1000000000000000000, 01528 10000000000000000000U 01529 }; 01530 #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) 01531 #else 01532 static stbsp__uint64 const stbsp__powten[20] = { 01533 1, 01534 10, 01535 100, 01536 1000, 01537 10000, 01538 100000, 01539 1000000, 01540 10000000, 01541 100000000, 01542 1000000000, 01543 10000000000ULL, 01544 100000000000ULL, 01545 1000000000000ULL, 01546 10000000000000ULL, 01547 100000000000000ULL, 01548 1000000000000000ULL, 01549 10000000000000000ULL, 01550 100000000000000000ULL, 01551 1000000000000000000ULL, 01552 10000000000000000000ULL 01553 }; 01554 #define stbsp__tento19th (1000000000000000000ULL) 01555 #endif 01556 01557 #define stbsp__ddmulthi(oh, ol, xh, yh) \ 01558 { \ 01559 double ahi = 0, alo, bhi = 0, blo; \ 01560 stbsp__int64 bt; \ 01561 oh = xh * yh; \ 01562 STBSP__COPYFP(bt, xh); \ 01563 bt &= ((~(stbsp__uint64)0) << 27); \ 01564 STBSP__COPYFP(ahi, bt); \ 01565 alo = xh - ahi; \ 01566 STBSP__COPYFP(bt, yh); \ 01567 bt &= ((~(stbsp__uint64)0) << 27); \ 01568 STBSP__COPYFP(bhi, bt); \ 01569 blo = yh - bhi; \ 01570 ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ 01571 } 01572 01573 #define stbsp__ddtoS64(ob, xh, xl) \ 01574 { \ 01575 double ahi = 0, alo, vh, t; \ 01576 ob = (stbsp__int64)ph; \ 01577 vh = (double)ob; \ 01578 ahi = (xh - vh); \ 01579 t = (ahi - xh); \ 01580 alo = (xh - (ahi - t)) - (vh + t); \ 01581 ob += (stbsp__int64)(ahi + alo + xl); \ 01582 } 01583 01584 #define stbsp__ddrenorm(oh, ol) \ 01585 { \ 01586 double s; \ 01587 s = oh + ol; \ 01588 ol = ol - (s - oh); \ 01589 oh = s; \ 01590 } 01591 01592 #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); 01593 01594 #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); 01595 01596 static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 01597 { 01598 double ph, pl; 01599 if ((power >= 0) && (power <= 22)) { 01600 stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); 01601 } else { 01602 stbsp__int32 e, et, eb; 01603 double p2h, p2l; 01604 01605 e = power; 01606 if (power < 0) 01607 e = -e; 01608 et = (e * 0x2c9) >> 14; /* %23 */ 01609 if (et > 13) 01610 et = 13; 01611 eb = e - (et * 23); 01612 01613 ph = d; 01614 pl = 0.0; 01615 if (power < 0) { 01616 if (eb) { 01617 --eb; 01618 stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); 01619 stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); 01620 } 01621 if (et) { 01622 stbsp__ddrenorm(ph, pl); 01623 --et; 01624 stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); 01625 stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); 01626 ph = p2h; 01627 pl = p2l; 01628 } 01629 } else { 01630 if (eb) { 01631 e = eb; 01632 if (eb > 22) 01633 eb = 22; 01634 e -= eb; 01635 stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); 01636 if (e) { 01637 stbsp__ddrenorm(ph, pl); 01638 stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); 01639 stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); 01640 ph = p2h; 01641 pl = p2l; 01642 } 01643 } 01644 if (et) { 01645 stbsp__ddrenorm(ph, pl); 01646 --et; 01647 stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); 01648 stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); 01649 ph = p2h; 01650 pl = p2l; 01651 } 01652 } 01653 } 01654 stbsp__ddrenorm(ph, pl); 01655 *ohi = ph; 01656 *olo = pl; 01657 } 01658 01659 // given a float value, returns the significant bits in bits, and the position of the 01660 // decimal point in decimal_pos. +/-INF and NAN are specified by special values 01661 // returned in the decimal_pos parameter. 01662 // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 01663 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) 01664 { 01665 double d; 01666 stbsp__int64 bits = 0; 01667 stbsp__int32 expo, e, ng, tens; 01668 01669 d = value; 01670 STBSP__COPYFP(bits, d); 01671 expo = (stbsp__int32)((bits >> 52) & 2047); 01672 ng = (stbsp__int32)((stbsp__uint64) bits >> 63); 01673 if (ng) 01674 d = -d; 01675 01676 if (expo == 2047) // is nan or inf? 01677 { 01678 *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; 01679 *decimal_pos = STBSP__SPECIAL; 01680 *len = 3; 01681 return ng; 01682 } 01683 01684 if (expo == 0) // is zero or denormal 01685 { 01686 if ((bits << 1) == 0) // do zero 01687 { 01688 *decimal_pos = 1; 01689 *start = out; 01690 out[0] = '0'; 01691 *len = 1; 01692 return ng; 01693 } 01694 // find the right expo for denormals 01695 { 01696 stbsp__int64 v = ((stbsp__uint64)1) << 51; 01697 while ((bits & v) == 0) { 01698 --expo; 01699 v >>= 1; 01700 } 01701 } 01702 } 01703 01704 // find the decimal exponent as well as the decimal bits of the value 01705 { 01706 double ph, pl; 01707 01708 // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 01709 tens = expo - 1023; 01710 tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); 01711 01712 // move the significant bits into position and stick them into an int 01713 stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); 01714 01715 // get full as much precision from double-double as possible 01716 stbsp__ddtoS64(bits, ph, pl); 01717 01718 // check if we undershot 01719 if (((stbsp__uint64)bits) >= stbsp__tento19th) 01720 ++tens; 01721 } 01722 01723 // now do the rounding in integer land 01724 frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); 01725 if ((frac_digits < 24)) { 01726 stbsp__uint32 dg = 1; 01727 if ((stbsp__uint64)bits >= stbsp__powten[9]) 01728 dg = 10; 01729 while ((stbsp__uint64)bits >= stbsp__powten[dg]) { 01730 ++dg; 01731 if (dg == 20) 01732 goto noround; 01733 } 01734 if (frac_digits < dg) { 01735 stbsp__uint64 r; 01736 // add 0.5 at the right position and round 01737 e = dg - frac_digits; 01738 if ((stbsp__uint32)e >= 24) 01739 goto noround; 01740 r = stbsp__powten[e]; 01741 bits = bits + (r / 2); 01742 if ((stbsp__uint64)bits >= stbsp__powten[dg]) 01743 ++tens; 01744 bits /= r; 01745 } 01746 noround:; 01747 } 01748 01749 // kill long trailing runs of zeros 01750 if (bits) { 01751 stbsp__uint32 n; 01752 for (;;) { 01753 if (bits <= 0xffffffff) 01754 break; 01755 if (bits % 1000) 01756 goto donez; 01757 bits /= 1000; 01758 } 01759 n = (stbsp__uint32)bits; 01760 while ((n % 1000) == 0) 01761 n /= 1000; 01762 bits = n; 01763 donez:; 01764 } 01765 01766 // convert to string 01767 out += 64; 01768 e = 0; 01769 for (;;) { 01770 stbsp__uint32 n; 01771 char *o = out - 8; 01772 // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) 01773 if (bits >= 100000000) { 01774 n = (stbsp__uint32)(bits % 100000000); 01775 bits /= 100000000; 01776 } else { 01777 n = (stbsp__uint32)bits; 01778 bits = 0; 01779 } 01780 while (n) { 01781 out -= 2; 01782 *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 01783 n /= 100; 01784 e += 2; 01785 } 01786 if (bits == 0) { 01787 if ((e) && (out[0] == '0')) { 01788 ++out; 01789 --e; 01790 } 01791 break; 01792 } 01793 while (out != o) { 01794 *--out = '0'; 01795 ++e; 01796 } 01797 } 01798 01799 *decimal_pos = tens; 01800 *start = out; 01801 *len = e; 01802 return ng; 01803 } 01804 01805 #undef stbsp__ddmulthi 01806 #undef stbsp__ddrenorm 01807 #undef stbsp__ddmultlo 01808 #undef stbsp__ddmultlos 01809 #undef STBSP__SPECIAL 01810 #undef STBSP__COPYFP 01811 01812 #endif // STB_SPRINTF_NOFLOAT 01813 01814 // clean up 01815 #undef stbsp__uint16 01816 #undef stbsp__uint32 01817 #undef stbsp__int32 01818 #undef stbsp__uint64 01819 #undef stbsp__int64 01820 #undef STBSP__UNALIGNED 01821 01822 #endif // STB_SPRINTF_IMPLEMENTATION 01823 01824 /* 01825 ------------------------------------------------------------------------------ 01826 This software is available under 2 licenses -- choose whichever you prefer. 01827 ------------------------------------------------------------------------------ 01828 ALTERNATIVE A - MIT License 01829 Copyright (c) 2017 Sean Barrett 01830 Permission is hereby granted, free of charge, to any person obtaining a copy of 01831 this software and associated documentation files (the "Software"), to deal in 01832 the Software without restriction, including without limitation the rights to 01833 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 01834 of the Software, and to permit persons to whom the Software is furnished to do 01835 so, subject to the following conditions: 01836 The above copyright notice and this permission notice shall be included in all 01837 copies or substantial portions of the Software. 01838 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 01839 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 01840 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 01841 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 01842 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 01843 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 01844 SOFTWARE. 01845 ------------------------------------------------------------------------------ 01846 ALTERNATIVE B - Public Domain (www.unlicense.org) 01847 This is free and unencumbered software released into the public domain. 01848 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 01849 software, either in source code form or as a compiled binary, for any purpose, 01850 commercial or non-commercial, and by any means. 01851 In jurisdictions that recognize copyright laws, the author or authors of this 01852 software dedicate any and all copyright interest in the software to the public 01853 domain. We make this dedication for the benefit of the public at large and to 01854 the detriment of our heirs and successors. We intend this dedication to be an 01855 overt act of relinquishment in perpetuity of all present and future rights to 01856 this software under copyright law. 01857 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 01858 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 01859 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 01860 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 01861 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 01862 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 01863 ------------------------------------------------------------------------------ 01864 */