InChI
 
Loading...
Searching...
No Matches
stb_sprintf.h
Go to the documentation of this file.
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/*
33Single file sprintf replacement.
34
35Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
36Hereby placed in public domain.
37
38This is a full sprintf replacement that supports everything that
39the C runtime sprintfs support, including float/double, 64-bit integers,
40hex floats, field parameters (%*.*d stuff), length reads backs, etc.
41
42Why would you need this if sprintf already exists? Well, first off,
43it's *much* faster (see below). It's also much smaller than the CRT
44versions code-space-wise. We've also added some simple improvements
45that are super handy (commas in thousands, callbacks at buffer full,
46for example). Finally, the format strings for MSVC and GCC differ
47for 64-bit integers (among other small things), so this lets you use
48the same format strings in cross platform code.
49
50It uses the standard single file trick of being both the header file
51and the source itself. If you just include it normally, you just get
52the header file function definitions. To get the code, you include
53it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
54
55It only uses va_args macros from the C runtime to do it's work. It
56does cast doubles to S64s and shifts and divides U64s, which does
57drag in CRT code on most platforms.
58
59It compiles to roughly 8K with float support, and 4K without.
60As a comparison, when using MSVC static libs, calling sprintf drags
61in 16K.
62
63API:
64====
65int stbsp_sprintf( char * buf, char const * fmt, ... )
66int 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
70int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
71int 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
75int 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
83void stbsp_set_separators( char comma, char period )
84 Set the comma and period characters to use.
85
86FLOATS/DOUBLES:
87===============
88This code uses a internal float->ascii conversion method that uses
89doubles with error correction (double-doubles, for ~105 bits of
90precision). This conversion is round-trip perfect - that is, an atof
91of the values output here will give you the bit-exact double back.
92
93One difference is that our insignificant digits will be different than
94with MSVC or GCC (but they don't match each other either). We also
95don't attempt to find the minimum length matching float (pre-MSVC15
96doesn't either).
97
98If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
99and you'll save 4K of code space.
100
10164-BIT INTS:
102============
103This library also supports 64-bit integers and you can use MSVC style or
104GCC style indicators (%I64d or %lld). It supports the C99 specifiers
105for size_t and ptr_diff_t (%jd %zd) as well.
106
107EXTRAS:
108=======
109Like some GCCs, for integers and floats, you can use a ' (single quote)
110specifier and commas will be inserted on the thousands: "%'d" on 12345
111would print 12,345.
112
113For integers and floats, you can use a "$" specifier and the number
114will be converted to float and then divided to get kilo, mega, giga or
115tera 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
1172536000 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
119suffix, add "_" specifier: "%_$d" -> "2.53M".
120
121In addition to octal and hexadecimal conversions, you can print
122integers in binary: "%b" for 256 would print 100.
123
124PERFORMANCE 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
202typedef 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
208STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
209STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
210STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3);
211STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4);
212
213STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
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
254static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);
255static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);
256#define STBSP__SPECIAL 0x7000
257#endif
258
259static char stbsp__period = '.';
260static char stbsp__comma = ',';
261static 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
274STBSP__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
294static 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
309static 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
349STBSP__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 case 'n': // weird write-bytes specifier
616 {
617 int *d = va_arg(va, int *);
618 *d = tlen + (int)(bf - buf);
619 } break;
620
621#ifdef STB_SPRINTF_NOFLOAT
622 case 'A': // float
623 case 'a': // hex float
624 case 'G': // float
625 case 'g': // float
626 case 'E': // float
627 case 'e': // float
628 case 'f': // float
629 va_arg(va, double); // eat it
630 s = (char *)"No float";
631 l = 8;
632 lead[0] = 0;
633 tail[0] = 0;
634 pr = 0;
635 cs = 0;
636 STBSP__NOTUSED(dp);
637 goto scopy;
638#else
639 case 'A': // hex float
640 case 'a': // hex float
641 h = (f[0] == 'A') ? hexu : hex;
642 fv = va_arg(va, double);
643 if (pr == -1)
644 pr = 6; // default is 6
645 // read the double into a string
646 if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))
647 fl |= STBSP__NEGATIVE;
648
649 s = num + 64;
650
651 stbsp__lead_sign(fl, lead);
652
653 if (dp == -1023)
654 dp = (n64) ? -1022 : 0;
655 else
656 n64 |= (((stbsp__uint64)1) << 52);
657 n64 <<= (64 - 56);
658 if (pr < 15)
659 n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));
660// add leading chars
661
662#ifdef STB_SPRINTF_MSVC_MODE
663 *s++ = '0';
664 *s++ = 'x';
665#else
666 lead[1 + lead[0]] = '0';
667 lead[2 + lead[0]] = 'x';
668 lead[0] += 2;
669#endif
670 *s++ = h[(n64 >> 60) & 15];
671 n64 <<= 4;
672 if (pr)
673 *s++ = stbsp__period;
674 sn = s;
675
676 // print the bits
677 n = pr;
678 if (n > 13)
679 n = 13;
680 if (pr > (stbsp__int32)n)
681 tz = pr - n;
682 pr = 0;
683 while (n--) {
684 *s++ = h[(n64 >> 60) & 15];
685 n64 <<= 4;
686 }
687
688 // print the expo
689 tail[1] = h[17];
690 if (dp < 0) {
691 tail[2] = '-';
692 dp = -dp;
693 } else
694 tail[2] = '+';
695 n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));
696 tail[0] = (char)n;
697 for (;;) {
698 tail[n] = '0' + dp % 10;
699 if (n <= 3)
700 break;
701 --n;
702 dp /= 10;
703 }
704
705 dp = (int)(s - sn);
706 l = (int)(s - (num + 64));
707 s = num + 64;
708 cs = 1 + (3 << 24);
709 goto scopy;
710
711 case 'G': // float
712 case 'g': // float
713 h = (f[0] == 'G') ? hexu : hex;
714 fv = va_arg(va, double);
715 if (pr == -1)
716 pr = 6;
717 else if (pr == 0)
718 pr = 1; // default is 6
719 // read the double into a string
720 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))
721 fl |= STBSP__NEGATIVE;
722
723 // clamp the precision and delete extra zeros after clamp
724 n = pr;
725 if (l > (stbsp__uint32)pr)
726 l = pr;
727 while ((l > 1) && (pr) && (sn[l - 1] == '0')) {
728 --pr;
729 --l;
730 }
731
732 // should we use %e
733 if ((dp <= -4) || (dp > (stbsp__int32)n)) {
734 if (pr > (stbsp__int32)l)
735 pr = l - 1;
736 else if (pr)
737 --pr; // when using %e, there is one digit before the decimal
738 goto doexpfromg;
739 }
740 // this is the insane action to get the pr to match %g semantics for %f
741 if (dp > 0) {
742 pr = (dp < (stbsp__int32)l) ? l - dp : 0;
743 } else {
744 pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);
745 }
746 goto dofloatfromg;
747
748 case 'E': // float
749 case 'e': // float
750 h = (f[0] == 'E') ? hexu : hex;
751 fv = va_arg(va, double);
752 if (pr == -1)
753 pr = 6; // default is 6
754 // read the double into a string
755 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))
756 fl |= STBSP__NEGATIVE;
757 doexpfromg:
758 tail[0] = 0;
759 stbsp__lead_sign(fl, lead);
760 if (dp == STBSP__SPECIAL) {
761 s = (char *)sn;
762 cs = 0;
763 pr = 0;
764 goto scopy;
765 }
766 s = num + 64;
767 // handle leading chars
768 *s++ = sn[0];
769
770 if (pr)
771 *s++ = stbsp__period;
772
773 // handle after decimal
774 if ((l - 1) > (stbsp__uint32)pr)
775 l = pr + 1;
776 for (n = 1; n < l; n++)
777 *s++ = sn[n];
778 // trailing zeros
779 tz = pr - (l - 1);
780 pr = 0;
781 // dump expo
782 tail[1] = h[0xe];
783 dp -= 1;
784 if (dp < 0) {
785 tail[2] = '-';
786 dp = -dp;
787 } else
788 tail[2] = '+';
789#ifdef STB_SPRINTF_MSVC_MODE
790 n = 5;
791#else
792 n = (dp >= 100) ? 5 : 4;
793#endif
794 tail[0] = (char)n;
795 for (;;) {
796 tail[n] = '0' + dp % 10;
797 if (n <= 3)
798 break;
799 --n;
800 dp /= 10;
801 }
802 cs = 1 + (3 << 24); // how many tens
803 goto flt_lead;
804
805 case 'f': // float
806 fv = va_arg(va, double);
807 doafloat:
808 // do kilos
809 if (fl & STBSP__METRIC_SUFFIX) {
810 double divisor;
811 divisor = 1000.0f;
812 if (fl & STBSP__METRIC_1024)
813 divisor = 1024.0;
814 while (fl < 0x4000000) {
815 if ((fv < divisor) && (fv > -divisor))
816 break;
817 fv /= divisor;
818 fl += 0x1000000;
819 }
820 }
821 if (pr == -1)
822 pr = 6; // default is 6
823 // read the double into a string
824 if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))
825 fl |= STBSP__NEGATIVE;
826 dofloatfromg:
827 tail[0] = 0;
828 stbsp__lead_sign(fl, lead);
829 if (dp == STBSP__SPECIAL) {
830 s = (char *)sn;
831 cs = 0;
832 pr = 0;
833 goto scopy;
834 }
835 s = num + 64;
836
837 // handle the three decimal varieties
838 if (dp <= 0) {
839 stbsp__int32 i;
840 // handle 0.000*000xxxx
841 *s++ = '0';
842 if (pr)
843 *s++ = stbsp__period;
844 n = -dp;
845 if ((stbsp__int32)n > pr)
846 n = pr;
847 i = n;
848 while (i) {
849 if ((((stbsp__uintptr)s) & 3) == 0)
850 break;
851 *s++ = '0';
852 --i;
853 }
854 while (i >= 4) {
855 *(stbsp__uint32 *)s = 0x30303030;
856 s += 4;
857 i -= 4;
858 }
859 while (i) {
860 *s++ = '0';
861 --i;
862 }
863 if ((stbsp__int32)(l + n) > pr)
864 l = pr - n;
865 i = l;
866 while (i) {
867 *s++ = *sn++;
868 --i;
869 }
870 tz = pr - (n + l);
871 cs = 1 + (3 << 24); // how many tens did we write (for commas below)
872 } else {
873 cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;
874 if ((stbsp__uint32)dp >= l) {
875 // handle xxxx000*000.0
876 n = 0;
877 for (;;) {
878 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
879 cs = 0;
880 *s++ = stbsp__comma;
881 } else {
882 *s++ = sn[n];
883 ++n;
884 if (n >= l)
885 break;
886 }
887 }
888 if (n < (stbsp__uint32)dp) {
889 n = dp - n;
890 if ((fl & STBSP__TRIPLET_COMMA) == 0) {
891 while (n) {
892 if ((((stbsp__uintptr)s) & 3) == 0)
893 break;
894 *s++ = '0';
895 --n;
896 }
897 while (n >= 4) {
898 *(stbsp__uint32 *)s = 0x30303030;
899 s += 4;
900 n -= 4;
901 }
902 }
903 while (n) {
904 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
905 cs = 0;
906 *s++ = stbsp__comma;
907 } else {
908 *s++ = '0';
909 --n;
910 }
911 }
912 }
913 cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
914 if (pr) {
915 *s++ = stbsp__period;
916 tz = pr;
917 }
918 } else {
919 // handle xxxxx.xxxx000*000
920 n = 0;
921 for (;;) {
922 if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
923 cs = 0;
924 *s++ = stbsp__comma;
925 } else {
926 *s++ = sn[n];
927 ++n;
928 if (n >= (stbsp__uint32)dp)
929 break;
930 }
931 }
932 cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
933 if (pr)
934 *s++ = stbsp__period;
935 if ((l - dp) > (stbsp__uint32)pr)
936 l = pr + dp;
937 while (n < l) {
938 *s++ = sn[n];
939 ++n;
940 }
941 tz = pr - (l - dp);
942 }
943 }
944 pr = 0;
945
946 // handle k,m,g,t
947 if (fl & STBSP__METRIC_SUFFIX) {
948 char idx;
949 idx = 1;
950 if (fl & STBSP__METRIC_NOSPACE)
951 idx = 0;
952 tail[0] = idx;
953 tail[1] = ' ';
954 {
955 if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.
956 if (fl & STBSP__METRIC_1024)
957 tail[idx + 1] = "_KMGT"[fl >> 24];
958 else
959 tail[idx + 1] = "_kMGT"[fl >> 24];
960 idx++;
961 // If printing kibits and not in jedec, add the 'i'.
962 if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {
963 tail[idx + 1] = 'i';
964 idx++;
965 }
966 tail[0] = idx;
967 }
968 }
969 };
970
971 flt_lead:
972 // get the length that we copied
973 l = (stbsp__uint32)(s - (num + 64));
974 s = num + 64;
975 goto scopy;
976#endif
977
978 case 'B': // upper binary
979 case 'b': // lower binary
980 h = (f[0] == 'B') ? hexu : hex;
981 lead[0] = 0;
982 if (fl & STBSP__LEADING_0X) {
983 lead[0] = 2;
984 lead[1] = '0';
985 lead[2] = h[0xb];
986 }
987 l = (8 << 4) | (1 << 8);
988 goto radixnum;
989
990 case 'o': // octal
991 h = hexu;
992 lead[0] = 0;
993 if (fl & STBSP__LEADING_0X) {
994 lead[0] = 1;
995 lead[1] = '0';
996 }
997 l = (3 << 4) | (3 << 8);
998 goto radixnum;
999
1000 case 'p': // pointer
1001 fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;
1002 pr = sizeof(void *) * 2;
1003 fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
1004 // fall through - to X
1005
1006 case 'X': // upper hex
1007 case 'x': // lower hex
1008 h = (f[0] == 'X') ? hexu : hex;
1009 l = (4 << 4) | (4 << 8);
1010 lead[0] = 0;
1011 if (fl & STBSP__LEADING_0X) {
1012 lead[0] = 2;
1013 lead[1] = '0';
1014 lead[2] = h[16];
1015 }
1016 radixnum:
1017 // get the number
1018 if (fl & STBSP__INTMAX)
1019 n64 = va_arg(va, stbsp__uint64);
1020 else
1021 n64 = va_arg(va, stbsp__uint32);
1022
1023 s = num + STBSP__NUMSZ;
1024 dp = 0;
1025 // clear tail, and clear leading if value is zero
1026 tail[0] = 0;
1027 if (n64 == 0) {
1028 lead[0] = 0;
1029 if (pr == 0) {
1030 l = 0;
1031 cs = 0;
1032 goto scopy;
1033 }
1034 }
1035 // convert to string
1036 for (;;) {
1037 *--s = h[n64 & ((1 << (l >> 8)) - 1)];
1038 n64 >>= (l >> 8);
1039 if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))
1040 break;
1041 if (fl & STBSP__TRIPLET_COMMA) {
1042 ++l;
1043 if ((l & 15) == ((l >> 4) & 15)) {
1044 l &= ~15;
1045 *--s = stbsp__comma;
1046 }
1047 }
1048 };
1049 // get the tens and the comma pos
1050 cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);
1051 // get the length that we copied
1052 l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
1053 // copy it
1054 goto scopy;
1055
1056 case 'u': // unsigned
1057 case 'i':
1058 case 'd': // integer
1059 // get the integer and abs it
1060 if (fl & STBSP__INTMAX) {
1061 stbsp__int64 i64 = va_arg(va, stbsp__int64);
1062 n64 = (stbsp__uint64)i64;
1063 if ((f[0] != 'u') && (i64 < 0)) {
1064 n64 = (stbsp__uint64)-i64;
1065 fl |= STBSP__NEGATIVE;
1066 }
1067 } else {
1068 stbsp__int32 i = va_arg(va, stbsp__int32);
1069 n64 = (stbsp__uint32)i;
1070 if ((f[0] != 'u') && (i < 0)) {
1071 n64 = (stbsp__uint32)-i;
1072 fl |= STBSP__NEGATIVE;
1073 }
1074 }
1075
1076#ifndef STB_SPRINTF_NOFLOAT
1077 if (fl & STBSP__METRIC_SUFFIX) {
1078 if (n64 < 1024)
1079 pr = 0;
1080 else if (pr == -1)
1081 pr = 1;
1082 fv = (double)(stbsp__int64)n64;
1083 goto doafloat;
1084 }
1085#endif
1086
1087 // convert to string
1088 s = num + STBSP__NUMSZ;
1089 l = 0;
1090
1091 for (;;) {
1092 // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
1093 char *o = s - 8;
1094 if (n64 >= 100000000) {
1095 n = (stbsp__uint32)(n64 % 100000000);
1096 n64 /= 100000000;
1097 } else {
1098 n = (stbsp__uint32)n64;
1099 n64 = 0;
1100 }
1101 if ((fl & STBSP__TRIPLET_COMMA) == 0) {
1102 do {
1103 s -= 2;
1104 *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
1105 n /= 100;
1106 } while (n);
1107 }
1108 while (n) {
1109 if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
1110 l = 0;
1111 *--s = stbsp__comma;
1112 --o;
1113 } else {
1114 *--s = (char)(n % 10) + '0';
1115 n /= 10;
1116 }
1117 }
1118 if (n64 == 0) {
1119 if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))
1120 ++s;
1121 break;
1122 }
1123 while (s != o)
1124 if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
1125 l = 0;
1126 *--s = stbsp__comma;
1127 --o;
1128 } else {
1129 *--s = '0';
1130 }
1131 }
1132
1133 tail[0] = 0;
1134 stbsp__lead_sign(fl, lead);
1135
1136 // get the length that we copied
1137 l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
1138 if (l == 0) {
1139 *--s = '0';
1140 l = 1;
1141 }
1142 cs = l + (3 << 24);
1143 if (pr < 0)
1144 pr = 0;
1145
1146 scopy:
1147 // get fw=leading/trailing space, pr=leading zeros
1148 if (pr < (stbsp__int32)l)
1149 pr = l;
1150 n = pr + lead[0] + tail[0] + tz;
1151 if (fw < (stbsp__int32)n)
1152 fw = n;
1153 fw -= n;
1154 pr -= l;
1155
1156 // handle right justify and leading zeros
1157 if ((fl & STBSP__LEFTJUST) == 0) {
1158 if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr
1159 {
1160 pr = (fw > pr) ? fw : pr;
1161 fw = 0;
1162 } else {
1163 fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
1164 }
1165 }
1166
1167 // copy the spaces and/or zeros
1168 if (fw + pr) {
1169 stbsp__int32 i;
1170 stbsp__uint32 c;
1171
1172 // copy leading spaces (or when doing %8.4d stuff)
1173 if ((fl & STBSP__LEFTJUST) == 0)
1174 while (fw > 0) {
1175 stbsp__cb_buf_clamp(i, fw);
1176 fw -= i;
1177 while (i) {
1178 if ((((stbsp__uintptr)bf) & 3) == 0)
1179 break;
1180 *bf++ = ' ';
1181 --i;
1182 }
1183 while (i >= 4) {
1184 *(stbsp__uint32 *)bf = 0x20202020;
1185 bf += 4;
1186 i -= 4;
1187 }
1188 while (i) {
1189 *bf++ = ' ';
1190 --i;
1191 }
1192 stbsp__chk_cb_buf(1);
1193 }
1194
1195 // copy leader
1196 sn = lead + 1;
1197 while (lead[0]) {
1198 stbsp__cb_buf_clamp(i, lead[0]);
1199 lead[0] -= (char)i;
1200 while (i) {
1201 *bf++ = *sn++;
1202 --i;
1203 }
1204 stbsp__chk_cb_buf(1);
1205 }
1206
1207 // copy leading zeros
1208 c = cs >> 24;
1209 cs &= 0xffffff;
1210 cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;
1211 while (pr > 0) {
1212 stbsp__cb_buf_clamp(i, pr);
1213 pr -= i;
1214 if ((fl & STBSP__TRIPLET_COMMA) == 0) {
1215 while (i) {
1216 if ((((stbsp__uintptr)bf) & 3) == 0)
1217 break;
1218 *bf++ = '0';
1219 --i;
1220 }
1221 while (i >= 4) {
1222 *(stbsp__uint32 *)bf = 0x30303030;
1223 bf += 4;
1224 i -= 4;
1225 }
1226 }
1227 while (i) {
1228 if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {
1229 cs = 0;
1230 *bf++ = stbsp__comma;
1231 } else
1232 *bf++ = '0';
1233 --i;
1234 }
1235 stbsp__chk_cb_buf(1);
1236 }
1237 }
1238
1239 // copy leader if there is still one
1240 sn = lead + 1;
1241 while (lead[0]) {
1242 stbsp__int32 i;
1243 stbsp__cb_buf_clamp(i, lead[0]);
1244 lead[0] -= (char)i;
1245 while (i) {
1246 *bf++ = *sn++;
1247 --i;
1248 }
1249 stbsp__chk_cb_buf(1);
1250 }
1251
1252 // copy the string
1253 n = l;
1254 while (n) {
1255 stbsp__int32 i;
1256 stbsp__cb_buf_clamp(i, n);
1257 n -= i;
1258 STBSP__UNALIGNED(while (i >= 4) {
1259 *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s;
1260 bf += 4;
1261 s += 4;
1262 i -= 4;
1263 })
1264 while (i) {
1265 *bf++ = *s++;
1266 --i;
1267 }
1268 stbsp__chk_cb_buf(1);
1269 }
1270
1271 // copy trailing zeros
1272 while (tz) {
1273 stbsp__int32 i;
1274 stbsp__cb_buf_clamp(i, tz);
1275 tz -= i;
1276 while (i) {
1277 if ((((stbsp__uintptr)bf) & 3) == 0)
1278 break;
1279 *bf++ = '0';
1280 --i;
1281 }
1282 while (i >= 4) {
1283 *(stbsp__uint32 *)bf = 0x30303030;
1284 bf += 4;
1285 i -= 4;
1286 }
1287 while (i) {
1288 *bf++ = '0';
1289 --i;
1290 }
1291 stbsp__chk_cb_buf(1);
1292 }
1293
1294 // copy tail if there is one
1295 sn = tail + 1;
1296 while (tail[0]) {
1297 stbsp__int32 i;
1298 stbsp__cb_buf_clamp(i, tail[0]);
1299 tail[0] -= (char)i;
1300 while (i) {
1301 *bf++ = *sn++;
1302 --i;
1303 }
1304 stbsp__chk_cb_buf(1);
1305 }
1306
1307 // handle the left justify
1308 if (fl & STBSP__LEFTJUST)
1309 if (fw > 0) {
1310 while (fw) {
1311 stbsp__int32 i;
1312 stbsp__cb_buf_clamp(i, fw);
1313 fw -= i;
1314 while (i) {
1315 if ((((stbsp__uintptr)bf) & 3) == 0)
1316 break;
1317 *bf++ = ' ';
1318 --i;
1319 }
1320 while (i >= 4) {
1321 *(stbsp__uint32 *)bf = 0x20202020;
1322 bf += 4;
1323 i -= 4;
1324 }
1325 while (i--)
1326 *bf++ = ' ';
1327 stbsp__chk_cb_buf(1);
1328 }
1329 }
1330 break;
1331
1332 default: // unknown, just copy code
1333 s = num + STBSP__NUMSZ - 1;
1334 *s = f[0];
1335 l = 1;
1336 fw = fl = 0;
1337 lead[0] = 0;
1338 tail[0] = 0;
1339 pr = 0;
1340 dp = 0;
1341 cs = 0;
1342 goto scopy;
1343 }
1344 ++f;
1345 }
1346endfmt:
1347
1348 if (!callback)
1349 *bf = 0;
1350 else
1351 stbsp__flush_cb();
1352
1353done:
1354 return tlen + (int)(bf - buf);
1355}
1356
1357// cleanup
1358#undef STBSP__LEFTJUST
1359#undef STBSP__LEADINGPLUS
1360#undef STBSP__LEADINGSPACE
1361#undef STBSP__LEADING_0X
1362#undef STBSP__LEADINGZERO
1363#undef STBSP__INTMAX
1364#undef STBSP__TRIPLET_COMMA
1365#undef STBSP__NEGATIVE
1366#undef STBSP__METRIC_SUFFIX
1367#undef STBSP__NUMSZ
1368#undef stbsp__chk_cb_bufL
1369#undef stbsp__chk_cb_buf
1370#undef stbsp__flush_cb
1371#undef stbsp__cb_buf_clamp
1372
1373// ============================================================================
1374// wrapper functions
1375
1376STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
1377{
1378 int result;
1379 va_list va;
1380 va_start(va, fmt);
1381 result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
1382 va_end(va);
1383 return result;
1384}
1385
1386typedef struct stbsp__context {
1387 char *buf;
1388 int count;
1389 int length;
1390 char tmp[STB_SPRINTF_MIN];
1391} stbsp__context;
1392
1393static char *stbsp__clamp_callback(const char *buf, void *user, int len)
1394{
1395 stbsp__context *c = (stbsp__context *)user;
1396 c->length += len;
1397
1398 if (len > c->count)
1399 len = c->count;
1400
1401 if (len) {
1402 if (buf != c->buf) {
1403 const char *s, *se;
1404 char *d;
1405 d = c->buf;
1406 s = buf;
1407 se = buf + len;
1408 do {
1409 *d++ = *s++;
1410 } while (s < se);
1411 }
1412 c->buf += len;
1413 c->count -= len;
1414 }
1415
1416 if (c->count <= 0)
1417 return c->tmp;
1418 return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can
1419}
1420
1421static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )
1422{
1423 stbsp__context * c = (stbsp__context*)user;
1424 (void) sizeof(buf);
1425
1426 c->length += len;
1427 return c->tmp; // go direct into buffer if you can
1428}
1429
1430STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
1431{
1432 stbsp__context c;
1433
1434 if ( (count == 0) && !buf )
1435 {
1436 c.length = 0;
1437
1438 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );
1439 }
1440 else
1441 {
1442 int l;
1443
1444 c.buf = buf;
1445 c.count = count;
1446 c.length = 0;
1447
1448 STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );
1449
1450 // zero-terminate
1451 l = (int)( c.buf - buf );
1452 if ( l >= count ) // should never be greater, only equal (or less) than count
1453 l = count - 1;
1454 buf[l] = 0;
1455 }
1456
1457 return c.length;
1458}
1459
1460STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)
1461{
1462 int result;
1463 va_list va;
1464 va_start(va, fmt);
1465
1466 result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);
1467 va_end(va);
1468
1469 return result;
1470}
1471
1472STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)
1473{
1474 return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
1475}
1476
1477// =======================================================================
1478// low level float utility functions
1479
1480#ifndef STB_SPRINTF_NOFLOAT
1481
1482// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
1483#define STBSP__COPYFP(dest, src) \
1484 { \
1485 int cn; \
1486 for (cn = 0; cn < 8; cn++) \
1487 ((char *)&dest)[cn] = ((char *)&src)[cn]; \
1488 }
1489
1490// get float info
1491static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)
1492{
1493 double d;
1494 stbsp__int64 b = 0;
1495
1496 // load value and round at the frac_digits
1497 d = value;
1498
1499 STBSP__COPYFP(b, d);
1500
1501 *bits = b & ((((stbsp__uint64)1) << 52) - 1);
1502 *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);
1503
1504 return (stbsp__int32)((stbsp__uint64) b >> 63);
1505}
1506
1507static double const stbsp__bot[23] = {
1508 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,
1509 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022
1510};
1511static double const stbsp__negbot[22] = {
1512 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,
1513 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022
1514};
1515static double const stbsp__negboterr[22] = {
1516 -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,
1517 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029,
1518 -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035,
1519 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039
1520};
1521static double const stbsp__top[13] = {
1522 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
1523};
1524static double const stbsp__negtop[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};
1527static double const stbsp__toperr[13] = {
1528 8388608,
1529 6.8601809640529717e+028,
1530 -7.253143638152921e+052,
1531 -4.3377296974619174e+075,
1532 -1.5559416129466825e+098,
1533 -3.2841562489204913e+121,
1534 -3.7745893248228135e+144,
1535 -1.7356668416969134e+167,
1536 -3.8893577551088374e+190,
1537 -9.9566444326005119e+213,
1538 6.3641293062232429e+236,
1539 -5.2069140800249813e+259,
1540 -5.2504760255204387e+282
1541};
1542static double const stbsp__negtoperr[13] = {
1543 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109,
1544 -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201,
1545 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,
1546 8.0970921678014997e-317
1547};
1548
1549#if defined(_MSC_VER) && (_MSC_VER <= 1200)
1550static stbsp__uint64 const stbsp__powten[20] = {
1551 1,
1552 10,
1553 100,
1554 1000,
1555 10000,
1556 100000,
1557 1000000,
1558 10000000,
1559 100000000,
1560 1000000000,
1561 10000000000,
1562 100000000000,
1563 1000000000000,
1564 10000000000000,
1565 100000000000000,
1566 1000000000000000,
1567 10000000000000000,
1568 100000000000000000,
1569 1000000000000000000,
1570 10000000000000000000U
1571};
1572#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
1573#else
1574static stbsp__uint64 const stbsp__powten[20] = {
1575 1,
1576 10,
1577 100,
1578 1000,
1579 10000,
1580 100000,
1581 1000000,
1582 10000000,
1583 100000000,
1584 1000000000,
1585 10000000000ULL,
1586 100000000000ULL,
1587 1000000000000ULL,
1588 10000000000000ULL,
1589 100000000000000ULL,
1590 1000000000000000ULL,
1591 10000000000000000ULL,
1592 100000000000000000ULL,
1593 1000000000000000000ULL,
1594 10000000000000000000ULL
1595};
1596#define stbsp__tento19th (1000000000000000000ULL)
1597#endif
1598
1599#define stbsp__ddmulthi(oh, ol, xh, yh) \
1600 { \
1601 double ahi = 0, alo, bhi = 0, blo; \
1602 stbsp__int64 bt; \
1603 oh = xh * yh; \
1604 STBSP__COPYFP(bt, xh); \
1605 bt &= ((~(stbsp__uint64)0) << 27); \
1606 STBSP__COPYFP(ahi, bt); \
1607 alo = xh - ahi; \
1608 STBSP__COPYFP(bt, yh); \
1609 bt &= ((~(stbsp__uint64)0) << 27); \
1610 STBSP__COPYFP(bhi, bt); \
1611 blo = yh - bhi; \
1612 ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \
1613 }
1614
1615#define stbsp__ddtoS64(ob, xh, xl) \
1616 { \
1617 double ahi = 0, alo, vh, t; \
1618 ob = (stbsp__int64)xh; \
1619 vh = (double)ob; \
1620 ahi = (xh - vh); \
1621 t = (ahi - xh); \
1622 alo = (xh - (ahi - t)) - (vh + t); \
1623 ob += (stbsp__int64)(ahi + alo + xl); \
1624 }
1625
1626#define stbsp__ddrenorm(oh, ol) \
1627 { \
1628 double s; \
1629 s = oh + ol; \
1630 ol = ol - (s - oh); \
1631 oh = s; \
1632 }
1633
1634#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);
1635
1636#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);
1637
1638static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350
1639{
1640 double ph, pl;
1641 if ((power >= 0) && (power <= 22)) {
1642 stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);
1643 } else {
1644 stbsp__int32 e, et, eb;
1645 double p2h, p2l;
1646
1647 e = power;
1648 if (power < 0)
1649 e = -e;
1650 et = (e * 0x2c9) >> 14; /* %23 */
1651 if (et > 13)
1652 et = 13;
1653 eb = e - (et * 23);
1654
1655 ph = d;
1656 pl = 0.0;
1657 if (power < 0) {
1658 if (eb) {
1659 --eb;
1660 stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);
1661 stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);
1662 }
1663 if (et) {
1664 stbsp__ddrenorm(ph, pl);
1665 --et;
1666 stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);
1667 stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);
1668 ph = p2h;
1669 pl = p2l;
1670 }
1671 } else {
1672 if (eb) {
1673 e = eb;
1674 if (eb > 22)
1675 eb = 22;
1676 e -= eb;
1677 stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);
1678 if (e) {
1679 stbsp__ddrenorm(ph, pl);
1680 stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);
1681 stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);
1682 ph = p2h;
1683 pl = p2l;
1684 }
1685 }
1686 if (et) {
1687 stbsp__ddrenorm(ph, pl);
1688 --et;
1689 stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);
1690 stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);
1691 ph = p2h;
1692 pl = p2l;
1693 }
1694 }
1695 }
1696 stbsp__ddrenorm(ph, pl);
1697 *ohi = ph;
1698 *olo = pl;
1699}
1700
1701// given a float value, returns the significant bits in bits, and the position of the
1702// decimal point in decimal_pos. +/-INF and NAN are specified by special values
1703// returned in the decimal_pos parameter.
1704// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
1705static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)
1706{
1707 double d;
1708 stbsp__int64 bits = 0;
1709 stbsp__int32 expo, e, ng, tens;
1710
1711 d = value;
1712 STBSP__COPYFP(bits, d);
1713 expo = (stbsp__int32)((bits >> 52) & 2047);
1714 ng = (stbsp__int32)((stbsp__uint64) bits >> 63);
1715 if (ng)
1716 d = -d;
1717
1718 if (expo == 2047) // is nan or inf?
1719 {
1720 *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";
1721 *decimal_pos = STBSP__SPECIAL;
1722 *len = 3;
1723 return ng;
1724 }
1725
1726 if (expo == 0) // is zero or denormal
1727 {
1728 if (((stbsp__uint64) bits << 1) == 0) // do zero
1729 {
1730 *decimal_pos = 1;
1731 *start = out;
1732 out[0] = '0';
1733 *len = 1;
1734 return ng;
1735 }
1736 // find the right expo for denormals
1737 {
1738 stbsp__int64 v = ((stbsp__uint64)1) << 51;
1739 while ((bits & v) == 0) {
1740 --expo;
1741 v >>= 1;
1742 }
1743 }
1744 }
1745
1746 // find the decimal exponent as well as the decimal bits of the value
1747 {
1748 double ph, pl;
1749
1750 // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
1751 tens = expo - 1023;
1752 tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);
1753
1754 // move the significant bits into position and stick them into an int
1755 stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);
1756
1757 // get full as much precision from double-double as possible
1758 stbsp__ddtoS64(bits, ph, pl);
1759
1760 // check if we undershot
1761 if (((stbsp__uint64)bits) >= stbsp__tento19th)
1762 ++tens;
1763 }
1764
1765 // now do the rounding in integer land
1766 frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);
1767 if ((frac_digits < 24)) {
1768 stbsp__uint32 dg = 1;
1769 if ((stbsp__uint64)bits >= stbsp__powten[9])
1770 dg = 10;
1771 while ((stbsp__uint64)bits >= stbsp__powten[dg]) {
1772 ++dg;
1773 if (dg == 20)
1774 goto noround;
1775 }
1776 if (frac_digits < dg) {
1777 stbsp__uint64 r;
1778 // add 0.5 at the right position and round
1779 e = dg - frac_digits;
1780 if ((stbsp__uint32)e >= 24)
1781 goto noround;
1782 r = stbsp__powten[e];
1783 bits = bits + (r / 2);
1784 if ((stbsp__uint64)bits >= stbsp__powten[dg])
1785 ++tens;
1786 bits /= r;
1787 }
1788 noround:;
1789 }
1790
1791 // kill long trailing runs of zeros
1792 if (bits) {
1793 stbsp__uint32 n;
1794 for (;;) {
1795 if (bits <= 0xffffffff)
1796 break;
1797 if (bits % 1000)
1798 goto donez;
1799 bits /= 1000;
1800 }
1801 n = (stbsp__uint32)bits;
1802 while ((n % 1000) == 0)
1803 n /= 1000;
1804 bits = n;
1805 donez:;
1806 }
1807
1808 // convert to string
1809 out += 64;
1810 e = 0;
1811 for (;;) {
1812 stbsp__uint32 n;
1813 char *o = out - 8;
1814 // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
1815 if (bits >= 100000000) {
1816 n = (stbsp__uint32)(bits % 100000000);
1817 bits /= 100000000;
1818 } else {
1819 n = (stbsp__uint32)bits;
1820 bits = 0;
1821 }
1822 while (n) {
1823 out -= 2;
1824 *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
1825 n /= 100;
1826 e += 2;
1827 }
1828 if (bits == 0) {
1829 if ((e) && (out[0] == '0')) {
1830 ++out;
1831 --e;
1832 }
1833 break;
1834 }
1835 while (out != o) {
1836 *--out = '0';
1837 ++e;
1838 }
1839 }
1840
1841 *decimal_pos = tens;
1842 *start = out;
1843 *len = e;
1844 return ng;
1845}
1846
1847#undef stbsp__ddmulthi
1848#undef stbsp__ddrenorm
1849#undef stbsp__ddmultlo
1850#undef stbsp__ddmultlos
1851#undef STBSP__SPECIAL
1852#undef STBSP__COPYFP
1853
1854#endif // STB_SPRINTF_NOFLOAT
1855
1856// clean up
1857#undef stbsp__uint16
1858#undef stbsp__uint32
1859#undef stbsp__int32
1860#undef stbsp__uint64
1861#undef stbsp__int64
1862#undef STBSP__UNALIGNED
1863
1864#endif // STB_SPRINTF_IMPLEMENTATION
1865
1866/*
1867------------------------------------------------------------------------------
1868This software is available under 2 licenses -- choose whichever you prefer.
1869------------------------------------------------------------------------------
1870ALTERNATIVE A - MIT License
1871Copyright (c) 2017 Sean Barrett
1872Permission is hereby granted, free of charge, to any person obtaining a copy of
1873this software and associated documentation files (the "Software"), to deal in
1874the Software without restriction, including without limitation the rights to
1875use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1876of the Software, and to permit persons to whom the Software is furnished to do
1877so, subject to the following conditions:
1878The above copyright notice and this permission notice shall be included in all
1879copies or substantial portions of the Software.
1880THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1881IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1882FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1883AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1884LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1885OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1886SOFTWARE.
1887------------------------------------------------------------------------------
1888ALTERNATIVE B - Public Domain (www.unlicense.org)
1889This is free and unencumbered software released into the public domain.
1890Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
1891software, either in source code form or as a compiled binary, for any purpose,
1892commercial or non-commercial, and by any means.
1893In jurisdictions that recognize copyright laws, the author or authors of this
1894software dedicate any and all copyright interest in the software to the public
1895domain. We make this dedication for the benefit of the public at large and to
1896the detriment of our heirs and successors. We intend this dedication to be an
1897overt act of relinquishment in perpetuity of all present and future rights to
1898this software under copyright law.
1899THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1900IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1901FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1902AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
1903ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
1904WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1905------------------------------------------------------------------------------
1906*/
#define STBSP__PUBLICDEF
Definition stb_sprintf.h:176
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() sprintf(char *buf, char const *fmt,...) STBSP__ATTRIBUTE_FORMAT(2
#define STB_SPRINTF_DECORATE(name)
Definition stb_sprintf.h:205
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() vsprintf(char *buf, char const *fmt, va_list va)
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE() set_separators(char comma, char period)
#define STBSP__NOTUSED(v)
Definition stb_sprintf.h:193
#define STBSP__PUBLICDEC
Definition stb_sprintf.h:175
#define STBSP__ASAN
Definition stb_sprintf.h:164
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() vsprintfcb(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
#define STBSP__ATTRIBUTE_FORMAT(fmt, va)
Definition stb_sprintf.h:187
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() snprintf(char *buf, int count, char const *fmt,...) STBSP__ATTRIBUTE_FORMAT(3
char * STBSP_SPRINTFCB(const char *buf, void *user, int len)
Definition stb_sprintf.h:202
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE() vsnprintf(char *buf, int count, char const *fmt, va_list va)
#define STB_SPRINTF_MIN
Definition stb_sprintf.h:200