added stb, more binaryout changes"
[henge/apc.git] / stb / deprecated / rrsprintf.h
1 #ifndef RR_SPRINTF_H_INCLUDE
2 #define RR_SPRINTF_H_INCLUDE
3
4 /*
5 Single file sprintf replacement.
6
7 Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
8 Hereby placed in public domain.
9
10 This is a full sprintf replacement that supports everything that
11 the C runtime sprintfs support, including float/double, 64-bit integers,
12 hex floats, field parameters (%*.*d stuff), length reads backs, etc.
13
14 Why would you need this if sprintf already exists? Well, first off,
15 it's *much* faster (see below). It's also much smaller than the CRT
16 versions code-space-wise. We've also added some simple improvements
17 that are super handy (commas in thousands, callbacks at buffer full,
18 for example). Finally, the format strings for MSVC and GCC differ
19 for 64-bit integers (among other small things), so this lets you use
20 the same format strings in cross platform code.
21
22 It uses the standard single file trick of being both the header file
23 and the source itself. If you just include it normally, you just get
24 the header file function definitions. To get the code, you include
25 it from a C or C++ file and define RR_SPRINTF_IMPLEMENTATION first.
26
27 It only uses va_args macros from the C runtime to do it's work. It
28 does cast doubles to S64s and shifts and divides U64s, which does
29 drag in CRT code on most platforms.
30
31 It compiles to roughly 8K with float support, and 4K without.
32 As a comparison, when using MSVC static libs, calling sprintf drags
33 in 16K.
34
35 API:
36 ====
37 int rrsprintf( char * buf, char const * fmt, ... )
38 int rrsnprintf( char * buf, int count, char const * fmt, ... )
39 Convert an arg list into a buffer. rrsnprintf always returns
40 a zero-terminated string (unlike regular snprintf).
41
42 int rrvsprintf( char * buf, char const * fmt, va_list va )
43 int rrvsnprintf( char * buf, int count, char const * fmt, va_list va )
44 Convert a va_list arg list into a buffer. rrvsnprintf always returns
45 a zero-terminated string (unlike regular snprintf).
46
47 int rrvsprintfcb( RRSPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
48 typedef char * RRSPRINTFCB( char const * buf, void * user, int len );
49 Convert into a buffer, calling back every RR_SPRINTF_MIN chars.
50 Your callback can then copy the chars out, print them or whatever.
51 This function is actually the workhorse for everything else.
52 The buffer you pass in must hold at least RR_SPRINTF_MIN characters.
53 // you return the next buffer to use or 0 to stop converting
54
55 void rrsetseparators( char comma, char period )
56 Set the comma and period characters to use.
57
58 FLOATS/DOUBLES:
59 ===============
60 This code uses a internal float->ascii conversion method that uses
61 doubles with error correction (double-doubles, for ~105 bits of
62 precision). This conversion is round-trip perfect - that is, an atof
63 of the values output here will give you the bit-exact double back.
64
65 One difference is that our insignificant digits will be different than
66 with MSVC or GCC (but they don't match each other either). We also
67 don't attempt to find the minimum length matching float (pre-MSVC15
68 doesn't either).
69
70 If you don't need float or doubles at all, define RR_SPRINTF_NOFLOAT
71 and you'll save 4K of code space.
72
73 64-BIT INTS:
74 ============
75 This library also supports 64-bit integers and you can use MSVC style or
76 GCC style indicators (%I64d or %lld). It supports the C99 specifiers
77 for size_t and ptr_diff_t (%jd %zd) as well.
78
79 EXTRAS:
80 =======
81 Like some GCCs, for integers and floats, you can use a ' (single quote)
82 specifier and commas will be inserted on the thousands: "%'d" on 12345
83 would print 12,345.
84
85 For integers and floats, you can use a "$" specifier and the number
86 will be converted to float and then divided to get kilo, mega, giga or
87 tera and then printed, so "%$d" 1024 is "1.0 k", "%$.2d" 2536000 is
88 "2.42 m", etc.
89
90 In addition to octal and hexadecimal conversions, you can print
91 integers in binary: "%b" for 256 would print 100.
92
93 PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
94 ===================================================================
95 "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
96 "%24d" across all 32-bit ints (4.5x/4.2x faster)
97 "%x" across all 32-bit ints (4.5x/3.8x faster)
98 "%08x" across all 32-bit ints (4.3x/3.8x faster)
99 "%f" across e-10 to e+10 floats (7.3x/6.0x faster)
100 "%e" across e-10 to e+10 floats (8.1x/6.0x faster)
101 "%g" across e-10 to e+10 floats (10.0x/7.1x faster)
102 "%f" for values near e-300 (7.9x/6.5x faster)
103 "%f" for values near e+300 (10.0x/9.1x faster)
104 "%e" for values near e-300 (10.1x/7.0x faster)
105 "%e" for values near e+300 (9.2x/6.0x faster)
106 "%.320f" for values near e-300 (12.6x/11.2x faster)
107 "%a" for random values (8.6x/4.3x faster)
108 "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
109 "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
110 "%s%s%s" for 64 char strings (7.1x/7.3x faster)
111 "...512 char string..." ( 35.0x/32.5x faster!)
112 */
113
114 #ifdef RR_SPRINTF_STATIC
115 #define RRPUBLIC_DEC static
116 #define RRPUBLIC_DEF static
117 #else
118 #ifdef __cplusplus
119 #define RRPUBLIC_DEC extern "C"
120 #define RRPUBLIC_DEF extern "C"
121 #else
122 #define RRPUBLIC_DEC extern
123 #define RRPUBLIC_DEF
124 #endif
125 #endif
126
127 #include <stdarg.h> // for va_list()
128
129 #ifndef RR_SPRINTF_MIN
130 #define RR_SPRINTF_MIN 512 // how many characters per callback
131 #endif
132 typedef char * RRSPRINTFCB( char * buf, void * user, int len );
133
134 #ifndef RR_SPRINTF_DECORATE
135 #define RR_SPRINTF_DECORATE(name) rr##name // define this before including if you want to change the names
136 #endif
137
138 #ifndef RR_SPRINTF_IMPLEMENTATION
139
140 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va );
141 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va );
142 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( sprintf ) ( char * buf, char const * fmt, ... );
143 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... );
144
145 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintfcb )( RRSPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va );
146 RRPUBLIC_DEF void RR_SPRINTF_DECORATE( setseparators )( char comma, char period );
147
148 #else
149
150 #include <stdlib.h> // for va_arg()
151
152 #define rU32 unsigned int
153 #define rS32 signed int
154
155 #ifdef _MSC_VER
156 #define rU64 unsigned __int64
157 #define rS64 signed __int64
158 #else
159 #define rU64 unsigned long long
160 #define rS64 signed long long
161 #endif
162 #define rU16 unsigned short
163
164 #ifndef rUINTa
165 #if defined(__ppc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
166 #define rUINTa rU64
167 #else
168 #define rUINTa rU32
169 #endif
170 #endif
171
172 #ifndef RR_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
173 #if defined(_MSC_VER) && (_MSC_VER<1900)
174 #define RR_SPRINTF_MSVC_MODE
175 #endif
176 #endif
177
178 #ifdef RR_SPRINTF_NOUNALIGNED // define this before inclusion to force rrsprint to always use aligned accesses
179 #define RR_UNALIGNED(code)
180 #else
181 #define RR_UNALIGNED(code) code
182 #endif
183
184 #ifndef RR_SPRINTF_NOFLOAT
185 // internal float utility functions
186 static rS32 rrreal_to_str( char const * * start, rU32 * len, char *out, rS32 * decimal_pos, double value, rU32 frac_digits );
187 static rS32 rrreal_to_parts( rS64 * bits, rS32 * expo, double value );
188 #define RRSPECIAL 0x7000
189 #endif
190
191 static char RRperiod='.';
192 static char RRcomma=',';
193 static char rrdiglookup[201]="00010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899";
194
195 RRPUBLIC_DEF void RR_SPRINTF_DECORATE( setseparators )( char pcomma, char pperiod )
196 {
197 RRperiod=pperiod;
198 RRcomma=pcomma;
199 }
200
201 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintfcb )( RRSPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
202 {
203 static char hex[]="0123456789abcdefxp";
204 static char hexu[]="0123456789ABCDEFXP";
205 char * bf;
206 char const * f;
207 int tlen = 0;
208
209 bf = buf;
210 f = fmt;
211 for(;;)
212 {
213 rS32 fw,pr,tz; rU32 fl;
214
215 #define LJ 1
216 #define LP 2
217 #define LS 4
218 #define LX 8
219 #define LZ 16
220 #define BI 32
221 #define CS 64
222 #define NG 128
223 #define KI 256
224 #define HW 512
225
226 // macros for the callback buffer stuff
227 #define chk_cb_bufL(bytes) { int len = (int)(bf-buf); if ((len+(bytes))>=RR_SPRINTF_MIN) { tlen+=len; if (0==(bf=buf=callback(buf,user,len))) goto done; } }
228 #define chk_cb_buf(bytes) { if ( callback ) { chk_cb_bufL(bytes); } }
229 #define flush_cb() { chk_cb_bufL(RR_SPRINTF_MIN-1); } //flush if there is even one byte in the buffer
230 #define cb_buf_clamp(cl,v) cl = v; if ( callback ) { int lg = RR_SPRINTF_MIN-(int)(bf-buf); if (cl>lg) cl=lg; }
231
232 // fast copy everything up to the next % (or end of string)
233 for(;;)
234 {
235 while (((rUINTa)f)&3)
236 {
237 schk1: if (f[0]=='%') goto scandd;
238 schk2: if (f[0]==0) goto endfmt;
239 chk_cb_buf(1); *bf++=f[0]; ++f;
240 }
241 for(;;)
242 {
243 rU32 v,c;
244 v=*(rU32*)f; c=(~v)&0x80808080;
245 if ((v-0x26262626)&c) goto schk1;
246 if ((v-0x01010101)&c) goto schk2;
247 if (callback) if ((RR_SPRINTF_MIN-(int)(bf-buf))<4) goto schk1;
248 *(rU32*)bf=v; bf+=4; f+=4;
249 }
250 } scandd:
251
252 ++f;
253
254 // ok, we have a percent, read the modifiers first
255 fw = 0; pr = -1; fl = 0; tz = 0;
256
257 // flags
258 for(;;)
259 {
260 switch(f[0])
261 {
262 // if we have left just
263 case '-': fl|=LJ; ++f; continue;
264 // if we have leading plus
265 case '+': fl|=LP; ++f; continue;
266 // if we have leading space
267 case ' ': fl|=LS; ++f; continue;
268 // if we have leading 0x
269 case '#': fl|=LX; ++f; continue;
270 // if we have thousand commas
271 case '\'': fl|=CS; ++f; continue;
272 // if we have kilo marker
273 case '$': fl|=KI; ++f; continue;
274 // if we have leading zero
275 case '0': fl|=LZ; ++f; goto flags_done;
276 default: goto flags_done;
277 }
278 }
279 flags_done:
280
281 // get the field width
282 if ( f[0] == '*' ) {fw = va_arg(va,rU32); ++f;} else { while (( f[0] >= '0' ) && ( f[0] <= '9' )) { fw = fw * 10 + f[0] - '0'; f++; } }
283 // get the precision
284 if ( f[0]=='.' ) { ++f; if ( f[0] == '*' ) {pr = va_arg(va,rU32); ++f;} else { pr = 0; while (( f[0] >= '0' ) && ( f[0] <= '9' )) { pr = pr * 10 + f[0] - '0'; f++; } } }
285
286 // handle integer size overrides
287 switch(f[0])
288 {
289 // are we halfwidth?
290 case 'h': fl|=HW; ++f; break;
291 // are we 64-bit (unix style)
292 case 'l': ++f; if ( f[0]=='l') { fl|=BI; ++f; } break;
293 // are we 64-bit on intmax? (c99)
294 case 'j': fl|=BI; ++f; break;
295 // are we 64-bit on size_t or ptrdiff_t? (c99)
296 case 'z': case 't': fl|=((sizeof(char*)==8)?BI:0); ++f; break;
297 // are we 64-bit (msft style)
298 case 'I': if ( ( f[1]=='6') && ( f[2]=='4') ) { fl|=BI; f+=3; } else if ( ( f[1]=='3') && ( f[2]=='2') ) { f+=3; } else { fl|=((sizeof(void*)==8)?BI:0); ++f; } break;
299 default: break;
300 }
301
302 // handle each replacement
303 switch( f[0] )
304 {
305 #define NUMSZ 512 // big enough for e308 (with commas) or e-307
306 char num[NUMSZ];
307 char lead[8];
308 char tail[8];
309 char *s;
310 char const *h;
311 rU32 l,n,cs;
312 rU64 n64;
313 #ifndef RR_SPRINTF_NOFLOAT
314 double fv;
315 #endif
316 rS32 dp; char const * sn;
317
318 case 's':
319 // get the string
320 s = va_arg(va,char*); if (s==0) s = (char*)"null";
321 // get the length
322 sn = s;
323 for(;;)
324 {
325 if ((((rUINTa)sn)&3)==0) break;
326 lchk:
327 if (sn[0]==0) goto ld;
328 ++sn;
329 }
330 n = 0xffffffff;
331 if (pr>=0) { n=(rU32)(sn-s); if (n>=(rU32)pr) goto ld; n=((rU32)(pr-n))>>2; }
332 while(n)
333 {
334 rU32 v=*(rU32*)sn;
335 if ((v-0x01010101)&(~v)&0x80808080UL) goto lchk;
336 sn+=4;
337 --n;
338 }
339 goto lchk;
340 ld:
341
342 l = (rU32) ( sn - s );
343 // clamp to precision
344 if ( l > (rU32)pr ) l = pr;
345 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
346 // copy the string in
347 goto scopy;
348
349 case 'c': // char
350 // get the character
351 s = num + NUMSZ -1; *s = (char)va_arg(va,int);
352 l = 1;
353 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
354 goto scopy;
355
356 case 'n': // weird write-bytes specifier
357 { int * d = va_arg(va,int*);
358 *d = tlen + (int)( bf - buf ); }
359 break;
360
361 #ifdef RR_SPRINTF_NOFLOAT
362 case 'A': // float
363 case 'a': // hex float
364 case 'G': // float
365 case 'g': // float
366 case 'E': // float
367 case 'e': // float
368 case 'f': // float
369 va_arg(va,double); // eat it
370 s = (char*)"No float";
371 l = 8;
372 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
373 goto scopy;
374 #else
375 case 'A': // float
376 h=hexu;
377 goto hexfloat;
378
379 case 'a': // hex float
380 h=hex;
381 hexfloat:
382 fv = va_arg(va,double);
383 if (pr==-1) pr=6; // default is 6
384 // read the double into a string
385 if ( rrreal_to_parts( (rS64*)&n64, &dp, fv ) )
386 fl |= NG;
387
388 s = num+64;
389
390 // sign
391 lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
392
393 if (dp==-1023) dp=(n64)?-1022:0; else n64|=(((rU64)1)<<52);
394 n64<<=(64-56);
395 if (pr<15) n64+=((((rU64)8)<<56)>>(pr*4));
396 // add leading chars
397
398 #ifdef RR_SPRINTF_MSVC_MODE
399 *s++='0';*s++='x';
400 #else
401 lead[1+lead[0]]='0'; lead[2+lead[0]]='x'; lead[0]+=2;
402 #endif
403 *s++=h[(n64>>60)&15]; n64<<=4;
404 if ( pr ) *s++=RRperiod;
405 sn = s;
406
407 // print the bits
408 n = pr; if (n>13) n = 13; if (pr>(rS32)n) tz=pr-n; pr = 0;
409 while(n--) { *s++=h[(n64>>60)&15]; n64<<=4; }
410
411 // print the expo
412 tail[1]=h[17];
413 if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
414 n = (dp>=1000)?6:((dp>=100)?5:((dp>=10)?4:3));
415 tail[0]=(char)n;
416 for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
417
418 dp = (int)(s-sn);
419 l = (int)(s-(num+64));
420 s = num+64;
421 cs = 1 + (3<<24);
422 goto scopy;
423
424 case 'G': // float
425 h=hexu;
426 goto dosmallfloat;
427
428 case 'g': // float
429 h=hex;
430 dosmallfloat:
431 fv = va_arg(va,double);
432 if (pr==-1) pr=6; else if (pr==0) pr = 1; // default is 6
433 // read the double into a string
434 if ( rrreal_to_str( &sn, &l, num, &dp, fv, (pr-1)|0x80000000 ) )
435 fl |= NG;
436
437 // clamp the precision and delete extra zeros after clamp
438 n = pr;
439 if ( l > (rU32)pr ) l = pr; while ((l>1)&&(pr)&&(sn[l-1]=='0')) { --pr; --l; }
440
441 // should we use %e
442 if ((dp<=-4)||(dp>(rS32)n))
443 {
444 if ( pr > (rS32)l ) pr = l-1; else if ( pr ) --pr; // when using %e, there is one digit before the decimal
445 goto doexpfromg;
446 }
447 // this is the insane action to get the pr to match %g sematics for %f
448 if(dp>0) { pr=(dp<(rS32)l)?l-dp:0; } else { pr = -dp+((pr>(rS32)l)?l:pr); }
449 goto dofloatfromg;
450
451 case 'E': // float
452 h=hexu;
453 goto doexp;
454
455 case 'e': // float
456 h=hex;
457 doexp:
458 fv = va_arg(va,double);
459 if (pr==-1) pr=6; // default is 6
460 // read the double into a string
461 if ( rrreal_to_str( &sn, &l, num, &dp, fv, pr|0x80000000 ) )
462 fl |= NG;
463 doexpfromg:
464 tail[0]=0;
465 lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
466 if ( dp == RRSPECIAL ) { s=(char*)sn; cs=0; pr=0; goto scopy; }
467 s=num+64;
468 // handle leading chars
469 *s++=sn[0];
470
471 if (pr) *s++=RRperiod;
472
473 // handle after decimal
474 if ((l-1)>(rU32)pr) l=pr+1;
475 for(n=1;n<l;n++) *s++=sn[n];
476 // trailing zeros
477 tz = pr-(l-1); pr=0;
478 // dump expo
479 tail[1]=h[0xe];
480 dp -= 1;
481 if (dp<0) { tail[2]='-'; dp=-dp;} else tail[2]='+';
482 #ifdef RR_SPRINTF_MSVC_MODE
483 n = 5;
484 #else
485 n = (dp>=100)?5:4;
486 #endif
487 tail[0]=(char)n;
488 for(;;) { tail[n]='0'+dp%10; if (n<=3) break; --n; dp/=10; }
489 cs = 1 + (3<<24); // how many tens
490 goto flt_lead;
491
492 case 'f': // float
493 fv = va_arg(va,double);
494 doafloat:
495 // do kilos
496 if (fl&KI) {while(fl<0x4000000) { if ((fv<1024.0) && (fv>-1024.0)) break; fv/=1024.0; fl+=0x1000000; }}
497 if (pr==-1) pr=6; // default is 6
498 // read the double into a string
499 if ( rrreal_to_str( &sn, &l, num, &dp, fv, pr ) )
500 fl |= NG;
501 dofloatfromg:
502 tail[0]=0;
503 // sign
504 lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
505 if ( dp == RRSPECIAL ) { s=(char*)sn; cs=0; pr=0; goto scopy; }
506 s=num+64;
507
508 // handle the three decimal varieties
509 if (dp<=0)
510 {
511 rS32 i;
512 // handle 0.000*000xxxx
513 *s++='0'; if (pr) *s++=RRperiod;
514 n=-dp; if((rS32)n>pr) n=pr; i=n; while(i) { if ((((rUINTa)s)&3)==0) break; *s++='0'; --i; } while(i>=4) { *(rU32*)s=0x30303030; s+=4; i-=4; } while(i) { *s++='0'; --i; }
515 if ((rS32)(l+n)>pr) l=pr-n; i=l; while(i) { *s++=*sn++; --i; }
516 tz = pr-(n+l);
517 cs = 1 + (3<<24); // how many tens did we write (for commas below)
518 }
519 else
520 {
521 cs = (fl&CS)?((600-(rU32)dp)%3):0;
522 if ((rU32)dp>=l)
523 {
524 // handle xxxx000*000.0
525 n=0; for(;;) { if ((fl&CS) && (++cs==4)) { cs = 0; *s++=RRcomma; } else { *s++=sn[n]; ++n; if (n>=l) break; } }
526 if (n<(rU32)dp)
527 {
528 n = dp - n;
529 if ((fl&CS)==0) { while(n) { if ((((rUINTa)s)&3)==0) break; *s++='0'; --n; } while(n>=4) { *(rU32*)s=0x30303030; s+=4; n-=4; } }
530 while(n) { if ((fl&CS) && (++cs==4)) { cs = 0; *s++=RRcomma; } else { *s++='0'; --n; } }
531 }
532 cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
533 if (pr) { *s++=RRperiod; tz=pr;}
534 }
535 else
536 {
537 // handle xxxxx.xxxx000*000
538 n=0; for(;;) { if ((fl&CS) && (++cs==4)) { cs = 0; *s++=RRcomma; } else { *s++=sn[n]; ++n; if (n>=(rU32)dp) break; } }
539 cs = (int)(s-(num+64)) + (3<<24); // cs is how many tens
540 if (pr) *s++=RRperiod;
541 if ((l-dp)>(rU32)pr) l=pr+dp;
542 while(n<l) { *s++=sn[n]; ++n; }
543 tz = pr-(l-dp);
544 }
545 }
546 pr = 0;
547
548 // handle k,m,g,t
549 if (fl&KI) { tail[0]=1; tail[1]=' '; { if (fl>>24) { tail[2]="_kmgt"[fl>>24]; tail[0]=2; } } };
550
551 flt_lead:
552 // get the length that we copied
553 l = (rU32) ( s-(num+64) );
554 s=num+64;
555 goto scopy;
556 #endif
557
558 case 'B': // upper binary
559 h = hexu;
560 goto binary;
561
562 case 'b': // lower binary
563 h = hex;
564 binary:
565 lead[0]=0;
566 if (fl&LX) { lead[0]=2;lead[1]='0';lead[2]=h[0xb]; }
567 l=(8<<4)|(1<<8);
568 goto radixnum;
569
570 case 'o': // octal
571 h = hexu;
572 lead[0]=0;
573 if (fl&LX) { lead[0]=1;lead[1]='0'; }
574 l=(3<<4)|(3<<8);
575 goto radixnum;
576
577 case 'p': // pointer
578 fl |= (sizeof(void*)==8)?BI:0;
579 pr = sizeof(void*)*2;
580 fl &= ~LZ; // 'p' only prints the pointer with zeros
581 // drop through to X
582
583 case 'X': // upper binary
584 h = hexu;
585 goto dohexb;
586
587 case 'x': // lower binary
588 h = hex; dohexb:
589 l=(4<<4)|(4<<8);
590 lead[0]=0;
591 if (fl&LX) { lead[0]=2;lead[1]='0';lead[2]=h[16]; }
592 radixnum:
593 // get the number
594 if ( fl&BI )
595 n64 = va_arg(va,rU64);
596 else
597 n64 = va_arg(va,rU32);
598
599 s = num + NUMSZ; dp = 0;
600 // clear tail, and clear leading if value is zero
601 tail[0]=0; if (n64==0) { lead[0]=0; if (pr==0) { l=0; cs = ( ((l>>4)&15)) << 24; goto scopy; } }
602 // convert to string
603 for(;;) { *--s = h[n64&((1<<(l>>8))-1)]; n64>>=(l>>8); if ( ! ( (n64) || ((rS32) ( (num+NUMSZ) - s ) < pr ) ) ) break; if ( fl&CS) { ++l; if ((l&15)==((l>>4)&15)) { l&=~15; *--s=RRcomma; } } };
604 // get the tens and the comma pos
605 cs = (rU32) ( (num+NUMSZ) - s ) + ( ( ((l>>4)&15)) << 24 );
606 // get the length that we copied
607 l = (rU32) ( (num+NUMSZ) - s );
608 // copy it
609 goto scopy;
610
611 case 'u': // unsigned
612 case 'i':
613 case 'd': // integer
614 // get the integer and abs it
615 if ( fl&BI )
616 {
617 rS64 i64 = va_arg(va,rS64); n64 = (rU64)i64; if ((f[0]!='u') && (i64<0)) { n64=(rU64)-i64; fl|=NG; }
618 }
619 else
620 {
621 rS32 i = va_arg(va,rS32); n64 = (rU32)i; if ((f[0]!='u') && (i<0)) { n64=(rU32)-i; fl|=NG; }
622 }
623
624 #ifndef RR_SPRINTF_NOFLOAT
625 if (fl&KI) { if (n64<1024) pr=0; else if (pr==-1) pr=1; fv=(double)(rS64)n64; goto doafloat; }
626 #endif
627
628 // convert to string
629 s = num+NUMSZ; l=0;
630
631 for(;;)
632 {
633 // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
634 char * o=s-8;
635 if (n64>=100000000) { n = (rU32)( n64 % 100000000); n64 /= 100000000; } else {n = (rU32)n64; n64 = 0; }
636 if((fl&CS)==0) { while(n) { s-=2; *(rU16*)s=*(rU16*)&rrdiglookup[(n%100)*2]; n/=100; } }
637 while (n) { if ( ( fl&CS) && (l++==3) ) { l=0; *--s=RRcomma; --o; } else { *--s=(char)(n%10)+'0'; n/=10; } }
638 if (n64==0) { if ((s[0]=='0') && (s!=(num+NUMSZ))) ++s; break; }
639 while (s!=o) if ( ( fl&CS) && (l++==3) ) { l=0; *--s=RRcomma; --o; } else { *--s='0'; }
640 }
641
642 tail[0]=0;
643 // sign
644 lead[0]=0; if (fl&NG) { lead[0]=1; lead[1]='-'; } else if (fl&LS) { lead[0]=1; lead[1]=' '; } else if (fl&LP) { lead[0]=1; lead[1]='+'; };
645
646 // get the length that we copied
647 l = (rU32) ( (num+NUMSZ) - s ); if ( l == 0 ) { *--s='0'; l = 1; }
648 cs = l + (3<<24);
649 if (pr<0) pr = 0;
650
651 scopy:
652 // get fw=leading/trailing space, pr=leading zeros
653 if (pr<(rS32)l) pr = l;
654 n = pr + lead[0] + tail[0] + tz;
655 if (fw<(rS32)n) fw = n;
656 fw -= n;
657 pr -= l;
658
659 // handle right justify and leading zeros
660 if ( (fl&LJ)==0 )
661 {
662 if (fl&LZ) // if leading zeros, everything is in pr
663 {
664 pr = (fw>pr)?fw:pr;
665 fw = 0;
666 }
667 else
668 {
669 fl &= ~CS; // if no leading zeros, then no commas
670 }
671 }
672
673 // copy the spaces and/or zeros
674 if (fw+pr)
675 {
676 rS32 i; rU32 c;
677
678 // copy leading spaces (or when doing %8.4d stuff)
679 if ( (fl&LJ)==0 ) while(fw>0) { cb_buf_clamp(i,fw); fw -= i; while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++=' '; --i; } while(i>=4) { *(rU32*)bf=0x20202020; bf+=4; i-=4; } while (i) {*bf++=' '; --i;} chk_cb_buf(1); }
680
681 // copy leader
682 sn=lead+1; while(lead[0]) { cb_buf_clamp(i,lead[0]); lead[0] -= (char)i; while (i) {*bf++=*sn++; --i;} chk_cb_buf(1); }
683
684 // copy leading zeros
685 c = cs >> 24; cs &= 0xffffff;
686 cs = (fl&CS)?((rU32)(c-((pr+cs)%(c+1)))):0;
687 while(pr>0) { cb_buf_clamp(i,pr); pr -= i; if((fl&CS)==0) { while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++='0'; --i; } while(i>=4) { *(rU32*)bf=0x30303030; bf+=4; i-=4; } } while (i) { if((fl&CS) && (cs++==c)) { cs = 0; *bf++=RRcomma; } else *bf++='0'; --i; } chk_cb_buf(1); }
688 }
689
690 // copy leader if there is still one
691 sn=lead+1; while(lead[0]) { rS32 i; cb_buf_clamp(i,lead[0]); lead[0] -= (char)i; while (i) {*bf++=*sn++; --i;} chk_cb_buf(1); }
692
693 // copy the string
694 n = l; while (n) { rS32 i; cb_buf_clamp(i,n); n-=i; RR_UNALIGNED( while(i>=4) { *(rU32*)bf=*(rU32*)s; bf+=4; s+=4; i-=4; } ) while (i) {*bf++=*s++; --i;} chk_cb_buf(1); }
695
696 // copy trailing zeros
697 while(tz) { rS32 i; cb_buf_clamp(i,tz); tz -= i; while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++='0'; --i; } while(i>=4) { *(rU32*)bf=0x30303030; bf+=4; i-=4; } while (i) {*bf++='0'; --i;} chk_cb_buf(1); }
698
699 // copy tail if there is one
700 sn=tail+1; while(tail[0]) { rS32 i; cb_buf_clamp(i,tail[0]); tail[0] -= (char)i; while (i) {*bf++=*sn++; --i;} chk_cb_buf(1); }
701
702 // handle the left justify
703 if (fl&LJ) if (fw>0) { while (fw) { rS32 i; cb_buf_clamp(i,fw); fw-=i; while(i) { if ((((rUINTa)bf)&3)==0) break; *bf++=' '; --i; } while(i>=4) { *(rU32*)bf=0x20202020; bf+=4; i-=4; } while (i--) *bf++=' '; chk_cb_buf(1); } }
704 break;
705
706 default: // unknown, just copy code
707 s = num + NUMSZ -1; *s = f[0];
708 l = 1;
709 fw=pr=fl=0;
710 lead[0]=0; tail[0]=0; pr = 0; dp = 0; cs = 0;
711 goto scopy;
712 }
713 ++f;
714 }
715 endfmt:
716
717 if (!callback)
718 *bf = 0;
719 else
720 flush_cb();
721
722 done:
723 return tlen + (int)(bf-buf);
724 }
725
726 // cleanup
727 #undef LJ
728 #undef LP
729 #undef LS
730 #undef LX
731 #undef LZ
732 #undef BI
733 #undef CS
734 #undef NG
735 #undef KI
736 #undef NUMSZ
737 #undef chk_cb_bufL
738 #undef chk_cb_buf
739 #undef flush_cb
740 #undef cb_buf_clamp
741
742 // ============================================================================
743 // wrapper functions
744
745 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( sprintf )( char * buf, char const * fmt, ... )
746 {
747 va_list va;
748 va_start( va, fmt );
749 return RR_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
750 }
751
752 typedef struct RRCCS
753 {
754 char * buf;
755 int count;
756 char tmp[ RR_SPRINTF_MIN ];
757 } RRCCS;
758
759 static char * rrclampcallback( char * buf, void * user, int len )
760 {
761 RRCCS * c = (RRCCS*)user;
762
763 if ( len > c->count ) len = c->count;
764
765 if (len)
766 {
767 if ( buf != c->buf )
768 {
769 char * s, * d, * se;
770 d = c->buf; s = buf; se = buf+len;
771 do{ *d++ = *s++; } while (s<se);
772 }
773 c->buf += len;
774 c->count -= len;
775 }
776
777 if ( c->count <= 0 ) return 0;
778 return ( c->count >= RR_SPRINTF_MIN ) ? c->buf : c->tmp; // go direct into buffer if you can
779 }
780
781 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
782 {
783 RRCCS c;
784 int l;
785
786 if ( count == 0 )
787 return 0;
788
789 c.buf = buf;
790 c.count = count;
791
792 RR_SPRINTF_DECORATE( vsprintfcb )( rrclampcallback, &c, rrclampcallback(0,&c,0), fmt, va );
793
794 // zero-terminate
795 l = (int)( c.buf - buf );
796 if ( l >= count ) // should never be greater, only equal (or less) than count
797 l = count - 1;
798 buf[l] = 0;
799
800 return l;
801 }
802
803 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( snprintf )( char * buf, int count, char const * fmt, ... )
804 {
805 va_list va;
806 va_start( va, fmt );
807
808 return RR_SPRINTF_DECORATE( vsnprintf )( buf, count, fmt, va );
809 }
810
811 RRPUBLIC_DEF int RR_SPRINTF_DECORATE( vsprintf )( char * buf, char const * fmt, va_list va )
812 {
813 return RR_SPRINTF_DECORATE( vsprintfcb )( 0, 0, buf, fmt, va );
814 }
815
816 // =======================================================================
817 // low level float utility functions
818
819 #ifndef RR_SPRINTF_NOFLOAT
820
821 // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
822 #define RRCOPYFP(dest,src) { int cn; for(cn=0;cn<8;cn++) ((char*)&dest)[cn]=((char*)&src)[cn]; }
823
824 // get float info
825 static rS32 rrreal_to_parts( rS64 * bits, rS32 * expo, double value )
826 {
827 double d;
828 rS64 b = 0;
829
830 // load value and round at the frac_digits
831 d = value;
832
833 RRCOPYFP( b, d );
834
835 *bits = b & ((((rU64)1)<<52)-1);
836 *expo = ((b >> 52) & 2047)-1023;
837
838 return (rS32)(b >> 63);
839 }
840
841 static double const rrbot[23]={1e+000,1e+001,1e+002,1e+003,1e+004,1e+005,1e+006,1e+007,1e+008,1e+009,1e+010,1e+011,1e+012,1e+013,1e+014,1e+015,1e+016,1e+017,1e+018,1e+019,1e+020,1e+021,1e+022};
842 static double const rrnegbot[22]={1e-001,1e-002,1e-003,1e-004,1e-005,1e-006,1e-007,1e-008,1e-009,1e-010,1e-011,1e-012,1e-013,1e-014,1e-015,1e-016,1e-017,1e-018,1e-019,1e-020,1e-021,1e-022};
843 static double const rrnegboterr[22]={-5.551115123125783e-018,-2.0816681711721684e-019,-2.0816681711721686e-020,-4.7921736023859299e-021,-8.1803053914031305e-022,4.5251888174113741e-023,4.5251888174113739e-024,-2.0922560830128471e-025,-6.2281591457779853e-026,-3.6432197315497743e-027,6.0503030718060191e-028,2.0113352370744385e-029,-3.0373745563400371e-030,1.1806906454401013e-032,-7.7705399876661076e-032,2.0902213275965398e-033,-7.1542424054621921e-034,-7.1542424054621926e-035,2.4754073164739869e-036,5.4846728545790429e-037,9.2462547772103625e-038,-4.8596774326570872e-039};
844 static double const rrtop[13]={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};
845 static double const rrnegtop[13]={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};
846 static double const rrtoperr[13]={8388608,6.8601809640529717e+028,-7.253143638152921e+052,-4.3377296974619174e+075,-1.5559416129466825e+098,-3.2841562489204913e+121,-3.7745893248228135e+144,-1.7356668416969134e+167,-3.8893577551088374e+190,-9.9566444326005119e+213,6.3641293062232429e+236,-5.2069140800249813e+259,-5.2504760255204387e+282};
847 static double const rrnegtoperr[13]={3.9565301985100693e-040,-2.299904345391321e-063,3.6506201437945798e-086,1.1875228833981544e-109,-5.0644902316928607e-132,-6.7156837247865426e-155,-2.812077463003139e-178,-5.7778912386589953e-201,7.4997100559334532e-224,-4.6439668915134491e-247,-6.3691100762962136e-270,-9.436808465446358e-293,8.0970921678014997e-317};
848
849 #if defined(_MSC_VER) && (_MSC_VER<=1200)
850 static rU64 const rrpot[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000,100000000000, 1000000000000,10000000000000,100000000000000,1000000000000000, 10000000000000000,100000000000000000,1000000000000000000,10000000000000000000U };
851 #define rrtento19th ((rU64)1000000000000000000)
852 #else
853 static rU64 const rrpot[20]={1,10,100,1000, 10000,100000,1000000,10000000, 100000000,1000000000,10000000000ULL,100000000000ULL, 1000000000000ULL,10000000000000ULL,100000000000000ULL,1000000000000000ULL, 10000000000000000ULL,100000000000000000ULL,1000000000000000000ULL,10000000000000000000ULL };
854 #define rrtento19th (1000000000000000000ULL)
855 #endif
856
857 #define rrddmulthi(oh,ol,xh,yh) \
858 { \
859 double ahi=0,alo,bhi=0,blo; \
860 rS64 bt; \
861 oh = xh * yh; \
862 RRCOPYFP(bt,xh); bt&=((~(rU64)0)<<27); RRCOPYFP(ahi,bt); alo = xh-ahi; \
863 RRCOPYFP(bt,yh); bt&=((~(rU64)0)<<27); RRCOPYFP(bhi,bt); blo = yh-bhi; \
864 ol = ((ahi*bhi-oh)+ahi*blo+alo*bhi)+alo*blo; \
865 }
866
867 #define rrddtoS64(ob,xh,xl) \
868 { \
869 double ahi=0,alo,vh,t;\
870 ob = (rS64)ph;\
871 vh=(double)ob;\
872 ahi = ( xh - vh );\
873 t = ( ahi - xh );\
874 alo = (xh-(ahi-t))-(vh+t);\
875 ob += (rS64)(ahi+alo+xl);\
876 }
877
878
879 #define rrddrenorm(oh,ol) { double s; s=oh+ol; ol=ol-(s-oh); oh=s; }
880
881 #define rrddmultlo(oh,ol,xh,xl,yh,yl) \
882 ol = ol + ( xh*yl + xl*yh ); \
883
884 #define rrddmultlos(oh,ol,xh,yl) \
885 ol = ol + ( xh*yl ); \
886
887 static void rrraise_to_power10( double *ohi, double *olo, double d, rS32 power ) // power can be -323 to +350
888 {
889 double ph, pl;
890 if ((power>=0) && (power<=22))
891 {
892 rrddmulthi(ph,pl,d,rrbot[power]);
893 }
894 else
895 {
896 rS32 e,et,eb;
897 double p2h,p2l;
898
899 e=power; if (power<0) e=-e;
900 et = (e*0x2c9)>>14;/* %23 */ if (et>13) et=13; eb = e-(et*23);
901
902 ph = d; pl = 0.0;
903 if (power<0)
904 {
905 if (eb) { --eb; rrddmulthi(ph,pl,d,rrnegbot[eb]); rrddmultlos(ph,pl,d,rrnegboterr[eb]); }
906 if (et)
907 {
908 rrddrenorm(ph,pl);
909 --et; rrddmulthi(p2h,p2l,ph,rrnegtop[et]); rrddmultlo(p2h,p2l,ph,pl,rrnegtop[et],rrnegtoperr[et]); ph=p2h;pl=p2l;
910 }
911 }
912 else
913 {
914 if (eb)
915 {
916 e = eb; if (eb>22) eb=22; e -= eb;
917 rrddmulthi(ph,pl,d,rrbot[eb]);
918 if ( e ) { rrddrenorm(ph,pl); rrddmulthi(p2h,p2l,ph,rrbot[e]); rrddmultlos(p2h,p2l,rrbot[e],pl); ph=p2h;pl=p2l; }
919 }
920 if (et)
921 {
922 rrddrenorm(ph,pl);
923 --et; rrddmulthi(p2h,p2l,ph,rrtop[et]); rrddmultlo(p2h,p2l,ph,pl,rrtop[et],rrtoperr[et]); ph=p2h;pl=p2l;
924 }
925 }
926 }
927 rrddrenorm(ph,pl);
928 *ohi = ph; *olo = pl;
929 }
930
931 // given a float value, returns the significant bits in bits, and the position of the
932 // decimal point in decimal_pos. +/-INF and NAN are specified by special values
933 // returned in the decimal_pos parameter.
934 // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
935 static rS32 rrreal_to_str( char const * * start, rU32 * len, char *out, rS32 * decimal_pos, double value, rU32 frac_digits )
936 {
937 double d;
938 rS64 bits = 0;
939 rS32 expo, e, ng, tens;
940
941 d = value;
942 RRCOPYFP(bits,d);
943 expo = (bits >> 52) & 2047;
944 ng = (rS32)(bits >> 63);
945 if (ng) d=-d;
946
947 if ( expo == 2047 ) // is nan or inf?
948 {
949 *start = (bits&((((rU64)1)<<52)-1)) ? "NaN" : "Inf";
950 *decimal_pos = RRSPECIAL;
951 *len = 3;
952 return ng;
953 }
954
955 if ( expo == 0 ) // is zero or denormal
956 {
957 if ((bits<<1)==0) // do zero
958 {
959 *decimal_pos = 1;
960 *start = out;
961 out[0] = '0'; *len = 1;
962 return ng;
963 }
964 // find the right expo for denormals
965 {
966 rS64 v = ((rU64)1)<<51;
967 while ((bits&v)==0) { --expo; v >>= 1; }
968 }
969 }
970
971 // find the decimal exponent as well as the decimal bits of the value
972 {
973 double ph,pl;
974
975 // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
976 tens=expo-1023; tens = (tens<0)?((tens*617)/2048):(((tens*1233)/4096)+1);
977
978 // move the significant bits into position and stick them into an int
979 rrraise_to_power10( &ph, &pl, d, 18-tens );
980
981 // get full as much precision from double-double as possible
982 rrddtoS64( bits, ph,pl );
983
984 // check if we undershot
985 if ( ((rU64)bits) >= rrtento19th ) ++tens;
986 }
987
988 // now do the rounding in integer land
989 frac_digits = ( frac_digits & 0x80000000 ) ? ( (frac_digits&0x7ffffff) + 1 ) : ( tens + frac_digits );
990 if ( ( frac_digits < 24 ) )
991 {
992 rU32 dg = 1; if ((rU64)bits >= rrpot[9] ) dg=10; while( (rU64)bits >= rrpot[dg] ) { ++dg; if (dg==20) goto noround; }
993 if ( frac_digits < dg )
994 {
995 rU64 r;
996 // add 0.5 at the right position and round
997 e = dg - frac_digits;
998 if ( (rU32)e >= 24 ) goto noround;
999 r = rrpot[e];
1000 bits = bits + (r/2);
1001 if ( (rU64)bits >= rrpot[dg] ) ++tens;
1002 bits /= r;
1003 }
1004 noround:;
1005 }
1006
1007 // kill long trailing runs of zeros
1008 if ( bits )
1009 {
1010 rU32 n; for(;;) { if ( bits<=0xffffffff ) break; if (bits%1000) goto donez; bits/=1000; } n = (rU32)bits; while ((n%1000)==0) n/=1000; bits=n; donez:;
1011 }
1012
1013 // convert to string
1014 out += 64;
1015 e = 0;
1016 for(;;)
1017 {
1018 rU32 n;
1019 char * o = out-8;
1020 // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
1021 if (bits>=100000000) { n = (rU32)( bits % 100000000); bits /= 100000000; } else {n = (rU32)bits; bits = 0; }
1022 while(n) { out-=2; *(rU16*)out=*(rU16*)&rrdiglookup[(n%100)*2]; n/=100; e+=2; }
1023 if (bits==0) { if ((e) && (out[0]=='0')) { ++out; --e; } break; }
1024 while( out!=o ) { *--out ='0'; ++e; }
1025 }
1026
1027 *decimal_pos = tens;
1028 *start = out;
1029 *len = e;
1030 return ng;
1031 }
1032
1033 #undef rrddmulthi
1034 #undef rrddrenorm
1035 #undef rrddmultlo
1036 #undef rrddmultlos
1037 #undef RRSPECIAL
1038 #undef RRCOPYFP
1039
1040 #endif
1041
1042 // clean up
1043 #undef rU16
1044 #undef rU32
1045 #undef rS32
1046 #undef rU64
1047 #undef rS64
1048 #undef RRPUBLIC_DEC
1049 #undef RRPUBLIC_DEF
1050 #undef RR_SPRINTF_DECORATE
1051 #undef RR_UNALIGNED
1052
1053 #endif
1054
1055 #endif