stb_sprintf.h (58282B)
1 // stb_sprintf - v1.10 - public domain snprintf() implementation 2 // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 3 // http://github.com/nothings/stb 4 // 5 // allowed types: sc uidBboXx p AaGgEef n 6 // lengths : hh h ll j z t I64 I32 I 7 // 8 // Contributors: 9 // Fabian "ryg" Giesen (reformatting) 10 // github:aganm (attribute format) 11 // 12 // Contributors (bugfixes): 13 // github:d26435 14 // github:trex78 15 // github:account-login 16 // Jari Komppa (SI suffixes) 17 // Rohit Nirmal 18 // Marcin Wojdyr 19 // Leonard Ritter 20 // Stefano Zanotti 21 // Adam Allison 22 // Arvid Gerstmann 23 // Markus Kolb 24 // 25 // LICENSE: 26 // 27 // See end of file for license information. 28 29 #ifndef STB_SPRINTF_H_INCLUDE 30 #define STB_SPRINTF_H_INCLUDE 31 32 /* 33 Single file sprintf replacement. 34 35 Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. 36 Hereby placed in public domain. 37 38 This is a full sprintf replacement that supports everything that 39 the C runtime sprintfs support, including float/double, 64-bit integers, 40 hex floats, field parameters (%*.*d stuff), length reads backs, etc. 41 42 Why would you need this if sprintf already exists? Well, first off, 43 it's *much* faster (see below). It's also much smaller than the CRT 44 versions code-space-wise. We've also added some simple improvements 45 that are super handy (commas in thousands, callbacks at buffer full, 46 for example). Finally, the format strings for MSVC and GCC differ 47 for 64-bit integers (among other small things), so this lets you use 48 the same format strings in cross platform code. 49 50 It uses the standard single file trick of being both the header file 51 and the source itself. If you just include it normally, you just get 52 the header file function definitions. To get the code, you include 53 it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. 54 55 It only uses va_args macros from the C runtime to do it's work. It 56 does cast doubles to S64s and shifts and divides U64s, which does 57 drag in CRT code on most platforms. 58 59 It compiles to roughly 8K with float support, and 4K without. 60 As a comparison, when using MSVC static libs, calling sprintf drags 61 in 16K. 62 63 API: 64 ==== 65 int stbsp_sprintf( char * buf, char const * fmt, ... ) 66 int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) 67 Convert an arg list into a buffer. stbsp_snprintf always returns 68 a zero-terminated string (unlike regular snprintf). 69 70 int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) 71 int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) 72 Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns 73 a zero-terminated string (unlike regular snprintf). 74 75 int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) 76 typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); 77 Convert into a buffer, calling back every STB_SPRINTF_MIN chars. 78 Your callback can then copy the chars out, print them or whatever. 79 This function is actually the workhorse for everything else. 80 The buffer you pass in must hold at least STB_SPRINTF_MIN characters. 81 // you return the next buffer to use or 0 to stop converting 82 83 void stbsp_set_separators( char comma, char period ) 84 Set the comma and period characters to use. 85 86 FLOATS/DOUBLES: 87 =============== 88 This code uses a internal float->ascii conversion method that uses 89 doubles with error correction (double-doubles, for ~105 bits of 90 precision). This conversion is round-trip perfect - that is, an atof 91 of the values output here will give you the bit-exact double back. 92 93 One difference is that our insignificant digits will be different than 94 with MSVC or GCC (but they don't match each other either). We also 95 don't attempt to find the minimum length matching float (pre-MSVC15 96 doesn't either). 97 98 If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT 99 and you'll save 4K of code space. 100 101 64-BIT INTS: 102 ============ 103 This library also supports 64-bit integers and you can use MSVC style or 104 GCC style indicators (%I64d or %lld). It supports the C99 specifiers 105 for size_t and ptr_diff_t (%jd %zd) as well. 106 107 EXTRAS: 108 ======= 109 Like some GCCs, for integers and floats, you can use a ' (single quote) 110 specifier and commas will be inserted on the thousands: "%'d" on 12345 111 would print 12,345. 112 113 For integers and floats, you can use a "$" specifier and the number 114 will be converted to float and then divided to get kilo, mega, giga or 115 tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is 116 "2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 117 2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three 118 $:s: "%$$$d" -> "2.42 M". To remove the space between the number and the 119 suffix, add "_" specifier: "%_$d" -> "2.53M". 120 121 In addition to octal and hexadecimal conversions, you can print 122 integers in binary: "%b" for 256 would print 100. 123 124 PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): 125 =================================================================== 126 "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) 127 "%24d" across all 32-bit ints (4.5x/4.2x faster) 128 "%x" across all 32-bit ints (4.5x/3.8x faster) 129 "%08x" across all 32-bit ints (4.3x/3.8x faster) 130 "%f" across e-10 to e+10 floats (7.3x/6.0x faster) 131 "%e" across e-10 to e+10 floats (8.1x/6.0x faster) 132 "%g" across e-10 to e+10 floats (10.0x/7.1x faster) 133 "%f" for values near e-300 (7.9x/6.5x faster) 134 "%f" for values near e+300 (10.0x/9.1x faster) 135 "%e" for values near e-300 (10.1x/7.0x faster) 136 "%e" for values near e+300 (9.2x/6.0x faster) 137 "%.320f" for values near e-300 (12.6x/11.2x faster) 138 "%a" for random values (8.6x/4.3x faster) 139 "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) 140 "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) 141 "%s%s%s" for 64 char strings (7.1x/7.3x faster) 142 "...512 char string..." ( 35.0x/32.5x faster!) 143 */ 144 145 #if defined(__clang__) 146 #if defined(__has_feature) && defined(__has_attribute) 147 #if __has_feature(address_sanitizer) 148 #if __has_attribute(__no_sanitize__) 149 #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) 150 #elif __has_attribute(__no_sanitize_address__) 151 #define STBSP__ASAN __attribute__((__no_sanitize_address__)) 152 #elif __has_attribute(__no_address_safety_analysis__) 153 #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) 154 #endif 155 #endif 156 #endif 157 #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) 158 #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ 159 #define STBSP__ASAN __attribute__((__no_sanitize_address__)) 160 #endif 161 #endif 162 163 #ifndef STBSP__ASAN 164 #define STBSP__ASAN 165 #endif 166 167 #ifdef STB_SPRINTF_STATIC 168 #define STBSP__PUBLICDEC static 169 #define STBSP__PUBLICDEF static STBSP__ASAN 170 #else 171 #ifdef __cplusplus 172 #define STBSP__PUBLICDEC extern "C" 173 #define STBSP__PUBLICDEF extern "C" STBSP__ASAN 174 #else 175 #define STBSP__PUBLICDEC extern 176 #define STBSP__PUBLICDEF STBSP__ASAN 177 #endif 178 #endif 179 180 #if defined(__has_attribute) 181 #if __has_attribute(format) 182 #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) 183 #endif 184 #endif 185 186 #ifndef STBSP__ATTRIBUTE_FORMAT 187 #define STBSP__ATTRIBUTE_FORMAT(fmt,va) 188 #endif 189 190 #ifdef _MSC_VER 191 #define STBSP__NOTUSED(v) (void)(v) 192 #else 193 #define STBSP__NOTUSED(v) (void)sizeof(v) 194 #endif 195 196 #include <stdarg.h> // for va_arg(), va_list() 197 #include <stddef.h> // size_t, ptrdiff_t 198 199 #ifndef STB_SPRINTF_MIN 200 #define STB_SPRINTF_MIN 512 // how many characters per callback 201 #endif 202 typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); 203 204 #ifndef STB_SPRINTF_DECORATE 205 #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names 206 #endif 207 208 STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); 209 STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); 210 STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); 211 STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); 212 213 STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); 214 STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); 215 216 #endif // STB_SPRINTF_H_INCLUDE 217 218 #ifdef STB_SPRINTF_IMPLEMENTATION 219 220 #define stbsp__uint32 unsigned int 221 #define stbsp__int32 signed int 222 223 #ifdef _MSC_VER 224 #define stbsp__uint64 unsigned __int64 225 #define stbsp__int64 signed __int64 226 #else 227 #define stbsp__uint64 unsigned long long 228 #define stbsp__int64 signed long long 229 #endif 230 #define stbsp__uint16 unsigned short 231 232 #ifndef stbsp__uintptr 233 #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) 234 #define stbsp__uintptr stbsp__uint64 235 #else 236 #define stbsp__uintptr stbsp__uint32 237 #endif 238 #endif 239 240 #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) 241 #if defined(_MSC_VER) && (_MSC_VER < 1900) 242 #define STB_SPRINTF_MSVC_MODE 243 #endif 244 #endif 245 246 #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses 247 #define STBSP__UNALIGNED(code) 248 #else 249 #define STBSP__UNALIGNED(code) code 250 #endif 251 252 #ifndef STB_SPRINTF_NOFLOAT 253 // internal float utility functions 254 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); 255 static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); 256 #define STBSP__SPECIAL 0x7000 257 #endif 258 259 static char stbsp__period = '.'; 260 static char stbsp__comma = ','; 261 static struct 262 { 263 short temp; // force next field to be 2-byte aligned 264 char pair[201]; 265 } stbsp__digitpair = 266 { 267 0, 268 "00010203040506070809101112131415161718192021222324" 269 "25262728293031323334353637383940414243444546474849" 270 "50515253545556575859606162636465666768697071727374" 271 "75767778798081828384858687888990919293949596979899" 272 }; 273 274 STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) 275 { 276 stbsp__period = pperiod; 277 stbsp__comma = pcomma; 278 } 279 280 #define STBSP__LEFTJUST 1 281 #define STBSP__LEADINGPLUS 2 282 #define STBSP__LEADINGSPACE 4 283 #define STBSP__LEADING_0X 8 284 #define STBSP__LEADINGZERO 16 285 #define STBSP__INTMAX 32 286 #define STBSP__TRIPLET_COMMA 64 287 #define STBSP__NEGATIVE 128 288 #define STBSP__METRIC_SUFFIX 256 289 #define STBSP__HALFWIDTH 512 290 #define STBSP__METRIC_NOSPACE 1024 291 #define STBSP__METRIC_1024 2048 292 #define STBSP__METRIC_JEDEC 4096 293 294 static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) 295 { 296 sign[0] = 0; 297 if (fl & STBSP__NEGATIVE) { 298 sign[0] = 1; 299 sign[1] = '-'; 300 } else if (fl & STBSP__LEADINGSPACE) { 301 sign[0] = 1; 302 sign[1] = ' '; 303 } else if (fl & STBSP__LEADINGPLUS) { 304 sign[0] = 1; 305 sign[1] = '+'; 306 } 307 } 308 309 static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) 310 { 311 char const * sn = s; 312 313 // get up to 4-byte alignment 314 for (;;) { 315 if (((stbsp__uintptr)sn & 3) == 0) 316 break; 317 318 if (!limit || *sn == 0) 319 return (stbsp__uint32)(sn - s); 320 321 ++sn; 322 --limit; 323 } 324 325 // scan over 4 bytes at a time to find terminating 0 326 // this will intentionally scan up to 3 bytes past the end of buffers, 327 // but becase it works 4B aligned, it will never cross page boundaries 328 // (hence the STBSP__ASAN markup; the over-read here is intentional 329 // and harmless) 330 while (limit >= 4) { 331 stbsp__uint32 v = *(stbsp__uint32 *)sn; 332 // bit hack to find if there's a 0 byte in there 333 if ((v - 0x01010101) & (~v) & 0x80808080UL) 334 break; 335 336 sn += 4; 337 limit -= 4; 338 } 339 340 // handle the last few characters to find actual size 341 while (limit && *sn) { 342 ++sn; 343 --limit; 344 } 345 346 return (stbsp__uint32)(sn - s); 347 } 348 349 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) 350 { 351 static char hex[] = "0123456789abcdefxp"; 352 static char hexu[] = "0123456789ABCDEFXP"; 353 char *bf; 354 char const *f; 355 int tlen = 0; 356 357 bf = buf; 358 f = fmt; 359 for (;;) { 360 stbsp__int32 fw, pr, tz; 361 stbsp__uint32 fl; 362 363 // macros for the callback buffer stuff 364 #define stbsp__chk_cb_bufL(bytes) \ 365 { \ 366 int len = (int)(bf - buf); \ 367 if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ 368 tlen += len; \ 369 if (0 == (bf = buf = callback(buf, user, len))) \ 370 goto done; \ 371 } \ 372 } 373 #define stbsp__chk_cb_buf(bytes) \ 374 { \ 375 if (callback) { \ 376 stbsp__chk_cb_bufL(bytes); \ 377 } \ 378 } 379 #define stbsp__flush_cb() \ 380 { \ 381 stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ 382 } // flush if there is even one byte in the buffer 383 #define stbsp__cb_buf_clamp(cl, v) \ 384 cl = v; \ 385 if (callback) { \ 386 int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ 387 if (cl > lg) \ 388 cl = lg; \ 389 } 390 391 // fast copy everything up to the next % (or end of string) 392 for (;;) { 393 while (((stbsp__uintptr)f) & 3) { 394 schk1: 395 if (f[0] == '%') 396 goto scandd; 397 schk2: 398 if (f[0] == 0) 399 goto endfmt; 400 stbsp__chk_cb_buf(1); 401 *bf++ = f[0]; 402 ++f; 403 } 404 for (;;) { 405 // Check if the next 4 bytes contain %(0x25) or end of string. 406 // Using the 'hasless' trick: 407 // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord 408 stbsp__uint32 v, c; 409 v = *(stbsp__uint32 *)f; 410 c = (~v) & 0x80808080; 411 if (((v ^ 0x25252525) - 0x01010101) & c) 412 goto schk1; 413 if ((v - 0x01010101) & c) 414 goto schk2; 415 if (callback) 416 if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) 417 goto schk1; 418 #ifdef STB_SPRINTF_NOUNALIGNED 419 if(((stbsp__uintptr)bf) & 3) { 420 bf[0] = f[0]; 421 bf[1] = f[1]; 422 bf[2] = f[2]; 423 bf[3] = f[3]; 424 } else 425 #endif 426 { 427 *(stbsp__uint32 *)bf = v; 428 } 429 bf += 4; 430 f += 4; 431 } 432 } 433 scandd: 434 435 ++f; 436 437 // ok, we have a percent, read the modifiers first 438 fw = 0; 439 pr = -1; 440 fl = 0; 441 tz = 0; 442 443 // flags 444 for (;;) { 445 switch (f[0]) { 446 // if we have left justify 447 case '-': 448 fl |= STBSP__LEFTJUST; 449 ++f; 450 continue; 451 // if we have leading plus 452 case '+': 453 fl |= STBSP__LEADINGPLUS; 454 ++f; 455 continue; 456 // if we have leading space 457 case ' ': 458 fl |= STBSP__LEADINGSPACE; 459 ++f; 460 continue; 461 // if we have leading 0x 462 case '#': 463 fl |= STBSP__LEADING_0X; 464 ++f; 465 continue; 466 // if we have thousand commas 467 case '\'': 468 fl |= STBSP__TRIPLET_COMMA; 469 ++f; 470 continue; 471 // if we have kilo marker (none->kilo->kibi->jedec) 472 case '$': 473 if (fl & STBSP__METRIC_SUFFIX) { 474 if (fl & STBSP__METRIC_1024) { 475 fl |= STBSP__METRIC_JEDEC; 476 } else { 477 fl |= STBSP__METRIC_1024; 478 } 479 } else { 480 fl |= STBSP__METRIC_SUFFIX; 481 } 482 ++f; 483 continue; 484 // if we don't want space between metric suffix and number 485 case '_': 486 fl |= STBSP__METRIC_NOSPACE; 487 ++f; 488 continue; 489 // if we have leading zero 490 case '0': 491 fl |= STBSP__LEADINGZERO; 492 ++f; 493 goto flags_done; 494 default: goto flags_done; 495 } 496 } 497 flags_done: 498 499 // get the field width 500 if (f[0] == '*') { 501 fw = va_arg(va, stbsp__uint32); 502 ++f; 503 } else { 504 while ((f[0] >= '0') && (f[0] <= '9')) { 505 fw = fw * 10 + f[0] - '0'; 506 f++; 507 } 508 } 509 // get the precision 510 if (f[0] == '.') { 511 ++f; 512 if (f[0] == '*') { 513 pr = va_arg(va, stbsp__uint32); 514 ++f; 515 } else { 516 pr = 0; 517 while ((f[0] >= '0') && (f[0] <= '9')) { 518 pr = pr * 10 + f[0] - '0'; 519 f++; 520 } 521 } 522 } 523 524 // handle integer size overrides 525 switch (f[0]) { 526 // are we halfwidth? 527 case 'h': 528 fl |= STBSP__HALFWIDTH; 529 ++f; 530 if (f[0] == 'h') 531 ++f; // QUARTERWIDTH 532 break; 533 // are we 64-bit (unix style) 534 case 'l': 535 fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); 536 ++f; 537 if (f[0] == 'l') { 538 fl |= STBSP__INTMAX; 539 ++f; 540 } 541 break; 542 // are we 64-bit on intmax? (c99) 543 case 'j': 544 fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; 545 ++f; 546 break; 547 // are we 64-bit on size_t or ptrdiff_t? (c99) 548 case 'z': 549 fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 550 ++f; 551 break; 552 case 't': 553 fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; 554 ++f; 555 break; 556 // are we 64-bit (msft style) 557 case 'I': 558 if ((f[1] == '6') && (f[2] == '4')) { 559 fl |= STBSP__INTMAX; 560 f += 3; 561 } else if ((f[1] == '3') && (f[2] == '2')) { 562 f += 3; 563 } else { 564 fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); 565 ++f; 566 } 567 break; 568 default: break; 569 } 570 571 // handle each replacement 572 switch (f[0]) { 573 #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 574 char num[STBSP__NUMSZ]; 575 char lead[8]; 576 char tail[8]; 577 char *s; 578 char const *h; 579 stbsp__uint32 l, n, cs; 580 stbsp__uint64 n64; 581 #ifndef STB_SPRINTF_NOFLOAT 582 double fv; 583 #endif 584 stbsp__int32 dp; 585 char const *sn; 586 587 case 's': 588 // get the string 589 s = va_arg(va, char *); 590 if (s == 0) 591 s = (char *)"null"; 592 // get the length, limited to desired precision 593 // always limit to ~0u chars since our counts are 32b 594 l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); 595 lead[0] = 0; 596 tail[0] = 0; 597 pr = 0; 598 dp = 0; 599 cs = 0; 600 // copy the string in 601 goto scopy; 602 603 case 'c': // char 604 // get the character 605 s = num + STBSP__NUMSZ - 1; 606 *s = (char)va_arg(va, int); 607 l = 1; 608 lead[0] = 0; 609 tail[0] = 0; 610 pr = 0; 611 dp = 0; 612 cs = 0; 613 goto scopy; 614 615 #if 0 616 // Disabled since it's terribly unsafe. 617 case 'n': // weird write-bytes specifier 618 { 619 int *d = va_arg(va, int *); 620 *d = tlen + (int)(bf - buf); 621 } break; 622 #endif 623 624 #ifdef STB_SPRINTF_NOFLOAT 625 case 'A': // float 626 case 'a': // hex float 627 case 'G': // float 628 case 'g': // float 629 case 'E': // float 630 case 'e': // float 631 case 'f': // float 632 va_arg(va, double); // eat it 633 s = (char *)"No float"; 634 l = 8; 635 lead[0] = 0; 636 tail[0] = 0; 637 pr = 0; 638 cs = 0; 639 STBSP__NOTUSED(dp); 640 goto scopy; 641 #else 642 case 'A': // hex float 643 case 'a': // hex float 644 h = (f[0] == 'A') ? hexu : hex; 645 fv = va_arg(va, double); 646 if (pr == -1) 647 pr = 6; // default is 6 648 // read the double into a string 649 if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) 650 fl |= STBSP__NEGATIVE; 651 652 s = num + 64; 653 654 stbsp__lead_sign(fl, lead); 655 656 if (dp == -1023) 657 dp = (n64) ? -1022 : 0; 658 else 659 n64 |= (((stbsp__uint64)1) << 52); 660 n64 <<= (64 - 56); 661 if (pr < 15) 662 n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); 663 // add leading chars 664 665 #ifdef STB_SPRINTF_MSVC_MODE 666 *s++ = '0'; 667 *s++ = 'x'; 668 #else 669 lead[1 + lead[0]] = '0'; 670 lead[2 + lead[0]] = 'x'; 671 lead[0] += 2; 672 #endif 673 *s++ = h[(n64 >> 60) & 15]; 674 n64 <<= 4; 675 if (pr) 676 *s++ = stbsp__period; 677 sn = s; 678 679 // print the bits 680 n = pr; 681 if (n > 13) 682 n = 13; 683 if (pr > (stbsp__int32)n) 684 tz = pr - n; 685 pr = 0; 686 while (n--) { 687 *s++ = h[(n64 >> 60) & 15]; 688 n64 <<= 4; 689 } 690 691 // print the expo 692 tail[1] = h[17]; 693 if (dp < 0) { 694 tail[2] = '-'; 695 dp = -dp; 696 } else 697 tail[2] = '+'; 698 n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); 699 tail[0] = (char)n; 700 for (;;) { 701 tail[n] = '0' + dp % 10; 702 if (n <= 3) 703 break; 704 --n; 705 dp /= 10; 706 } 707 708 dp = (int)(s - sn); 709 l = (int)(s - (num + 64)); 710 s = num + 64; 711 cs = 1 + (3 << 24); 712 goto scopy; 713 714 case 'G': // float 715 case 'g': // float 716 h = (f[0] == 'G') ? hexu : hex; 717 fv = va_arg(va, double); 718 if (pr == -1) 719 pr = 6; 720 else if (pr == 0) 721 pr = 1; // default is 6 722 // read the double into a string 723 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) 724 fl |= STBSP__NEGATIVE; 725 726 // clamp the precision and delete extra zeros after clamp 727 n = pr; 728 if (l > (stbsp__uint32)pr) 729 l = pr; 730 while ((l > 1) && (pr) && (sn[l - 1] == '0')) { 731 --pr; 732 --l; 733 } 734 735 // should we use %e 736 if ((dp <= -4) || (dp > (stbsp__int32)n)) { 737 if (pr > (stbsp__int32)l) 738 pr = l - 1; 739 else if (pr) 740 --pr; // when using %e, there is one digit before the decimal 741 goto doexpfromg; 742 } 743 // this is the insane action to get the pr to match %g semantics for %f 744 if (dp > 0) { 745 pr = (dp < (stbsp__int32)l) ? l - dp : 0; 746 } else { 747 pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); 748 } 749 goto dofloatfromg; 750 751 case 'E': // float 752 case 'e': // float 753 h = (f[0] == 'E') ? hexu : hex; 754 fv = va_arg(va, double); 755 if (pr == -1) 756 pr = 6; // default is 6 757 // read the double into a string 758 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) 759 fl |= STBSP__NEGATIVE; 760 doexpfromg: 761 tail[0] = 0; 762 stbsp__lead_sign(fl, lead); 763 if (dp == STBSP__SPECIAL) { 764 s = (char *)sn; 765 cs = 0; 766 pr = 0; 767 goto scopy; 768 } 769 s = num + 64; 770 // handle leading chars 771 *s++ = sn[0]; 772 773 if (pr) 774 *s++ = stbsp__period; 775 776 // handle after decimal 777 if ((l - 1) > (stbsp__uint32)pr) 778 l = pr + 1; 779 for (n = 1; n < l; n++) 780 *s++ = sn[n]; 781 // trailing zeros 782 tz = pr - (l - 1); 783 pr = 0; 784 // dump expo 785 tail[1] = h[0xe]; 786 dp -= 1; 787 if (dp < 0) { 788 tail[2] = '-'; 789 dp = -dp; 790 } else 791 tail[2] = '+'; 792 #ifdef STB_SPRINTF_MSVC_MODE 793 n = 5; 794 #else 795 n = (dp >= 100) ? 5 : 4; 796 #endif 797 tail[0] = (char)n; 798 for (;;) { 799 tail[n] = '0' + dp % 10; 800 if (n <= 3) 801 break; 802 --n; 803 dp /= 10; 804 } 805 cs = 1 + (3 << 24); // how many tens 806 goto flt_lead; 807 808 case 'f': // float 809 fv = va_arg(va, double); 810 doafloat: 811 // do kilos 812 if (fl & STBSP__METRIC_SUFFIX) { 813 double divisor; 814 divisor = 1000.0f; 815 if (fl & STBSP__METRIC_1024) 816 divisor = 1024.0; 817 while (fl < 0x4000000) { 818 if ((fv < divisor) && (fv > -divisor)) 819 break; 820 fv /= divisor; 821 fl += 0x1000000; 822 } 823 } 824 if (pr == -1) 825 pr = 6; // default is 6 826 // read the double into a string 827 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) 828 fl |= STBSP__NEGATIVE; 829 dofloatfromg: 830 tail[0] = 0; 831 stbsp__lead_sign(fl, lead); 832 if (dp == STBSP__SPECIAL) { 833 s = (char *)sn; 834 cs = 0; 835 pr = 0; 836 goto scopy; 837 } 838 s = num + 64; 839 840 // handle the three decimal varieties 841 if (dp <= 0) { 842 stbsp__int32 i; 843 // handle 0.000*000xxxx 844 *s++ = '0'; 845 if (pr) 846 *s++ = stbsp__period; 847 n = -dp; 848 if ((stbsp__int32)n > pr) 849 n = pr; 850 i = n; 851 while (i) { 852 if ((((stbsp__uintptr)s) & 3) == 0) 853 break; 854 *s++ = '0'; 855 --i; 856 } 857 while (i >= 4) { 858 *(stbsp__uint32 *)s = 0x30303030; 859 s += 4; 860 i -= 4; 861 } 862 while (i) { 863 *s++ = '0'; 864 --i; 865 } 866 if ((stbsp__int32)(l + n) > pr) 867 l = pr - n; 868 i = l; 869 while (i) { 870 *s++ = *sn++; 871 --i; 872 } 873 tz = pr - (n + l); 874 cs = 1 + (3 << 24); // how many tens did we write (for commas below) 875 } else { 876 cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; 877 if ((stbsp__uint32)dp >= l) { 878 // handle xxxx000*000.0 879 n = 0; 880 for (;;) { 881 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 882 cs = 0; 883 *s++ = stbsp__comma; 884 } else { 885 *s++ = sn[n]; 886 ++n; 887 if (n >= l) 888 break; 889 } 890 } 891 if (n < (stbsp__uint32)dp) { 892 n = dp - n; 893 if ((fl & STBSP__TRIPLET_COMMA) == 0) { 894 while (n) { 895 if ((((stbsp__uintptr)s) & 3) == 0) 896 break; 897 *s++ = '0'; 898 --n; 899 } 900 while (n >= 4) { 901 *(stbsp__uint32 *)s = 0x30303030; 902 s += 4; 903 n -= 4; 904 } 905 } 906 while (n) { 907 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 908 cs = 0; 909 *s++ = stbsp__comma; 910 } else { 911 *s++ = '0'; 912 --n; 913 } 914 } 915 } 916 cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 917 if (pr) { 918 *s++ = stbsp__period; 919 tz = pr; 920 } 921 } else { 922 // handle xxxxx.xxxx000*000 923 n = 0; 924 for (;;) { 925 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { 926 cs = 0; 927 *s++ = stbsp__comma; 928 } else { 929 *s++ = sn[n]; 930 ++n; 931 if (n >= (stbsp__uint32)dp) 932 break; 933 } 934 } 935 cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens 936 if (pr) 937 *s++ = stbsp__period; 938 if ((l - dp) > (stbsp__uint32)pr) 939 l = pr + dp; 940 while (n < l) { 941 *s++ = sn[n]; 942 ++n; 943 } 944 tz = pr - (l - dp); 945 } 946 } 947 pr = 0; 948 949 // handle k,m,g,t 950 if (fl & STBSP__METRIC_SUFFIX) { 951 char idx; 952 idx = 1; 953 if (fl & STBSP__METRIC_NOSPACE) 954 idx = 0; 955 tail[0] = idx; 956 tail[1] = ' '; 957 { 958 if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. 959 if (fl & STBSP__METRIC_1024) 960 tail[idx + 1] = "_KMGT"[fl >> 24]; 961 else 962 tail[idx + 1] = "_kMGT"[fl >> 24]; 963 idx++; 964 // If printing kibits and not in jedec, add the 'i'. 965 if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { 966 tail[idx + 1] = 'i'; 967 idx++; 968 } 969 tail[0] = idx; 970 } 971 } 972 }; 973 974 flt_lead: 975 // get the length that we copied 976 l = (stbsp__uint32)(s - (num + 64)); 977 s = num + 64; 978 goto scopy; 979 #endif 980 981 case 'B': // upper binary 982 case 'b': // lower binary 983 h = (f[0] == 'B') ? hexu : hex; 984 lead[0] = 0; 985 if (fl & STBSP__LEADING_0X) { 986 lead[0] = 2; 987 lead[1] = '0'; 988 lead[2] = h[0xb]; 989 } 990 l = (8 << 4) | (1 << 8); 991 goto radixnum; 992 993 case 'o': // octal 994 h = hexu; 995 lead[0] = 0; 996 if (fl & STBSP__LEADING_0X) { 997 lead[0] = 1; 998 lead[1] = '0'; 999 } 1000 l = (3 << 4) | (3 << 8); 1001 goto radixnum; 1002 1003 case 'p': // pointer 1004 fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; 1005 pr = sizeof(void *) * 2; 1006 fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros 1007 // fall through - to X 1008 1009 case 'X': // upper hex 1010 case 'x': // lower hex 1011 h = (f[0] == 'X') ? hexu : hex; 1012 l = (4 << 4) | (4 << 8); 1013 lead[0] = 0; 1014 if (fl & STBSP__LEADING_0X) { 1015 lead[0] = 2; 1016 lead[1] = '0'; 1017 lead[2] = h[16]; 1018 } 1019 radixnum: 1020 // get the number 1021 if (fl & STBSP__INTMAX) 1022 n64 = va_arg(va, stbsp__uint64); 1023 else 1024 n64 = va_arg(va, stbsp__uint32); 1025 1026 s = num + STBSP__NUMSZ; 1027 dp = 0; 1028 // clear tail, and clear leading if value is zero 1029 tail[0] = 0; 1030 if (n64 == 0) { 1031 lead[0] = 0; 1032 if (pr == 0) { 1033 l = 0; 1034 cs = 0; 1035 goto scopy; 1036 } 1037 } 1038 // convert to string 1039 for (;;) { 1040 *--s = h[n64 & ((1 << (l >> 8)) - 1)]; 1041 n64 >>= (l >> 8); 1042 if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) 1043 break; 1044 if (fl & STBSP__TRIPLET_COMMA) { 1045 ++l; 1046 if ((l & 15) == ((l >> 4) & 15)) { 1047 l &= ~15; 1048 *--s = stbsp__comma; 1049 } 1050 } 1051 }; 1052 // get the tens and the comma pos 1053 cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); 1054 // get the length that we copied 1055 l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 1056 // copy it 1057 goto scopy; 1058 1059 case 'u': // unsigned 1060 case 'i': 1061 case 'd': // integer 1062 // get the integer and abs it 1063 if (fl & STBSP__INTMAX) { 1064 stbsp__int64 i64 = va_arg(va, stbsp__int64); 1065 n64 = (stbsp__uint64)i64; 1066 if ((f[0] != 'u') && (i64 < 0)) { 1067 n64 = (stbsp__uint64)-i64; 1068 fl |= STBSP__NEGATIVE; 1069 } 1070 } else { 1071 stbsp__int32 i = va_arg(va, stbsp__int32); 1072 n64 = (stbsp__uint32)i; 1073 if ((f[0] != 'u') && (i < 0)) { 1074 n64 = (stbsp__uint32)-i; 1075 fl |= STBSP__NEGATIVE; 1076 } 1077 } 1078 1079 #ifndef STB_SPRINTF_NOFLOAT 1080 if (fl & STBSP__METRIC_SUFFIX) { 1081 if (n64 < 1024) 1082 pr = 0; 1083 else if (pr == -1) 1084 pr = 1; 1085 fv = (double)(stbsp__int64)n64; 1086 goto doafloat; 1087 } 1088 #endif 1089 1090 // convert to string 1091 s = num + STBSP__NUMSZ; 1092 l = 0; 1093 1094 for (;;) { 1095 // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) 1096 char *o = s - 8; 1097 if (n64 >= 100000000) { 1098 n = (stbsp__uint32)(n64 % 100000000); 1099 n64 /= 100000000; 1100 } else { 1101 n = (stbsp__uint32)n64; 1102 n64 = 0; 1103 } 1104 if ((fl & STBSP__TRIPLET_COMMA) == 0) { 1105 do { 1106 s -= 2; 1107 *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 1108 n /= 100; 1109 } while (n); 1110 } 1111 while (n) { 1112 if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 1113 l = 0; 1114 *--s = stbsp__comma; 1115 --o; 1116 } else { 1117 *--s = (char)(n % 10) + '0'; 1118 n /= 10; 1119 } 1120 } 1121 if (n64 == 0) { 1122 if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) 1123 ++s; 1124 break; 1125 } 1126 while (s != o) 1127 if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { 1128 l = 0; 1129 *--s = stbsp__comma; 1130 --o; 1131 } else { 1132 *--s = '0'; 1133 } 1134 } 1135 1136 tail[0] = 0; 1137 stbsp__lead_sign(fl, lead); 1138 1139 // get the length that we copied 1140 l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); 1141 if (l == 0) { 1142 *--s = '0'; 1143 l = 1; 1144 } 1145 cs = l + (3 << 24); 1146 if (pr < 0) 1147 pr = 0; 1148 1149 scopy: 1150 // get fw=leading/trailing space, pr=leading zeros 1151 if (pr < (stbsp__int32)l) 1152 pr = l; 1153 n = pr + lead[0] + tail[0] + tz; 1154 if (fw < (stbsp__int32)n) 1155 fw = n; 1156 fw -= n; 1157 pr -= l; 1158 1159 // handle right justify and leading zeros 1160 if ((fl & STBSP__LEFTJUST) == 0) { 1161 if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr 1162 { 1163 pr = (fw > pr) ? fw : pr; 1164 fw = 0; 1165 } else { 1166 fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas 1167 } 1168 } 1169 1170 // copy the spaces and/or zeros 1171 if (fw + pr) { 1172 stbsp__int32 i; 1173 stbsp__uint32 c; 1174 1175 // copy leading spaces (or when doing %8.4d stuff) 1176 if ((fl & STBSP__LEFTJUST) == 0) 1177 while (fw > 0) { 1178 stbsp__cb_buf_clamp(i, fw); 1179 fw -= i; 1180 while (i) { 1181 if ((((stbsp__uintptr)bf) & 3) == 0) 1182 break; 1183 *bf++ = ' '; 1184 --i; 1185 } 1186 while (i >= 4) { 1187 *(stbsp__uint32 *)bf = 0x20202020; 1188 bf += 4; 1189 i -= 4; 1190 } 1191 while (i) { 1192 *bf++ = ' '; 1193 --i; 1194 } 1195 stbsp__chk_cb_buf(1); 1196 } 1197 1198 // copy leader 1199 sn = lead + 1; 1200 while (lead[0]) { 1201 stbsp__cb_buf_clamp(i, lead[0]); 1202 lead[0] -= (char)i; 1203 while (i) { 1204 *bf++ = *sn++; 1205 --i; 1206 } 1207 stbsp__chk_cb_buf(1); 1208 } 1209 1210 // copy leading zeros 1211 c = cs >> 24; 1212 cs &= 0xffffff; 1213 cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; 1214 while (pr > 0) { 1215 stbsp__cb_buf_clamp(i, pr); 1216 pr -= i; 1217 if ((fl & STBSP__TRIPLET_COMMA) == 0) { 1218 while (i) { 1219 if ((((stbsp__uintptr)bf) & 3) == 0) 1220 break; 1221 *bf++ = '0'; 1222 --i; 1223 } 1224 while (i >= 4) { 1225 *(stbsp__uint32 *)bf = 0x30303030; 1226 bf += 4; 1227 i -= 4; 1228 } 1229 } 1230 while (i) { 1231 if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { 1232 cs = 0; 1233 *bf++ = stbsp__comma; 1234 } else 1235 *bf++ = '0'; 1236 --i; 1237 } 1238 stbsp__chk_cb_buf(1); 1239 } 1240 } 1241 1242 // copy leader if there is still one 1243 sn = lead + 1; 1244 while (lead[0]) { 1245 stbsp__int32 i; 1246 stbsp__cb_buf_clamp(i, lead[0]); 1247 lead[0] -= (char)i; 1248 while (i) { 1249 *bf++ = *sn++; 1250 --i; 1251 } 1252 stbsp__chk_cb_buf(1); 1253 } 1254 1255 // copy the string 1256 n = l; 1257 while (n) { 1258 stbsp__int32 i; 1259 stbsp__cb_buf_clamp(i, n); 1260 n -= i; 1261 STBSP__UNALIGNED(while (i >= 4) { 1262 *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; 1263 bf += 4; 1264 s += 4; 1265 i -= 4; 1266 }) 1267 while (i) { 1268 *bf++ = *s++; 1269 --i; 1270 } 1271 stbsp__chk_cb_buf(1); 1272 } 1273 1274 // copy trailing zeros 1275 while (tz) { 1276 stbsp__int32 i; 1277 stbsp__cb_buf_clamp(i, tz); 1278 tz -= i; 1279 while (i) { 1280 if ((((stbsp__uintptr)bf) & 3) == 0) 1281 break; 1282 *bf++ = '0'; 1283 --i; 1284 } 1285 while (i >= 4) { 1286 *(stbsp__uint32 *)bf = 0x30303030; 1287 bf += 4; 1288 i -= 4; 1289 } 1290 while (i) { 1291 *bf++ = '0'; 1292 --i; 1293 } 1294 stbsp__chk_cb_buf(1); 1295 } 1296 1297 // copy tail if there is one 1298 sn = tail + 1; 1299 while (tail[0]) { 1300 stbsp__int32 i; 1301 stbsp__cb_buf_clamp(i, tail[0]); 1302 tail[0] -= (char)i; 1303 while (i) { 1304 *bf++ = *sn++; 1305 --i; 1306 } 1307 stbsp__chk_cb_buf(1); 1308 } 1309 1310 // handle the left justify 1311 if (fl & STBSP__LEFTJUST) 1312 if (fw > 0) { 1313 while (fw) { 1314 stbsp__int32 i; 1315 stbsp__cb_buf_clamp(i, fw); 1316 fw -= i; 1317 while (i) { 1318 if ((((stbsp__uintptr)bf) & 3) == 0) 1319 break; 1320 *bf++ = ' '; 1321 --i; 1322 } 1323 while (i >= 4) { 1324 *(stbsp__uint32 *)bf = 0x20202020; 1325 bf += 4; 1326 i -= 4; 1327 } 1328 while (i--) 1329 *bf++ = ' '; 1330 stbsp__chk_cb_buf(1); 1331 } 1332 } 1333 break; 1334 1335 default: // unknown, just copy code 1336 s = num + STBSP__NUMSZ - 1; 1337 *s = f[0]; 1338 l = 1; 1339 fw = fl = 0; 1340 lead[0] = 0; 1341 tail[0] = 0; 1342 pr = 0; 1343 dp = 0; 1344 cs = 0; 1345 goto scopy; 1346 } 1347 ++f; 1348 } 1349 endfmt: 1350 1351 if (!callback) 1352 *bf = 0; 1353 else 1354 stbsp__flush_cb(); 1355 1356 done: 1357 return tlen + (int)(bf - buf); 1358 } 1359 1360 // cleanup 1361 #undef STBSP__LEFTJUST 1362 #undef STBSP__LEADINGPLUS 1363 #undef STBSP__LEADINGSPACE 1364 #undef STBSP__LEADING_0X 1365 #undef STBSP__LEADINGZERO 1366 #undef STBSP__INTMAX 1367 #undef STBSP__TRIPLET_COMMA 1368 #undef STBSP__NEGATIVE 1369 #undef STBSP__METRIC_SUFFIX 1370 #undef STBSP__NUMSZ 1371 #undef stbsp__chk_cb_bufL 1372 #undef stbsp__chk_cb_buf 1373 #undef stbsp__flush_cb 1374 #undef stbsp__cb_buf_clamp 1375 1376 // ============================================================================ 1377 // wrapper functions 1378 1379 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) 1380 { 1381 int result; 1382 va_list va; 1383 va_start(va, fmt); 1384 result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 1385 va_end(va); 1386 return result; 1387 } 1388 1389 typedef struct stbsp__context { 1390 char *buf; 1391 int count; 1392 int length; 1393 char tmp[STB_SPRINTF_MIN]; 1394 } stbsp__context; 1395 1396 static char *stbsp__clamp_callback(const char *buf, void *user, int len) 1397 { 1398 stbsp__context *c = (stbsp__context *)user; 1399 c->length += len; 1400 1401 if (len > c->count) 1402 len = c->count; 1403 1404 if (len) { 1405 if (buf != c->buf) { 1406 const char *s, *se; 1407 char *d; 1408 d = c->buf; 1409 s = buf; 1410 se = buf + len; 1411 do { 1412 *d++ = *s++; 1413 } while (s < se); 1414 } 1415 c->buf += len; 1416 c->count -= len; 1417 } 1418 1419 if (c->count <= 0) 1420 return c->tmp; 1421 return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can 1422 } 1423 1424 static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) 1425 { 1426 stbsp__context * c = (stbsp__context*)user; 1427 (void) sizeof(buf); 1428 1429 c->length += len; 1430 return c->tmp; // go direct into buffer if you can 1431 } 1432 1433 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) 1434 { 1435 stbsp__context c; 1436 1437 if ( (count == 0) && !buf ) 1438 { 1439 c.length = 0; 1440 1441 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); 1442 } 1443 else 1444 { 1445 int l; 1446 1447 c.buf = buf; 1448 c.count = count; 1449 c.length = 0; 1450 1451 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); 1452 1453 // zero-terminate 1454 l = (int)( c.buf - buf ); 1455 if ( l >= count ) // should never be greater, only equal (or less) than count 1456 l = count - 1; 1457 buf[l] = 0; 1458 } 1459 1460 return c.length; 1461 } 1462 1463 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) 1464 { 1465 int result; 1466 va_list va; 1467 va_start(va, fmt); 1468 1469 result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); 1470 va_end(va); 1471 1472 return result; 1473 } 1474 1475 STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) 1476 { 1477 return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); 1478 } 1479 1480 // ======================================================================= 1481 // low level float utility functions 1482 1483 #ifndef STB_SPRINTF_NOFLOAT 1484 1485 // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) 1486 #define STBSP__COPYFP(dest, src) \ 1487 { \ 1488 int cn; \ 1489 for (cn = 0; cn < 8; cn++) \ 1490 ((char *)&dest)[cn] = ((char *)&src)[cn]; \ 1491 } 1492 1493 // get float info 1494 static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) 1495 { 1496 double d; 1497 stbsp__int64 b = 0; 1498 1499 // load value and round at the frac_digits 1500 d = value; 1501 1502 STBSP__COPYFP(b, d); 1503 1504 *bits = b & ((((stbsp__uint64)1) << 52) - 1); 1505 *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); 1506 1507 return (stbsp__int32)((stbsp__uint64) b >> 63); 1508 } 1509 1510 static double const stbsp__bot[23] = { 1511 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, 1512 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 1513 }; 1514 static double const stbsp__negbot[22] = { 1515 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, 1516 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 1517 }; 1518 static double const stbsp__negboterr[22] = { 1519 -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, 1520 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, 1521 -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, 1522 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 1523 }; 1524 static double const stbsp__top[13] = { 1525 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 1526 }; 1527 static double const stbsp__negtop[13] = { 1528 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 1529 }; 1530 static double const stbsp__toperr[13] = { 1531 8388608, 1532 6.8601809640529717e+028, 1533 -7.253143638152921e+052, 1534 -4.3377296974619174e+075, 1535 -1.5559416129466825e+098, 1536 -3.2841562489204913e+121, 1537 -3.7745893248228135e+144, 1538 -1.7356668416969134e+167, 1539 -3.8893577551088374e+190, 1540 -9.9566444326005119e+213, 1541 6.3641293062232429e+236, 1542 -5.2069140800249813e+259, 1543 -5.2504760255204387e+282 1544 }; 1545 static double const stbsp__negtoperr[13] = { 1546 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, 1547 -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, 1548 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, 1549 8.0970921678014997e-317 1550 }; 1551 1552 #if defined(_MSC_VER) && (_MSC_VER <= 1200) 1553 static stbsp__uint64 const stbsp__powten[20] = { 1554 1, 1555 10, 1556 100, 1557 1000, 1558 10000, 1559 100000, 1560 1000000, 1561 10000000, 1562 100000000, 1563 1000000000, 1564 10000000000, 1565 100000000000, 1566 1000000000000, 1567 10000000000000, 1568 100000000000000, 1569 1000000000000000, 1570 10000000000000000, 1571 100000000000000000, 1572 1000000000000000000, 1573 10000000000000000000U 1574 }; 1575 #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) 1576 #else 1577 static stbsp__uint64 const stbsp__powten[20] = { 1578 1, 1579 10, 1580 100, 1581 1000, 1582 10000, 1583 100000, 1584 1000000, 1585 10000000, 1586 100000000, 1587 1000000000, 1588 10000000000ULL, 1589 100000000000ULL, 1590 1000000000000ULL, 1591 10000000000000ULL, 1592 100000000000000ULL, 1593 1000000000000000ULL, 1594 10000000000000000ULL, 1595 100000000000000000ULL, 1596 1000000000000000000ULL, 1597 10000000000000000000ULL 1598 }; 1599 #define stbsp__tento19th (1000000000000000000ULL) 1600 #endif 1601 1602 #define stbsp__ddmulthi(oh, ol, xh, yh) \ 1603 { \ 1604 double ahi = 0, alo, bhi = 0, blo; \ 1605 stbsp__int64 bt; \ 1606 oh = xh * yh; \ 1607 STBSP__COPYFP(bt, xh); \ 1608 bt &= ((~(stbsp__uint64)0) << 27); \ 1609 STBSP__COPYFP(ahi, bt); \ 1610 alo = xh - ahi; \ 1611 STBSP__COPYFP(bt, yh); \ 1612 bt &= ((~(stbsp__uint64)0) << 27); \ 1613 STBSP__COPYFP(bhi, bt); \ 1614 blo = yh - bhi; \ 1615 ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ 1616 } 1617 1618 #define stbsp__ddtoS64(ob, xh, xl) \ 1619 { \ 1620 double ahi = 0, alo, vh, t; \ 1621 ob = (stbsp__int64)xh; \ 1622 vh = (double)ob; \ 1623 ahi = (xh - vh); \ 1624 t = (ahi - xh); \ 1625 alo = (xh - (ahi - t)) - (vh + t); \ 1626 ob += (stbsp__int64)(ahi + alo + xl); \ 1627 } 1628 1629 #define stbsp__ddrenorm(oh, ol) \ 1630 { \ 1631 double s; \ 1632 s = oh + ol; \ 1633 ol = ol - (s - oh); \ 1634 oh = s; \ 1635 } 1636 1637 #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); 1638 1639 #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); 1640 1641 static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 1642 { 1643 double ph, pl; 1644 if ((power >= 0) && (power <= 22)) { 1645 stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); 1646 } else { 1647 stbsp__int32 e, et, eb; 1648 double p2h, p2l; 1649 1650 e = power; 1651 if (power < 0) 1652 e = -e; 1653 et = (e * 0x2c9) >> 14; /* %23 */ 1654 if (et > 13) 1655 et = 13; 1656 eb = e - (et * 23); 1657 1658 ph = d; 1659 pl = 0.0; 1660 if (power < 0) { 1661 if (eb) { 1662 --eb; 1663 stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); 1664 stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); 1665 } 1666 if (et) { 1667 stbsp__ddrenorm(ph, pl); 1668 --et; 1669 stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); 1670 stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); 1671 ph = p2h; 1672 pl = p2l; 1673 } 1674 } else { 1675 if (eb) { 1676 e = eb; 1677 if (eb > 22) 1678 eb = 22; 1679 e -= eb; 1680 stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); 1681 if (e) { 1682 stbsp__ddrenorm(ph, pl); 1683 stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); 1684 stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); 1685 ph = p2h; 1686 pl = p2l; 1687 } 1688 } 1689 if (et) { 1690 stbsp__ddrenorm(ph, pl); 1691 --et; 1692 stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); 1693 stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); 1694 ph = p2h; 1695 pl = p2l; 1696 } 1697 } 1698 } 1699 stbsp__ddrenorm(ph, pl); 1700 *ohi = ph; 1701 *olo = pl; 1702 } 1703 1704 // given a float value, returns the significant bits in bits, and the position of the 1705 // decimal point in decimal_pos. +/-INF and NAN are specified by special values 1706 // returned in the decimal_pos parameter. 1707 // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 1708 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) 1709 { 1710 double d; 1711 stbsp__int64 bits = 0; 1712 stbsp__int32 expo, e, ng, tens; 1713 1714 d = value; 1715 STBSP__COPYFP(bits, d); 1716 expo = (stbsp__int32)((bits >> 52) & 2047); 1717 ng = (stbsp__int32)((stbsp__uint64) bits >> 63); 1718 if (ng) 1719 d = -d; 1720 1721 if (expo == 2047) // is nan or inf? 1722 { 1723 *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; 1724 *decimal_pos = STBSP__SPECIAL; 1725 *len = 3; 1726 return ng; 1727 } 1728 1729 if (expo == 0) // is zero or denormal 1730 { 1731 if (((stbsp__uint64) bits << 1) == 0) // do zero 1732 { 1733 *decimal_pos = 1; 1734 *start = out; 1735 out[0] = '0'; 1736 *len = 1; 1737 return ng; 1738 } 1739 // find the right expo for denormals 1740 { 1741 stbsp__int64 v = ((stbsp__uint64)1) << 51; 1742 while ((bits & v) == 0) { 1743 --expo; 1744 v >>= 1; 1745 } 1746 } 1747 } 1748 1749 // find the decimal exponent as well as the decimal bits of the value 1750 { 1751 double ph, pl; 1752 1753 // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 1754 tens = expo - 1023; 1755 tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); 1756 1757 // move the significant bits into position and stick them into an int 1758 stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); 1759 1760 // get full as much precision from double-double as possible 1761 stbsp__ddtoS64(bits, ph, pl); 1762 1763 // check if we undershot 1764 if (((stbsp__uint64)bits) >= stbsp__tento19th) 1765 ++tens; 1766 } 1767 1768 // now do the rounding in integer land 1769 frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); 1770 if ((frac_digits < 24)) { 1771 stbsp__uint32 dg = 1; 1772 if ((stbsp__uint64)bits >= stbsp__powten[9]) 1773 dg = 10; 1774 while ((stbsp__uint64)bits >= stbsp__powten[dg]) { 1775 ++dg; 1776 if (dg == 20) 1777 goto noround; 1778 } 1779 if (frac_digits < dg) { 1780 stbsp__uint64 r; 1781 // add 0.5 at the right position and round 1782 e = dg - frac_digits; 1783 if ((stbsp__uint32)e >= 24) 1784 goto noround; 1785 r = stbsp__powten[e]; 1786 bits = bits + (r / 2); 1787 if ((stbsp__uint64)bits >= stbsp__powten[dg]) 1788 ++tens; 1789 bits /= r; 1790 } 1791 noround:; 1792 } 1793 1794 // kill long trailing runs of zeros 1795 if (bits) { 1796 stbsp__uint32 n; 1797 for (;;) { 1798 if (bits <= 0xffffffff) 1799 break; 1800 if (bits % 1000) 1801 goto donez; 1802 bits /= 1000; 1803 } 1804 n = (stbsp__uint32)bits; 1805 while ((n % 1000) == 0) 1806 n /= 1000; 1807 bits = n; 1808 donez:; 1809 } 1810 1811 // convert to string 1812 out += 64; 1813 e = 0; 1814 for (;;) { 1815 stbsp__uint32 n; 1816 char *o = out - 8; 1817 // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) 1818 if (bits >= 100000000) { 1819 n = (stbsp__uint32)(bits % 100000000); 1820 bits /= 100000000; 1821 } else { 1822 n = (stbsp__uint32)bits; 1823 bits = 0; 1824 } 1825 while (n) { 1826 out -= 2; 1827 *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; 1828 n /= 100; 1829 e += 2; 1830 } 1831 if (bits == 0) { 1832 if ((e) && (out[0] == '0')) { 1833 ++out; 1834 --e; 1835 } 1836 break; 1837 } 1838 while (out != o) { 1839 *--out = '0'; 1840 ++e; 1841 } 1842 } 1843 1844 *decimal_pos = tens; 1845 *start = out; 1846 *len = e; 1847 return ng; 1848 } 1849 1850 #undef stbsp__ddmulthi 1851 #undef stbsp__ddrenorm 1852 #undef stbsp__ddmultlo 1853 #undef stbsp__ddmultlos 1854 #undef STBSP__SPECIAL 1855 #undef STBSP__COPYFP 1856 1857 #endif // STB_SPRINTF_NOFLOAT 1858 1859 // clean up 1860 #undef stbsp__uint16 1861 #undef stbsp__uint32 1862 #undef stbsp__int32 1863 #undef stbsp__uint64 1864 #undef stbsp__int64 1865 #undef STBSP__UNALIGNED 1866 1867 #endif // STB_SPRINTF_IMPLEMENTATION 1868 1869 /* 1870 ------------------------------------------------------------------------------ 1871 This software is available under 2 licenses -- choose whichever you prefer. 1872 ------------------------------------------------------------------------------ 1873 ALTERNATIVE A - MIT License 1874 Copyright (c) 2017 Sean Barrett 1875 Permission is hereby granted, free of charge, to any person obtaining a copy of 1876 this software and associated documentation files (the "Software"), to deal in 1877 the Software without restriction, including without limitation the rights to 1878 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 1879 of the Software, and to permit persons to whom the Software is furnished to do 1880 so, subject to the following conditions: 1881 The above copyright notice and this permission notice shall be included in all 1882 copies or substantial portions of the Software. 1883 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1884 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1885 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1886 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1887 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1888 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1889 SOFTWARE. 1890 ------------------------------------------------------------------------------ 1891 ALTERNATIVE B - Public Domain (www.unlicense.org) 1892 This is free and unencumbered software released into the public domain. 1893 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 1894 software, either in source code form or as a compiled binary, for any purpose, 1895 commercial or non-commercial, and by any means. 1896 In jurisdictions that recognize copyright laws, the author or authors of this 1897 software dedicate any and all copyright interest in the software to the public 1898 domain. We make this dedication for the benefit of the public at large and to 1899 the detriment of our heirs and successors. We intend this dedication to be an 1900 overt act of relinquishment in perpetuity of all present and future rights to 1901 this software under copyright law. 1902 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1903 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1904 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1905 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 1906 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1907 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1908 ------------------------------------------------------------------------------ 1909 */