vppinfra: Add missing %o
[vpp.git] / src / vppinfra / format.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*------------------------------------------------------------------
16  * format.c -- see notice below
17  *
18  * October 2003, Eliot Dresselhaus
19  *
20  * Modifications to this file Copyright (c) 2003 by cisco Systems, Inc.
21  * All rights reserved.
22  *------------------------------------------------------------------
23  */
24
25 /*
26   Copyright (c) 2001, 2002, 2003, 2006 Eliot Dresselhaus
27
28   Permission is hereby granted, free of charge, to any person obtaining
29   a copy of this software and associated documentation files (the
30   "Software"), to deal in the Software without restriction, including
31   without limitation the rights to use, copy, modify, merge, publish,
32   distribute, sublicense, and/or sell copies of the Software, and to
33   permit persons to whom the Software is furnished to do so, subject to
34   the following conditions:
35
36   The above copyright notice and this permission notice shall be
37   included in all copies or substantial portions of the Software.
38
39   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46 */
47
48 #include <stdarg.h>             /* va_start, etc */
49
50 #ifdef CLIB_UNIX
51 #include <unistd.h>
52 #include <stdio.h>
53 #endif
54
55 #ifdef CLIB_STANDALONE
56 #include <vppinfra/standalone_stdio.h>
57 #endif
58
59 #include <vppinfra/mem.h>
60 #include <vppinfra/format.h>
61 #include <vppinfra/vec.h>
62 #include <vppinfra/error.h>
63 #include <vppinfra/string.h>
64 #include <vppinfra/os.h>        /* os_puts */
65 #include <vppinfra/math.h>
66
67 typedef struct
68 {
69   /* Output number in this base. */
70   u8 base;
71
72   /* Number of show of 64 bit number. */
73   u8 n_bits;
74
75   /* Signed or unsigned. */
76   u8 is_signed;
77
78   /* Output digits uppercase (not lowercase) %X versus %x. */
79   u8 uppercase_digits;
80 } format_integer_options_t;
81
82 static u8 *format_integer (u8 * s, u64 number,
83                            format_integer_options_t * options);
84 static u8 *format_float (u8 * s, f64 x, uword n_digits_to_print,
85                          uword output_style);
86
87 typedef struct
88 {
89   /* String justification: + => right, - => left, = => center. */
90   uword justify;
91
92   /* Width of string (before and after decimal point for numbers).
93      0 => natural width. */
94   uword width[2];
95
96   /* Long => 'l', long long 'L', int 0. */
97   uword how_long;
98
99   /* Pad character.  Defaults to space. */
100   uword pad_char;
101 } format_info_t;
102
103 static u8 *
104 justify (u8 * s, format_info_t * fi, uword s_len_orig)
105 {
106   uword i0, l0, l1;
107
108   i0 = s_len_orig;
109   l0 = i0 + fi->width[0];
110   l1 = vec_len (s);
111
112   /* If width is zero user returned width. */
113   if (l0 == i0)
114     l0 = l1;
115
116   if (l1 > l0)
117     _vec_len (s) = l0;
118   else if (l0 > l1)
119     {
120       uword n = l0 - l1;
121       uword n_left = 0, n_right = 0;
122
123       switch (fi->justify)
124         {
125         case '-':
126           n_right = n;
127           break;
128
129         case '+':
130           n_left = n;
131           break;
132
133         case '=':
134           n_right = n_left = n / 2;
135           if (n % 2)
136             n_left++;
137           break;
138         }
139       if (n_left > 0)
140         {
141           vec_insert (s, n_left, i0);
142           clib_memset (s + i0, fi->pad_char, n_left);
143           l1 = vec_len (s);
144         }
145       if (n_right > 0)
146         {
147           vec_resize (s, n_right);
148           clib_memset (s + l1, fi->pad_char, n_right);
149         }
150     }
151   return s;
152 }
153
154 static const u8 *
155 do_percent (u8 ** _s, const u8 * fmt, va_list * va)
156 {
157   u8 *s = *_s;
158   uword c;
159
160   const u8 *f = fmt;
161
162   format_info_t fi = {
163     .justify = '+',
164     .width = {0},
165     .pad_char = ' ',
166     .how_long = 0,
167   };
168
169   uword i;
170
171   ASSERT (f[0] == '%');
172
173   switch (c = *++f)
174     {
175     case '%':
176       /* %% => % */
177       vec_add1 (s, c);
178       f++;
179       goto done;
180
181     case '-':
182     case '+':
183     case '=':
184       fi.justify = c;
185       c = *++f;
186       break;
187     }
188
189   /* Parse width0 . width1. */
190   {
191     uword is_first_digit = 1;
192
193     fi.width[0] = fi.width[1] = 0;
194     for (i = 0; i < 2; i++)
195       {
196         if (c == '0' && i == 0 && is_first_digit)
197           fi.pad_char = '0';
198         is_first_digit = 0;
199         if (c == '*')
200           {
201             fi.width[i] = va_arg (*va, int);
202             c = *++f;
203           }
204         else
205           {
206             while (c >= '0' && c <= '9')
207               {
208                 fi.width[i] = 10 * fi.width[i] + (c - '0');
209                 c = *++f;
210               }
211           }
212         if (c != '.')
213           break;
214         c = *++f;
215       }
216   }
217
218   /* Parse %l* and %L* */
219   switch (c)
220     {
221     case 'w':
222       /* word format. */
223       fi.how_long = 'w';
224       c = *++f;
225       break;
226
227     case 'L':
228     case 'l':
229       fi.how_long = c;
230       c = *++f;
231       if (c == 'l' && *f == 'l')
232         {
233           fi.how_long = 'L';
234           c = *++f;
235         }
236       break;
237     }
238
239   /* Finally we are ready for format letter. */
240   if (c != 0)
241     {
242       uword s_initial_len = vec_len (s);
243       format_integer_options_t o = {
244         .is_signed = 0,
245         .base = 10,
246         .n_bits = BITS (uword),
247         .uppercase_digits = 0,
248       };
249
250       f++;
251
252       switch (c)
253         {
254         default:
255           {
256             /* Try to give a helpful error message. */
257             vec_free (s);
258             s = format (s, "**** CLIB unknown format `%%%c' ****", c);
259             goto done;
260           }
261
262         case 'c':
263           vec_add1 (s, va_arg (*va, int));
264           break;
265
266         case 'p':
267           vec_add1 (s, '0');
268           vec_add1 (s, 'x');
269
270           o.is_signed = 0;
271           o.n_bits = BITS (uword *);
272           o.base = 16;
273           o.uppercase_digits = 0;
274
275           s = format_integer (s, pointer_to_uword (va_arg (*va, void *)), &o);
276           break;
277
278         case 'x':
279         case 'X':
280         case 'u':
281         case 'o':
282         case 'd':
283           {
284             u64 number;
285
286             o.base = 10;
287             if (c == 'x' || c == 'X')
288               o.base = 16;
289             o.is_signed = c == 'd';
290             o.uppercase_digits = c == 'X';
291
292             switch (fi.how_long)
293               {
294               case 'L':
295                 number = va_arg (*va, unsigned long long);
296                 o.n_bits = BITS (unsigned long long);
297                 break;
298
299               case 'l':
300                 number = va_arg (*va, long);
301                 o.n_bits = BITS (long);
302                 break;
303
304               case 'w':
305                 number = va_arg (*va, word);
306                 o.n_bits = BITS (uword);
307                 break;
308
309               default:
310                 number = va_arg (*va, int);
311                 o.n_bits = BITS (int);
312                 break;
313               }
314
315             if (c == 'o')
316               o.base = 8;
317
318             s = format_integer (s, number, &o);
319           }
320           break;
321
322         case 's':
323         case 'S':
324           {
325             char *cstring = va_arg (*va, char *);
326             uword len;
327
328             if (!cstring)
329               {
330                 cstring = "(nil)";
331                 len = 5;
332               }
333             else if (fi.width[1] != 0)
334               len = clib_min (strlen (cstring), fi.width[1]);
335             else
336               len = strlen (cstring);
337
338             /* %S => format string as C identifier (replace _ with space). */
339             if (c == 'S')
340               {
341                 for (i = 0; i < len; i++)
342                   vec_add1 (s, cstring[i] == '_' ? ' ' : cstring[i]);
343               }
344             else
345               vec_add (s, cstring, len);
346           }
347           break;
348
349         case 'v':
350           {
351             u8 *v = va_arg (*va, u8 *);
352             uword len;
353
354             if (fi.width[1] != 0)
355               len = clib_min (vec_len (v), fi.width[1]);
356             else
357               len = vec_len (v);
358
359             vec_add (s, v, len);
360           }
361           break;
362
363         case 'f':
364         case 'g':
365         case 'e':
366           /* Floating point. */
367           ASSERT (fi.how_long == 0 || fi.how_long == 'l');
368           s = format_float (s, va_arg (*va, double), fi.width[1], c);
369           break;
370
371         case 'U':
372           /* User defined function. */
373           {
374             typedef u8 *(user_func_t) (u8 * s, va_list * args);
375             user_func_t *u = va_arg (*va, user_func_t *);
376
377             s = (*u) (s, va);
378           }
379           break;
380         }
381
382       s = justify (s, &fi, s_initial_len);
383     }
384
385 done:
386   *_s = s;
387   return f;
388 }
389
390 __clib_export u8 *
391 va_format (u8 * s, const char *fmt, va_list * va)
392 {
393   const u8 *f = (u8 *) fmt, *g;
394   u8 c;
395
396   g = f;
397   while (1)
398     {
399       c = *f;
400
401       if (!c)
402         break;
403
404       if (c == '%')
405         {
406           if (f > g)
407             vec_add (s, g, f - g);
408           f = g = do_percent (&s, f, va);
409         }
410       else
411         {
412           f++;
413         }
414     }
415
416   if (f > g)
417     vec_add (s, g, f - g);
418
419 #ifdef __COVERITY__
420   if (s == 0)
421     return (u8 *) "liar liar pants on fire s can't be zero!";
422 #endif
423
424   return s;
425 }
426
427 __clib_export u8 *
428 format (u8 * s, const char *fmt, ...)
429 {
430   va_list va;
431   va_start (va, fmt);
432   s = va_format (s, fmt, &va);
433   va_end (va);
434 #ifdef __COVERITY__
435   if (s == 0)
436     return (u8 *) "liar liar pants on fire s can't be zero!";
437 #endif
438   return s;
439 }
440
441 __clib_export word
442 va_fformat (FILE * f, char *fmt, va_list * va)
443 {
444   word ret;
445   u8 *s;
446
447   s = va_format (0, fmt, va);
448
449 #ifdef CLIB_UNIX
450   if (f)
451     {
452       ret = fwrite (s, vec_len (s), 1, f);
453     }
454   else
455 #endif /* CLIB_UNIX */
456     {
457       ret = 0;
458       os_puts (s, vec_len (s), /* is_error */ 0);
459     }
460
461   vec_free (s);
462   return ret;
463 }
464
465 __clib_export word
466 fformat (FILE * f, char *fmt, ...)
467 {
468   va_list va;
469   word ret;
470
471   va_start (va, fmt);
472   ret = va_fformat (f, fmt, &va);
473   va_end (va);
474
475   return (ret);
476 }
477
478 #ifdef CLIB_UNIX
479 __clib_export void
480 fformat_append_cr (FILE * ofp, const char *fmt, ...)
481 {
482   va_list va;
483
484   va_start (va, fmt);
485   (void) va_fformat (ofp, (char *) fmt, &va);
486   va_end (va);
487   fformat (ofp, "\n");
488 }
489
490 __clib_export word
491 fdformat (int fd, char *fmt, ...)
492 {
493   word ret;
494   u8 *s;
495   va_list va;
496
497   va_start (va, fmt);
498   s = va_format (0, fmt, &va);
499   va_end (va);
500
501   ret = write (fd, s, vec_len (s));
502   vec_free (s);
503   return ret;
504 }
505 #endif
506
507 /* Format integral type. */
508 static u8 *
509 format_integer (u8 * s, u64 number, format_integer_options_t * options)
510 {
511   u64 q;
512   u32 r;
513   u8 digit_buffer[128];
514   u8 *d = digit_buffer + sizeof (digit_buffer);
515   word c, base;
516
517   if (options->is_signed && (i64) number < 0)
518     {
519       number = -number;
520       vec_add1 (s, '-');
521     }
522
523   if (options->n_bits < BITS (number))
524     number &= ((u64) 1 << options->n_bits) - 1;
525
526   base = options->base;
527
528   while (1)
529     {
530       q = number / base;
531       r = number % base;
532
533       if (r < 10 + 26 + 26)
534         {
535           if (r < 10)
536             c = '0' + r;
537           else if (r < 10 + 26)
538             c = 'a' + (r - 10);
539           else
540             c = 'A' + (r - 10 - 26);
541
542           if (options->uppercase_digits
543               && base <= 10 + 26 && c >= 'a' && c <= 'z')
544             c += 'A' - 'a';
545
546           *--d = c;
547         }
548       else                      /* will never happen, warning be gone */
549         {
550           *--d = '?';
551         }
552
553       if (q == 0)
554         break;
555
556       number = q;
557     }
558
559   vec_add (s, d, digit_buffer + sizeof (digit_buffer) - d);
560   return s;
561 }
562
563 /* Floating point formatting. */
564 /* Deconstruct IEEE 64 bit number into sign exponent and fraction. */
565 #define f64_down(f,sign,expon,fraction)                         \
566 do {                                                            \
567   union { u64 u; f64 f; } _f64_down_tmp;                        \
568   _f64_down_tmp.f = (f);                                        \
569   (sign) = (_f64_down_tmp.u >> 63);                             \
570   (expon) = ((_f64_down_tmp.u >> 52) & 0x7ff) - 1023;           \
571   (fraction) = ((_f64_down_tmp.u << 12) >> 12) | ((u64) 1 << 52); \
572 } while (0)
573
574 /* Construct IEEE 64 bit number. */
575 static f64
576 f64_up (uword sign, word expon, u64 fraction)
577 {
578   union
579   {
580     u64 u;
581     f64 f;
582   } tmp;
583
584   tmp.u = (u64) ((sign) != 0) << 63;
585
586   expon += 1023;
587   if (expon > 1023)
588     expon = 1023;
589   if (expon < 0)
590     expon = 0;
591   tmp.u |= (u64) expon << 52;
592
593   tmp.u |= fraction & (((u64) 1 << 52) - 1);
594
595   return tmp.f;
596 }
597
598 /* Returns approximate precision of number given its exponent. */
599 static f64
600 f64_precision (int base2_expon)
601 {
602   static int n_bits = 0;
603
604   if (!n_bits)
605     {
606       /* Compute number of significant bits in floating point representation. */
607       f64 one = 0;
608       f64 small = 1;
609
610       while (one != 1)
611         {
612           small *= .5;
613           n_bits++;
614           one = 1 + small;
615         }
616     }
617
618   return f64_up (0, base2_expon - n_bits, 0);
619 }
620
621 /* Return x 10^n */
622 static f64
623 times_power_of_ten (f64 x, int n)
624 {
625   if (n >= 0)
626     {
627       static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, };
628       while (n >= 8)
629         {
630           x *= 1e+8;
631           n -= 8;
632         }
633       return x * t[n];
634     }
635   else
636     {
637       static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, };
638       while (n <= -8)
639         {
640           x *= 1e-8;
641           n += 8;
642         }
643       return x * t[-n];
644     }
645
646 }
647
648 /* Write x = y * 10^expon with 1 < y < 10. */
649 static f64
650 normalize (f64 x, word * expon_return, f64 * prec_return)
651 {
652   word expon2, expon10;
653   CLIB_UNUSED (u64 fraction);
654   CLIB_UNUSED (word sign);
655   f64 prec;
656
657   f64_down (x, sign, expon2, fraction);
658
659   expon10 =
660     .5 +
661     expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
662
663   prec = f64_precision (expon2);
664   x = times_power_of_ten (x, -expon10);
665   prec = times_power_of_ten (prec, -expon10);
666
667   while (x < 1)
668     {
669       x *= 10;
670       prec *= 10;
671       expon10--;
672     }
673
674   while (x > 10)
675     {
676       x *= .1;
677       prec *= .1;
678       expon10++;
679     }
680
681   if (x + prec >= 10)
682     {
683       x = 1;
684       expon10++;
685     }
686
687   *expon_return = expon10;
688   *prec_return = prec;
689
690   return x;
691 }
692
693 static u8 *
694 add_some_zeros (u8 * s, uword n_zeros)
695 {
696   while (n_zeros > 0)
697     {
698       vec_add1 (s, '0');
699       n_zeros--;
700     }
701   return s;
702 }
703
704 /* Format a floating point number with the given number of fractional
705    digits (e.g. 1.2345 with 2 fraction digits yields "1.23") and output style. */
706 static u8 *
707 format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
708 {
709   f64 prec;
710   word sign, expon, n_fraction_done, added_decimal_point;
711   /* Position of decimal point relative to where we are. */
712   word decimal_point;
713
714   /* Default number of digits to print when its not specified. */
715   if (n_fraction_digits == ~0)
716     n_fraction_digits = 7;
717   n_fraction_done = 0;
718   decimal_point = 0;
719   added_decimal_point = 0;
720   sign = expon = 0;
721
722   /* Special case: zero. */
723   if (x == 0)
724     {
725     do_zero:
726       vec_add1 (s, '0');
727       goto done;
728     }
729
730   if (x < 0)
731     {
732       x = -x;
733       sign = 1;
734     }
735
736   /* Check for not-a-number. */
737   if (isnan (x))
738     return format (s, "%cNaN", sign ? '-' : '+');
739
740   /* Check for infinity. */
741   if (isinf (x))
742     return format (s, "%cinfinity", sign ? '-' : '+');
743
744   x = normalize (x, &expon, &prec);
745
746   /* Not enough digits to print anything: so just print 0 */
747   if ((word) - expon > (word) n_fraction_digits
748       && (output_style == 'f' || (output_style == 'g')))
749     goto do_zero;
750
751   if (sign)
752     vec_add1 (s, '-');
753
754   if (output_style == 'f'
755       || (output_style == 'g' && expon > -10 && expon < 10))
756     {
757       if (expon < 0)
758         {
759           /* Add decimal point and leading zeros. */
760           vec_add1 (s, '.');
761           n_fraction_done = clib_min (-(expon + 1), n_fraction_digits);
762           s = add_some_zeros (s, n_fraction_done);
763           decimal_point = -n_fraction_done;
764           added_decimal_point = 1;
765         }
766       else
767         decimal_point = expon + 1;
768     }
769   else
770     {
771       /* Exponential output style. */
772       decimal_point = 1;
773       output_style = 'e';
774     }
775
776   while (1)
777     {
778       uword digit;
779
780       /* Number is smaller than precision: call it zero. */
781       if (x < prec)
782         break;
783
784       digit = x;
785       x -= digit;
786       if (x + prec >= 1)
787         {
788           digit++;
789           x -= 1;
790         }
791
792       /* Round last printed digit. */
793       if (decimal_point <= 0
794           && n_fraction_done + 1 == n_fraction_digits && digit < 9)
795         digit += x >= .5;
796
797       vec_add1 (s, '0' + digit);
798
799       /* Move rightwards towards/away from decimal point. */
800       decimal_point--;
801
802       n_fraction_done += decimal_point < 0;
803       if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
804         break;
805
806       if (decimal_point == 0 && x != 0)
807         {
808           vec_add1 (s, '.');
809           added_decimal_point = 1;
810         }
811
812       x *= 10;
813       prec *= 10;
814     }
815
816 done:
817   if (decimal_point > 0)
818     {
819       s = add_some_zeros (s, decimal_point);
820       decimal_point = 0;
821     }
822
823   if (n_fraction_done < n_fraction_digits)
824     {
825       if (!added_decimal_point)
826         vec_add1 (s, '.');
827       s = add_some_zeros (s, n_fraction_digits - n_fraction_done);
828     }
829
830   if (output_style == 'e')
831     s = format (s, "e%wd", expon);
832
833   return s;
834 }
835
836
837 /*
838  * fd.io coding-style-patch-verification: ON
839  *
840  * Local Variables:
841  * eval: (c-set-style "gnu")
842  * End:
843  */