Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/imgui/src/stb_sprintf.h
4246 views
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__) || (defined(__riscv) && __riscv_xlen == 64)
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
*/
1910
1911