072ff3b33625c4922372cb61edecbb6547ebd1de
[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 'd':
282           {
283             u64 number;
284
285             o.base = 10;
286             if (c == 'x' || c == 'X')
287               o.base = 16;
288             o.is_signed = c == 'd';
289             o.uppercase_digits = c == 'X';
290
291             switch (fi.how_long)
292               {
293               case 'L':
294                 number = va_arg (*va, unsigned long long);
295                 o.n_bits = BITS (unsigned long long);
296                 break;
297
298               case 'l':
299                 number = va_arg (*va, long);
300                 o.n_bits = BITS (long);
301                 break;
302
303               case 'w':
304                 number = va_arg (*va, word);
305                 o.n_bits = BITS (uword);
306                 break;
307
308               default:
309                 number = va_arg (*va, int);
310                 o.n_bits = BITS (int);
311                 break;
312               }
313
314             s = format_integer (s, number, &o);
315           }
316           break;
317
318         case 's':
319         case 'S':
320           {
321             char *cstring = va_arg (*va, char *);
322             uword len;
323
324             if (!cstring)
325               {
326                 cstring = "(nil)";
327                 len = 5;
328               }
329             else if (fi.width[1] != 0)
330               len = clib_min (strlen (cstring), fi.width[1]);
331             else
332               len = strlen (cstring);
333
334             /* %S => format string as C identifier (replace _ with space). */
335             if (c == 'S')
336               {
337                 for (i = 0; i < len; i++)
338                   vec_add1 (s, cstring[i] == '_' ? ' ' : cstring[i]);
339               }
340             else
341               vec_add (s, cstring, len);
342           }
343           break;
344
345         case 'v':
346           {
347             u8 *v = va_arg (*va, u8 *);
348             uword len;
349
350             if (fi.width[1] != 0)
351               len = clib_min (vec_len (v), fi.width[1]);
352             else
353               len = vec_len (v);
354
355             vec_add (s, v, len);
356           }
357           break;
358
359         case 'f':
360         case 'g':
361         case 'e':
362           /* Floating point. */
363           ASSERT (fi.how_long == 0 || fi.how_long == 'l');
364           s = format_float (s, va_arg (*va, double), fi.width[1], c);
365           break;
366
367         case 'U':
368           /* User defined function. */
369           {
370             typedef u8 *(user_func_t) (u8 * s, va_list * args);
371             user_func_t *u = va_arg (*va, user_func_t *);
372
373             s = (*u) (s, va);
374           }
375           break;
376         }
377
378       s = justify (s, &fi, s_initial_len);
379     }
380
381 done:
382   *_s = s;
383   return f;
384 }
385
386 u8 *
387 va_format (u8 * s, const char *fmt, va_list * va)
388 {
389   const u8 *f = (u8 *) fmt, *g;
390   u8 c;
391
392   g = f;
393   while (1)
394     {
395       c = *f;
396
397       if (!c)
398         break;
399
400       if (c == '%')
401         {
402           if (f > g)
403             vec_add (s, g, f - g);
404           f = g = do_percent (&s, f, va);
405         }
406       else
407         {
408           f++;
409         }
410     }
411
412   if (f > g)
413     vec_add (s, g, f - g);
414
415   return s;
416 }
417
418 u8 *
419 format (u8 * s, const char *fmt, ...)
420 {
421   va_list va;
422   va_start (va, fmt);
423   s = va_format (s, fmt, &va);
424   va_end (va);
425 #ifdef __COVERITY__
426   if (s == 0)
427     return (u8 *) "liar liar pants on fire s can't be zero!";
428 #endif
429   return s;
430 }
431
432 word
433 va_fformat (FILE * f, char *fmt, va_list * va)
434 {
435   word ret;
436   u8 *s;
437
438   s = va_format (0, fmt, va);
439
440 #ifdef CLIB_UNIX
441   if (f)
442     {
443       ret = fwrite (s, vec_len (s), 1, f);
444     }
445   else
446 #endif /* CLIB_UNIX */
447     {
448       ret = 0;
449       os_puts (s, vec_len (s), /* is_error */ 0);
450     }
451
452   vec_free (s);
453   return ret;
454 }
455
456 word
457 fformat (FILE * f, char *fmt, ...)
458 {
459   va_list va;
460   word ret;
461
462   va_start (va, fmt);
463   ret = va_fformat (f, fmt, &va);
464   va_end (va);
465
466   return (ret);
467 }
468
469 #ifdef CLIB_UNIX
470 void
471 fformat_append_cr (FILE * ofp, const char *fmt, ...)
472 {
473   va_list va;
474
475   va_start (va, fmt);
476   (void) va_fformat (ofp, (char *) fmt, &va);
477   va_end (va);
478   fformat (ofp, "\n");
479 }
480
481 word
482 fdformat (int fd, char *fmt, ...)
483 {
484   word ret;
485   u8 *s;
486   va_list va;
487
488   va_start (va, fmt);
489   s = va_format (0, fmt, &va);
490   va_end (va);
491
492   ret = write (fd, s, vec_len (s));
493   vec_free (s);
494   return ret;
495 }
496 #endif
497
498 /* Format integral type. */
499 static u8 *
500 format_integer (u8 * s, u64 number, format_integer_options_t * options)
501 {
502   u64 q;
503   u32 r;
504   u8 digit_buffer[128];
505   u8 *d = digit_buffer + sizeof (digit_buffer);
506   word c, base;
507
508   if (options->is_signed && (i64) number < 0)
509     {
510       number = -number;
511       vec_add1 (s, '-');
512     }
513
514   if (options->n_bits < BITS (number))
515     number &= ((u64) 1 << options->n_bits) - 1;
516
517   base = options->base;
518
519   while (1)
520     {
521       q = number / base;
522       r = number % base;
523
524       if (r < 10 + 26 + 26)
525         {
526           if (r < 10)
527             c = '0' + r;
528           else if (r < 10 + 26)
529             c = 'a' + (r - 10);
530           else
531             c = 'A' + (r - 10 - 26);
532
533           if (options->uppercase_digits
534               && base <= 10 + 26 && c >= 'a' && c <= 'z')
535             c += 'A' - 'a';
536
537           *--d = c;
538         }
539       else                      /* will never happen, warning be gone */
540         {
541           *--d = '?';
542         }
543
544       if (q == 0)
545         break;
546
547       number = q;
548     }
549
550   vec_add (s, d, digit_buffer + sizeof (digit_buffer) - d);
551   return s;
552 }
553
554 /* Floating point formatting. */
555 /* Deconstruct IEEE 64 bit number into sign exponent and fraction. */
556 #define f64_down(f,sign,expon,fraction)                         \
557 do {                                                            \
558   union { u64 u; f64 f; } _f64_down_tmp;                        \
559   _f64_down_tmp.f = (f);                                        \
560   (sign) = (_f64_down_tmp.u >> 63);                             \
561   (expon) = ((_f64_down_tmp.u >> 52) & 0x7ff) - 1023;           \
562   (fraction) = ((_f64_down_tmp.u << 12) >> 12) | ((u64) 1 << 52); \
563 } while (0)
564
565 /* Construct IEEE 64 bit number. */
566 static f64
567 f64_up (uword sign, word expon, u64 fraction)
568 {
569   union
570   {
571     u64 u;
572     f64 f;
573   } tmp;
574
575   tmp.u = (u64) ((sign) != 0) << 63;
576
577   expon += 1023;
578   if (expon > 1023)
579     expon = 1023;
580   if (expon < 0)
581     expon = 0;
582   tmp.u |= (u64) expon << 52;
583
584   tmp.u |= fraction & (((u64) 1 << 52) - 1);
585
586   return tmp.f;
587 }
588
589 /* Returns approximate precision of number given its exponent. */
590 static f64
591 f64_precision (int base2_expon)
592 {
593   static int n_bits = 0;
594
595   if (!n_bits)
596     {
597       /* Compute number of significant bits in floating point representation. */
598       f64 one = 0;
599       f64 small = 1;
600
601       while (one != 1)
602         {
603           small *= .5;
604           n_bits++;
605           one = 1 + small;
606         }
607     }
608
609   return f64_up (0, base2_expon - n_bits, 0);
610 }
611
612 /* Return x 10^n */
613 static f64
614 times_power_of_ten (f64 x, int n)
615 {
616   if (n >= 0)
617     {
618       static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, };
619       while (n >= 8)
620         {
621           x *= 1e+8;
622           n -= 8;
623         }
624       return x * t[n];
625     }
626   else
627     {
628       static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, };
629       while (n <= -8)
630         {
631           x *= 1e-8;
632           n += 8;
633         }
634       return x * t[-n];
635     }
636
637 }
638
639 /* Write x = y * 10^expon with 1 < y < 10. */
640 static f64
641 normalize (f64 x, word * expon_return, f64 * prec_return)
642 {
643   word expon2, expon10;
644   CLIB_UNUSED (u64 fraction);
645   CLIB_UNUSED (word sign);
646   f64 prec;
647
648   f64_down (x, sign, expon2, fraction);
649
650   expon10 =
651     .5 +
652     expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
653
654   prec = f64_precision (expon2);
655   x = times_power_of_ten (x, -expon10);
656   prec = times_power_of_ten (prec, -expon10);
657
658   while (x < 1)
659     {
660       x *= 10;
661       prec *= 10;
662       expon10--;
663     }
664
665   while (x > 10)
666     {
667       x *= .1;
668       prec *= .1;
669       expon10++;
670     }
671
672   if (x + prec >= 10)
673     {
674       x = 1;
675       expon10++;
676     }
677
678   *expon_return = expon10;
679   *prec_return = prec;
680
681   return x;
682 }
683
684 static u8 *
685 add_some_zeros (u8 * s, uword n_zeros)
686 {
687   while (n_zeros > 0)
688     {
689       vec_add1 (s, '0');
690       n_zeros--;
691     }
692   return s;
693 }
694
695 /* Format a floating point number with the given number of fractional
696    digits (e.g. 1.2345 with 2 fraction digits yields "1.23") and output style. */
697 static u8 *
698 format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
699 {
700   f64 prec;
701   word sign, expon, n_fraction_done, added_decimal_point;
702   /* Position of decimal point relative to where we are. */
703   word decimal_point;
704
705   /* Default number of digits to print when its not specified. */
706   if (n_fraction_digits == ~0)
707     n_fraction_digits = 7;
708   n_fraction_done = 0;
709   decimal_point = 0;
710   added_decimal_point = 0;
711   sign = expon = 0;
712
713   /* Special case: zero. */
714   if (x == 0)
715     {
716     do_zero:
717       vec_add1 (s, '0');
718       goto done;
719     }
720
721   if (x < 0)
722     {
723       x = -x;
724       sign = 1;
725     }
726
727   /* Check for not-a-number. */
728   if (isnan (x))
729     return format (s, "%cNaN", sign ? '-' : '+');
730
731   /* Check for infinity. */
732   if (isinf (x))
733     return format (s, "%cinfinity", sign ? '-' : '+');
734
735   x = normalize (x, &expon, &prec);
736
737   /* Not enough digits to print anything: so just print 0 */
738   if ((word) - expon > (word) n_fraction_digits
739       && (output_style == 'f' || (output_style == 'g')))
740     goto do_zero;
741
742   if (sign)
743     vec_add1 (s, '-');
744
745   if (output_style == 'f'
746       || (output_style == 'g' && expon > -10 && expon < 10))
747     {
748       if (expon < 0)
749         {
750           /* Add decimal point and leading zeros. */
751           vec_add1 (s, '.');
752           n_fraction_done = clib_min (-(expon + 1), n_fraction_digits);
753           s = add_some_zeros (s, n_fraction_done);
754           decimal_point = -n_fraction_done;
755           added_decimal_point = 1;
756         }
757       else
758         decimal_point = expon + 1;
759     }
760   else
761     {
762       /* Exponential output style. */
763       decimal_point = 1;
764       output_style = 'e';
765     }
766
767   while (1)
768     {
769       uword digit;
770
771       /* Number is smaller than precision: call it zero. */
772       if (x < prec)
773         break;
774
775       digit = x;
776       x -= digit;
777       if (x + prec >= 1)
778         {
779           digit++;
780           x -= 1;
781         }
782
783       /* Round last printed digit. */
784       if (decimal_point <= 0
785           && n_fraction_done + 1 == n_fraction_digits && digit < 9)
786         digit += x >= .5;
787
788       vec_add1 (s, '0' + digit);
789
790       /* Move rightwards towards/away from decimal point. */
791       decimal_point--;
792
793       n_fraction_done += decimal_point < 0;
794       if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
795         break;
796
797       if (decimal_point == 0 && x != 0)
798         {
799           vec_add1 (s, '.');
800           added_decimal_point = 1;
801         }
802
803       x *= 10;
804       prec *= 10;
805     }
806
807 done:
808   if (decimal_point > 0)
809     {
810       s = add_some_zeros (s, decimal_point);
811       decimal_point = 0;
812     }
813
814   if (n_fraction_done < n_fraction_digits)
815     {
816       if (!added_decimal_point)
817         vec_add1 (s, '.');
818       s = add_some_zeros (s, n_fraction_digits - n_fraction_done);
819     }
820
821   if (output_style == 'e')
822     s = format (s, "e%wd", expon);
823
824   return s;
825 }
826
827
828 /*
829  * fd.io coding-style-patch-verification: ON
830  *
831  * Local Variables:
832  * eval: (c-set-style "gnu")
833  * End:
834  */