Branch data Line data Source code
1 : : /*
2 : : * The MIT License (MIT)
3 : : *
4 : : * Copyright (c) 2016 Mikkel F. Jørgensen, dvide.com
5 : : *
6 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
7 : : * of this software and associated documentation files (the "Software"), to deal
8 : : * in the Software without restriction, including without limitation the rights
9 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 : : * copies of the Software, and to permit persons to whom the Software is
11 : : * furnished to do so, subject to the following conditions:
12 : : *
13 : : * The above copyright notice and this permission notice shall be included in all
14 : : * copies or substantial portions of the Software.
15 : : *
16 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 : : * SOFTWARE.
23 : : *
24 : : *
25 : : * Fast printing of (u)int8/16/32/64_t, (u)int, (u)long.
26 : : *
27 : : * Functions take for the
28 : : *
29 : : * int print_<type>(type value, char *buf);
30 : : *
31 : : * and returns number of characters printed, excluding trailing '\0'
32 : : * which is also printed. Prints at most 21 characters including zero-
33 : : * termination.
34 : : *
35 : : * The function `print_bool` is a bit different - it simply prints "true\0" for
36 : : * non-zero integers, and "false\0" otherwise.
37 : : *
38 : : * The general algorithm is in-place formatting using binary search log10
39 : : * followed by duff device loop unrolling div / 100 stages.
40 : : *
41 : : * The simpler post copy algorithm also provided for fmt_(u)int uses a
42 : : * temp buffer and loops over div/100 and post copy to target buffer.
43 : : *
44 : : *
45 : : * Benchmarks on core-i7, 2.2GHz, 64-bit clang/OS-X -O2:
46 : : *
47 : : * print_int64: avg 15ns for values between INT64_MIN + (10^7/2 .. 10^7/2)
48 : : * print_int64: avg 11ns for values between 10^9 + (0..10,000,000).
49 : : * print_int32: avg 7ns for values cast from INT64_MIN + (10^7/2 .. 10^7/2)
50 : : * print_int32: avg 7ns for values between 10^9 + (0..10,000,000).
51 : : * print_int64: avg 13ns for values between 10^16 + (0..10,000,000).
52 : : * print_int64: avg 5ns for values between 0 and 10,000,000.
53 : : * print_int32: avg 5ns for values between 0 and 10,000,000.
54 : : * print_int16: avg 10ns for values cast from 0 and 10,000,000.
55 : : * print_int8: avg 4ns for values cast from 0 and 10,000,000.
56 : : *
57 : : * Post copy algorithm:
58 : : * print_int: avg 12ns for values between INT64_MIN + (10^7/2 .. 10^7/2)
59 : : * print_int: avg 14ns for values between 10^9 + (0..10,000,000).
60 : : * print_long: avg 29ns for values between INT64_MIN + (10^7/2 .. 10^7/2)
61 : : *
62 : : * The post copy algorithm is nearly half as fast as the in-place
63 : : * algorithm, but can also be faster occasionally - possibly because the
64 : : * optimizer being able to skip the copy step.
65 : : */
66 : :
67 : : #ifndef PPRINTINT_H
68 : : #define PPRINTINT_H
69 : :
70 : : #ifndef UINT8_MAX
71 : : #include <stdint.h>
72 : : #endif
73 : :
74 : : #define PDIAGNOSTIC_IGNORE_UNUSED_FUNCTION
75 : : #include "pdiagnostic_push.h"
76 : :
77 : : static int print_bool(int n, char *p);
78 : :
79 : : static int print_uint8(uint8_t n, char *p);
80 : : static int print_uint16(uint16_t n, char *p);
81 : : static int print_uint32(uint32_t n, char *p);
82 : : static int print_uint64(uint64_t n, char *p);
83 : : static int print_int8(int8_t n, char *p);
84 : : static int print_int16(int16_t n, char *p);
85 : : static int print_int32(int32_t n, char *p);
86 : : static int print_int64(int64_t n, char *p);
87 : :
88 : : /*
89 : : * Uses slightly slower, but more compact alogrithm
90 : : * that is not hardcoded to implementation size.
91 : : * Other types may be defined using macros below.
92 : : */
93 : : static int print_ulong(unsigned long n, char *p);
94 : : static int print_uint(unsigned int n, char *p);
95 : : static int print_int(int n, char *p);
96 : : static int print_long(long n, char *p);
97 : :
98 : :
99 : : #if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
100 : : #define __print_unaligned_copy_16(p, q) (*(uint16_t*)(p) = *(uint16_t*)(q))
101 : : #else
102 : : #define __print_unaligned_copy_16(p, q) \
103 : : ((((uint8_t*)(p))[0] = ((uint8_t*)(q))[0]), \
104 : : (((uint8_t*)(p))[1] = ((uint8_t*)(q))[1]))
105 : : #endif
106 : :
107 : : static const char __print_digit_pairs[] =
108 : : "0001020304050607080910111213141516171819"
109 : : "2021222324252627282930313233343536373839"
110 : : "4041424344454647484950515253545556575859"
111 : : "6061626364656667686970717273747576777879"
112 : : "8081828384858687888990919293949596979899";
113 : :
114 : : #define __print_stage() \
115 : : p -= 2; \
116 : : dp = __print_digit_pairs + (n % 100) * 2; \
117 : : n /= 100; \
118 : : __print_unaligned_copy_16(p, dp);
119 : :
120 : : #define __print_long_stage() \
121 : : __print_stage() \
122 : : __print_stage()
123 : :
124 : : #define __print_short_stage() \
125 : : *--p = (n % 10) + '0'; \
126 : : n /= 10;
127 : :
128 : 1 : static int print_bool(int n, char *buf)
129 : : {
130 [ + - ]: 1 : if (n) {
131 : 1 : memcpy(buf, "true\0", 5);
132 : 1 : return 4;
133 : : } else {
134 : 0 : memcpy(buf, "false\0", 6);
135 : 0 : return 5;
136 : : }
137 : : }
138 : :
139 : 13 : static int print_uint8(uint8_t n, char *p)
140 : : {
141 : : const char *dp;
142 : :
143 [ - + ]: 13 : if (n >= 100) {
144 : : p += 3;
145 : 0 : *p = '\0';
146 : 0 : __print_stage();
147 : 0 : p[-1] = n + '0';
148 : 0 : return 3;
149 : : }
150 [ + + ]: 13 : if (n >= 10) {
151 : : p += 2;
152 : 5 : *p = '\0';
153 : 5 : __print_stage();
154 : 5 : return 2;
155 : : }
156 : 8 : p[1] = '\0';
157 : 8 : p[0] = n + '0';
158 : 8 : return 1;
159 : : }
160 : :
161 : 12 : static int print_uint16(uint16_t n, char *p)
162 : : {
163 : : int k = 0;
164 : : const char *dp;
165 : :
166 [ - + ]: 12 : if (n >= 1000) {
167 [ # # ]: 0 : if(n >= 10000) {
168 : : k = 5;
169 : : } else {
170 : : k = 4;
171 : : }
172 : : } else {
173 [ + + ]: 12 : if(n >= 100) {
174 : : k = 3;
175 [ + + ]: 10 : } else if(n >= 10) {
176 : : k = 2;
177 : : } else {
178 : : k = 1;
179 : : }
180 : : }
181 : 12 : p += k;
182 : 12 : *p = '\0';
183 [ + + ]: 12 : if (k & 1) {
184 [ - + + - ]: 5 : switch (k) {
185 : : case 5:
186 : 0 : __print_stage();
187 : : case 3:
188 : 2 : __print_stage();
189 : : case 1:
190 : 5 : p[-1] = n + '0';
191 : : }
192 : : } else {
193 [ - + - ]: 7 : switch (k) {
194 : : case 4:
195 : 0 : __print_stage();
196 : : case 2:
197 : 7 : __print_stage();
198 : : }
199 : : }
200 : 12 : return k;
201 : : }
202 : :
203 : 13 : static int print_uint32(uint32_t n, char *p)
204 : : {
205 : : int k = 0;
206 : : const char *dp;
207 : :
208 [ + + ]: 13 : if(n >= 10000UL) {
209 [ + - ]: 4 : if(n >= 10000000UL) {
210 [ + + ]: 4 : if(n >= 1000000000UL) {
211 : : k = 10;
212 [ - + ]: 1 : } else if(n >= 100000000UL) {
213 : : k = 9;
214 : : } else {
215 : : k = 8;
216 : : }
217 : : } else {
218 [ # # ]: 0 : if(n >= 1000000UL) {
219 : : k = 7;
220 [ # # ]: 0 : } else if(n >= 100000UL) {
221 : : k = 6;
222 : : } else {
223 : : k = 5;
224 : : }
225 : : }
226 : : } else {
227 [ - + ]: 9 : if(n >= 100UL) {
228 [ # # ]: 0 : if(n >= 1000UL) {
229 : : k = 4;
230 : : } else {
231 : : k = 3;
232 : : }
233 : : } else {
234 [ + + ]: 9 : if(n >= 10UL) {
235 : : k = 2;
236 : : } else {
237 : : k = 1UL;
238 : : }
239 : : }
240 : : }
241 : 13 : p += k;
242 : 13 : *p = '\0';
243 [ + + ]: 13 : if (k & 1) {
244 [ + - - - : 9 : switch (k) {
+ - ]
245 : : case 9:
246 : 1 : __print_stage();
247 : : case 7:
248 : 1 : __print_stage();
249 : : case 5:
250 : 1 : __print_stage();
251 : : case 3:
252 : 1 : __print_stage();
253 : : case 1:
254 : 9 : p[-1] = n + '0';
255 : : }
256 : : } else {
257 [ + - - - : 4 : switch (k) {
+ - ]
258 : : case 10:
259 : 3 : __print_stage();
260 : : case 8:
261 : 3 : __print_stage();
262 : : case 6:
263 : 3 : __print_stage();
264 : : case 4:
265 : 3 : __print_stage();
266 : : case 2:
267 : 4 : __print_stage();
268 : : }
269 : : }
270 : 13 : return k;
271 : : }
272 : :
273 : 8 : static int print_uint64(uint64_t n, char *p)
274 : : {
275 : : int k = 0;
276 : : const char *dp;
277 : : const uint64_t x = 1000000000ULL;
278 : :
279 [ + + ]: 8 : if (n < x) {
280 : 4 : return print_uint32((uint32_t)n, p);
281 : : }
282 [ + - ]: 4 : if(n >= 10000ULL * x) {
283 [ + - ]: 4 : if(n >= 10000000ULL * x) {
284 [ + - ]: 4 : if(n >= 1000000000ULL * x) {
285 [ + - ]: 4 : if (n >= 10000000000ULL * x) {
286 : : k = 11 + 9;
287 : : } else {
288 : : k = 10 + 9;
289 : : }
290 [ # # ]: 0 : } else if(n >= 100000000ULL * x) {
291 : : k = 9 + 9;
292 : : } else {
293 : : k = 8 + 9;
294 : : }
295 : : } else {
296 [ # # ]: 0 : if(n >= 1000000ULL * x) {
297 : : k = 7 + 9;
298 [ # # ]: 0 : } else if(n >= 100000ULL * x) {
299 : : k = 6 + 9;
300 : : } else {
301 : : k = 5 + 9;
302 : : }
303 : : }
304 : : } else {
305 [ # # ]: 0 : if(n >= 100ULL * x) {
306 [ # # ]: 0 : if(n >= 1000ULL * x) {
307 : : k = 4 + 9;
308 : : } else {
309 : : k = 3 + 9;
310 : : }
311 : : } else {
312 [ # # ]: 0 : if(n >= 10ULL * x) {
313 : : k = 2 + 9;
314 : : } else {
315 : : k = 1 + 9;
316 : : }
317 : : }
318 : : }
319 : 4 : p += k;
320 : 4 : *p = '\0';
321 [ + - ]: 4 : if (k & 1) {
322 [ + - - - : 4 : switch (k) {
- - ]
323 : : case 19:
324 : 4 : __print_stage();
325 : : case 17:
326 : 4 : __print_stage();
327 : : case 15:
328 : 4 : __print_stage();
329 : : case 13:
330 : 4 : __print_stage();
331 : : case 11:
332 : 4 : __print_stage()
333 : 4 : __print_short_stage();
334 : : }
335 : : } else {
336 [ # # # # : 0 : switch (k) {
# # # ]
337 : : case 20:
338 : 0 : __print_stage();
339 : : case 18:
340 : 0 : __print_stage();
341 : : case 16:
342 : 0 : __print_stage();
343 : : case 14:
344 : 0 : __print_stage();
345 : : case 12:
346 : 0 : __print_stage();
347 : : case 10:
348 : 0 : __print_stage();
349 : : }
350 : : }
351 : 4 : __print_long_stage()
352 : 4 : __print_long_stage()
353 : 4 : return k;
354 : : }
355 : :
356 : : static int print_int8(int8_t n, char *p)
357 : : {
358 : : int sign;
359 : :
360 [ # # ][ # # ]: 8 : if ((sign = n < 0)) {
[ # # - + ]
[ - + # # ]
[ - + ]
361 : 0 : *p++ = '-';
362 : 0 : n = -n;
363 : : }
364 : 8 : return print_uint8(n, p) + sign;
365 : : }
366 : :
367 : : static int print_int16(int16_t n, char *p)
368 : : {
369 : : int sign;
370 : :
371 [ # # ][ # # ]: 12 : if ((sign = n < 0)) {
[ # # - + ]
[ # # - + ]
[ # # ]
372 : 0 : *p++ = '-';
373 : 0 : n = -n;
374 : : }
375 : 12 : return print_uint16(n, p) + sign;
376 : : }
377 : :
378 : : static int print_int32(int32_t n, char *p)
379 : : {
380 : : int sign;
381 : :
382 [ # # ][ # # ]: 4 : if ((sign = n < 0)) {
[ # # # # ]
[ # # + + ]
[ # # ]
383 : 2 : *p++ = '-';
384 : 2 : n = -n;
385 : : }
386 : 4 : return print_uint32(n, p) + sign;
387 : : }
388 : :
389 : : static int print_int64(int64_t n, char *p)
390 : : {
391 : : int sign;
392 : :
393 [ # # ][ # # ]: 4 : if ((sign = n < 0)) {
[ # # # # ]
[ # # - + ]
[ # # ]
394 : 0 : *p++ = '-';
395 : 0 : n = -n;
396 : : }
397 : 4 : return print_uint64(n, p) + sign;
398 : : }
399 : :
400 : : #define __define_print_int_simple(NAME, UNAME, T, UT) \
401 : : static int UNAME(UT n, char *buf) \
402 : : { \
403 : : char tmp[20]; \
404 : : char* p = tmp + 20; \
405 : : char* q = p; \
406 : : unsigned int k, m; \
407 : : \
408 : : while (n >= 100) { \
409 : : p -= 2; \
410 : : m = (unsigned int)(n % 100) * 2; \
411 : : n /= 100; \
412 : : __print_unaligned_copy_16(p, __print_digit_pairs + m); \
413 : : } \
414 : : p -= 2; \
415 : : m = (unsigned int)n * 2; \
416 : : __print_unaligned_copy_16(p, __print_digit_pairs + m); \
417 : : if (n < 10) { \
418 : : ++p; \
419 : : } \
420 : : k = (unsigned int)(q - p); \
421 : : while (p != q) { \
422 : : *buf++ = *p++; \
423 : : } \
424 : : *buf = '\0'; \
425 : : return k; \
426 : : } \
427 : : \
428 : : static int NAME(T n, char *buf) \
429 : : { \
430 : : int sign = n < 0; \
431 : : \
432 : : if (sign) { \
433 : : *buf++ = '-'; \
434 : : n = -n; \
435 : : } \
436 : : return UNAME((UT)n, buf) + sign; \
437 : : }
438 : :
439 : : __define_print_int_simple(print_int, print_uint, int, unsigned int)
440 : : __define_print_int_simple(print_long, print_ulong, long, unsigned long)
441 : :
442 : : #ifdef PPRINTINT_BENCH
443 : : int main() {
444 : : int64_t count = 10000000; // 10^7
445 : : //int64_t base = 0;
446 : : //int64_t base = 10000000000000000; // 10^16
447 : : //int64_t base = 1000000000; // 10^9
448 : : int64_t base = INT64_MIN - count/2;
449 : : char buf[100];
450 : : int i, k = 0, n = 0;
451 : : for (i = 0; i < count; i++) {
452 : : k = print_int64(i + base, buf);
453 : : n += buf[0] + buf[k - 1];
454 : : }
455 : : return n;
456 : : }
457 : : /* Call with time on executable, multiply time in seconds by 100 to get time unit in ns/number. */
458 : : #endif /* PPRINTINT_BENCH */
459 : :
460 : : #ifdef PPRINTINT_TEST
461 : :
462 : : #include <stdio.h>
463 : : #include <string.h>
464 : :
465 : : int main()
466 : : {
467 : : char buf[21];
468 : : int failed = 0;
469 : : int k;
470 : :
471 : : k = print_uint64(UINT64_MAX, buf);
472 : : if (strlen(buf) != k) printf("length error\n");
473 : : if (strcmp("18446744073709551615", buf)) {
474 : : printf("UINT64_MAX didn't print correctly, got:\n'%s'\n", buf);
475 : : ++failed;
476 : : }
477 : : k = print_int64(INT64_MAX, buf);
478 : : if (strlen(buf) != k) printf("length error\n");
479 : : if (strcmp("9223372036854775807", buf)) {
480 : : printf("INT64_MAX didn't print correctly, got:\n'%s'\n", buf);
481 : : ++failed;
482 : : }
483 : : k = print_int64(INT64_MIN, buf);
484 : : if (strlen(buf) != k) printf("length error\n");
485 : : if (strcmp("-9223372036854775808", buf)) {
486 : : printf("INT64_MIN didn't print correctly, got:\n'%s'\n", buf);
487 : : ++failed;
488 : : }
489 : : k = print_uint32(UINT32_MAX, buf);
490 : : if (strlen(buf) != k) printf("length error\n");
491 : : if (strcmp("4294967295", buf)) {
492 : : printf("UINT32_MAX didn't print correctly, got:\n'%s'\n", buf);
493 : : ++failed;
494 : : }
495 : : k = print_int32(INT32_MAX, buf);
496 : : if (strlen(buf) != k) printf("length error\n");
497 : : if (strcmp("2147483647", buf)) {
498 : : printf("INT32_MAX didn't print correctly, got:\n'%s'\n", buf);
499 : : ++failed;
500 : : }
501 : : k = print_int32(INT32_MIN, buf);
502 : : if (strlen(buf) != k) printf("length error\n");
503 : : if (strcmp("-2147483648", buf)) {
504 : : printf("INT32_MIN didn't print correctly, got:\n'%s'\n", buf);
505 : : ++failed;
506 : : }
507 : : k = print_uint16(UINT16_MAX, buf);
508 : : if (strlen(buf) != k) printf("length error\n");
509 : : if (strcmp("65535", buf)) {
510 : : printf("UINT16_MAX didn't print correctly, got:\n'%s'\n", buf);
511 : : ++failed;
512 : : }
513 : : k = print_int16(INT16_MAX, buf);
514 : : if (strlen(buf) != k) printf("length error\n");
515 : : if (strcmp("32767", buf)) {
516 : : printf("INT16_MAX didn't print correctly, got:\n'%s'\n", buf);
517 : : ++failed;
518 : : }
519 : : k = print_int16(INT16_MIN, buf);
520 : : if (strlen(buf) != k) printf("length error\n");
521 : : if (strcmp("-32768", buf)) {
522 : : printf("INT16_MIN didn't print correctly, got:\n'%s'\n", buf);
523 : : ++failed;
524 : : }
525 : : k = print_uint8(UINT8_MAX, buf);
526 : : if (strlen(buf) != k) printf("length error\n");
527 : : if (strcmp("255", buf)) {
528 : : printf("INT8_MAX didn't print correctly, got:\n'%s'\n", buf);
529 : : ++failed;
530 : : }
531 : : k = print_int8(INT8_MAX, buf);
532 : : if (strlen(buf) != k) printf("length error\n");
533 : : if (strcmp("127", buf)) {
534 : : printf("INT8_MAX didn't print correctly, got:\n'%s'\n", buf);
535 : : ++failed;
536 : : }
537 : : k = print_int8(INT8_MIN, buf);
538 : : if (strlen(buf) != k) printf("length error\n");
539 : : if (strcmp("-128", buf)) {
540 : : printf("INT8_MIN didn't print correctly, got:\n'%s'\n", buf);
541 : : ++failed;
542 : : }
543 : : k = print_int(INT32_MAX, buf);
544 : : if (strlen(buf) != k) printf("length error\n");
545 : : if (strcmp("2147483647", buf)) {
546 : : printf("INT32_MAX didn't print correctly with k = print_int, got:\n'%s'\n", buf);
547 : : ++failed;
548 : : }
549 : : k = print_int(INT32_MIN, buf);
550 : : if (strlen(buf) != k) printf("length error\n");
551 : : if (strcmp("-2147483648", buf)) {
552 : : printf("INT32_MIN didn't print correctly k = print_int, got:\n'%s'\n", buf);
553 : : ++failed;
554 : : }
555 : : k = print_long(INT32_MAX, buf);
556 : : if (strlen(buf) != k) printf("length error\n");
557 : : if (strcmp("2147483647", buf)) {
558 : : printf("INT32_MAX didn't print correctly with fmt_long, got:\n'%s'\n", buf);
559 : : ++failed;
560 : : }
561 : : k = print_long(INT32_MIN, buf);
562 : : if (strlen(buf) != k) printf("length error\n");
563 : : if (strcmp("-2147483648", buf)) {
564 : : printf("INT32_MIN didn't print correctly fmt_long, got:\n'%s'\n", buf);
565 : : ++failed;
566 : : }
567 : : k = print_bool(1, buf);
568 : : if (strlen(buf) != k) printf("length error\n");
569 : : if (strcmp("true", buf) {
570 : : printf("1 didn't print 'true' as expected, got:\n'%s'\n", buf);
571 : : ++failed;
572 : : }
573 : : k = print_bool(-1, buf);
574 : : if (strlen(buf) != k) printf("length error\n");
575 : : if (strcmp("true", buf) {
576 : : printf("-1 didn't print 'true' as expected, got:\n'%s'\n", buf);
577 : : ++failed;
578 : : }
579 : : k = print_bool(, buf);
580 : : if (strlen(buf) != k) printf("length error\n");
581 : : if (strcmp("false", buf) {
582 : : printf("0 didn't print 'false' as expected, got:\n'%s'\n", buf);
583 : : ++failed;
584 : : }
585 : : if (failed) {
586 : : printf("FAILED\n");
587 : : return -1;
588 : : }
589 : : printf("SUCCESS\n");
590 : : return 0;
591 : : }
592 : : #endif /* PPRINTINT_TEST */
593 : :
594 : : #include "pdiagnostic_pop.h"
595 : :
596 : : #endif /* PPRINTINT_H */
|