blob: cf17b8a1acbcdffaf23b4874310c45b06e7e1929 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
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
Dave Barachc3799992016-08-15 11:12:27 -040048#include <stdarg.h> /* va_start, etc */
Ed Warnickecb9cada2015-12-08 15:45:58 -070049
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>
Dave Barachc3799992016-08-15 11:12:27 -040064#include <vppinfra/os.h> /* os_puts */
Chris Lukebb18ee62017-07-09 14:30:25 -040065#include <vppinfra/math.h>
Ed Warnickecb9cada2015-12-08 15:45:58 -070066
Dave Barachc3799992016-08-15 11:12:27 -040067typedef struct
68{
69 /* Output number in this base. */
70 u8 base;
Ed Warnickecb9cada2015-12-08 15:45:58 -070071
Dave Barachc3799992016-08-15 11:12:27 -040072 /* Number of show of 64 bit number. */
73 u8 n_bits;
Ed Warnickecb9cada2015-12-08 15:45:58 -070074
Dave Barachc3799992016-08-15 11:12:27 -040075 /* Signed or unsigned. */
76 u8 is_signed;
Ed Warnickecb9cada2015-12-08 15:45:58 -070077
Dave Barachc3799992016-08-15 11:12:27 -040078 /* Output digits uppercase (not lowercase) %X versus %x. */
79 u8 uppercase_digits;
Ed Warnickecb9cada2015-12-08 15:45:58 -070080} format_integer_options_t;
81
Dave Barachc3799992016-08-15 11:12:27 -040082static u8 *format_integer (u8 * s, u64 number,
83 format_integer_options_t * options);
84static u8 *format_float (u8 * s, f64 x, uword n_digits_to_print,
85 uword output_style);
Ed Warnickecb9cada2015-12-08 15:45:58 -070086
Dave Barachc3799992016-08-15 11:12:27 -040087typedef struct
88{
Ed Warnickecb9cada2015-12-08 15:45:58 -070089 /* 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
Dave Barachc3799992016-08-15 11:12:27 -0400103static u8 *
104justify (u8 * s, format_info_t * fi, uword s_len_orig)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700105{
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)
Damjan Marion8bea5892022-04-04 22:40:45 +0200117 vec_set_len (s, l0);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700118 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 '+':
Dave Barachc3799992016-08-15 11:12:27 -0400130 n_left = n;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700131 break;
132
133 case '=':
Dave Barachc3799992016-08-15 11:12:27 -0400134 n_right = n_left = n / 2;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700135 if (n % 2)
136 n_left++;
137 break;
138 }
139 if (n_left > 0)
140 {
141 vec_insert (s, n_left, i0);
Dave Barachb7b92992018-10-17 10:38:51 -0400142 clib_memset (s + i0, fi->pad_char, n_left);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700143 l1 = vec_len (s);
144 }
145 if (n_right > 0)
146 {
147 vec_resize (s, n_right);
Dave Barachb7b92992018-10-17 10:38:51 -0400148 clib_memset (s + l1, fi->pad_char, n_right);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700149 }
150 }
151 return s;
152}
153
Neale Ranns32e1c012016-11-22 17:07:28 +0000154static const u8 *
155do_percent (u8 ** _s, const u8 * fmt, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700156{
Dave Barachc3799992016-08-15 11:12:27 -0400157 u8 *s = *_s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700158 uword c;
159
Neale Ranns32e1c012016-11-22 17:07:28 +0000160 const u8 *f = fmt;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700161
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;
Dave Barachc3799992016-08-15 11:12:27 -0400199 if (c == '*')
200 {
201 fi.width[i] = va_arg (*va, int);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700202 c = *++f;
Dave Barachc3799992016-08-15 11:12:27 -0400203 }
204 else
205 {
206 while (c >= '0' && c <= '9')
207 {
208 fi.width[i] = 10 * fi.width[i] + (c - '0');
Ed Warnickecb9cada2015-12-08 15:45:58 -0700209 c = *++f;
Dave Barachc3799992016-08-15 11:12:27 -0400210 }
211 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700212 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 {
Dave Barachc3799992016-08-15 11:12:27 -0400254 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 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700261
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':
Nathan Skrzypczak04a14132021-05-07 19:39:51 +0200281 case 'o':
Ed Warnickecb9cada2015-12-08 15:45:58 -0700282 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
Nathan Skrzypczak04a14132021-05-07 19:39:51 +0200315 if (c == 'o')
316 o.base = 8;
317
Ed Warnickecb9cada2015-12-08 15:45:58 -0700318 s = format_integer (s, number, &o);
319 }
320 break;
321
322 case 's':
323 case 'S':
324 {
Dave Barachc3799992016-08-15 11:12:27 -0400325 char *cstring = va_arg (*va, char *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700326 uword len;
327
Dave Barachc3799992016-08-15 11:12:27 -0400328 if (!cstring)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700329 {
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);
Dave Barachc3799992016-08-15 11:12:27 -0400337
Ed Warnickecb9cada2015-12-08 15:45:58 -0700338 /* %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]);
Dave Barachc3799992016-08-15 11:12:27 -0400343 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700344 else
345 vec_add (s, cstring, len);
346 }
347 break;
348
349 case 'v':
350 {
Dave Barachc3799992016-08-15 11:12:27 -0400351 u8 *v = va_arg (*va, u8 *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700352 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
Dave Barachc3799992016-08-15 11:12:27 -0400363 case 'f':
364 case 'g':
365 case 'e':
Ed Warnickecb9cada2015-12-08 15:45:58 -0700366 /* Floating point. */
367 ASSERT (fi.how_long == 0 || fi.how_long == 'l');
Dave Barachc3799992016-08-15 11:12:27 -0400368 s = format_float (s, va_arg (*va, double), fi.width[1], c);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700369 break;
370
371 case 'U':
372 /* User defined function. */
373 {
Dave Barachc3799992016-08-15 11:12:27 -0400374 typedef u8 *(user_func_t) (u8 * s, va_list * args);
375 user_func_t *u = va_arg (*va, user_func_t *);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700376
Dave Barachc3799992016-08-15 11:12:27 -0400377 s = (*u) (s, va);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700378 }
379 break;
380 }
381
382 s = justify (s, &fi, s_initial_len);
383 }
384
Dave Barachc3799992016-08-15 11:12:27 -0400385done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700386 *_s = s;
387 return f;
388}
389
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200390__clib_export u8 *
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100391va_format (u8 * s, const char *fmt, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700392{
Neale Ranns32e1c012016-11-22 17:07:28 +0000393 const u8 *f = (u8 *) fmt, *g;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700394 u8 c;
395
396 g = f;
397 while (1)
398 {
399 c = *f;
400
Dave Barachc3799992016-08-15 11:12:27 -0400401 if (!c)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700402 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
Dave Barachac3b1132019-03-30 10:24:28 -0400419#ifdef __COVERITY__
420 if (s == 0)
421 return (u8 *) "liar liar pants on fire s can't be zero!";
422#endif
423
Ed Warnickecb9cada2015-12-08 15:45:58 -0700424 return s;
425}
426
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200427__clib_export u8 *
Neale Ranns0bfe5d82016-08-25 15:29:12 +0100428format (u8 * s, const char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700429{
430 va_list va;
431 va_start (va, fmt);
432 s = va_format (s, fmt, &va);
433 va_end (va);
Dave Barach19e540f2019-03-29 19:41:50 -0400434#ifdef __COVERITY__
435 if (s == 0)
436 return (u8 *) "liar liar pants on fire s can't be zero!";
437#endif
Ed Warnickecb9cada2015-12-08 15:45:58 -0700438 return s;
439}
440
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200441__clib_export word
Dave Barachc3799992016-08-15 11:12:27 -0400442va_fformat (FILE * f, char *fmt, va_list * va)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700443{
444 word ret;
Dave Barachc3799992016-08-15 11:12:27 -0400445 u8 *s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700446
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
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200465__clib_export word
Dave Barachc3799992016-08-15 11:12:27 -0400466fformat (FILE * f, char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700467{
Dave Barachc3799992016-08-15 11:12:27 -0400468 va_list va;
469 word ret;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700470
Dave Barachc3799992016-08-15 11:12:27 -0400471 va_start (va, fmt);
472 ret = va_fformat (f, fmt, &va);
473 va_end (va);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700474
Dave Barachc3799992016-08-15 11:12:27 -0400475 return (ret);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700476}
477
478#ifdef CLIB_UNIX
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200479__clib_export void
Yi Hed05ce972018-07-16 13:52:00 +0800480fformat_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
Damjan Mariondae1c7e2020-10-17 13:32:25 +0200490__clib_export word
Dave Barachc3799992016-08-15 11:12:27 -0400491fdformat (int fd, char *fmt, ...)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700492{
493 word ret;
Dave Barachc3799992016-08-15 11:12:27 -0400494 u8 *s;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700495 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. */
Dave Barachc3799992016-08-15 11:12:27 -0400508static u8 *
509format_integer (u8 * s, u64 number, format_integer_options_t * options)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700510{
511 u64 q;
512 u32 r;
513 u8 digit_buffer[128];
Dave Barachc3799992016-08-15 11:12:27 -0400514 u8 *d = digit_buffer + sizeof (digit_buffer);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700515 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
Dave Barachc3799992016-08-15 11:12:27 -0400533 if (r < 10 + 26 + 26)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700534 {
535 if (r < 10)
536 c = '0' + r;
Dave Barachc3799992016-08-15 11:12:27 -0400537 else if (r < 10 + 26)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700538 c = 'a' + (r - 10);
539 else
540 c = 'A' + (r - 10 - 26);
541
542 if (options->uppercase_digits
Dave Barachc3799992016-08-15 11:12:27 -0400543 && base <= 10 + 26 && c >= 'a' && c <= 'z')
Ed Warnickecb9cada2015-12-08 15:45:58 -0700544 c += 'A' - 'a';
545
546 *--d = c;
547 }
Dave Barachc3799992016-08-15 11:12:27 -0400548 else /* will never happen, warning be gone */
549 {
550 *--d = '?';
551 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700552
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) \
566do { \
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. */
Dave Barachc3799992016-08-15 11:12:27 -0400575static f64
576f64_up (uword sign, word expon, u64 fraction)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700577{
Dave Barachc3799992016-08-15 11:12:27 -0400578 union
579 {
580 u64 u;
581 f64 f;
582 } tmp;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700583
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. */
Dave Barachc3799992016-08-15 11:12:27 -0400599static f64
600f64_precision (int base2_expon)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700601{
602 static int n_bits = 0;
603
Dave Barachc3799992016-08-15 11:12:27 -0400604 if (!n_bits)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700605 {
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 */
Dave Barachc3799992016-08-15 11:12:27 -0400622static f64
623times_power_of_ten (f64 x, int n)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700624{
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 }
Dave Barachc3799992016-08-15 11:12:27 -0400645
Ed Warnickecb9cada2015-12-08 15:45:58 -0700646}
647
648/* Write x = y * 10^expon with 1 < y < 10. */
Dave Barachc3799992016-08-15 11:12:27 -0400649static f64
650normalize (f64 x, word * expon_return, f64 * prec_return)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700651{
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
Dave Barachc3799992016-08-15 11:12:27 -0400659 expon10 =
660 .5 +
661 expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
662
Ed Warnickecb9cada2015-12-08 15:45:58 -0700663 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
Dave Barachc3799992016-08-15 11:12:27 -0400693static u8 *
694add_some_zeros (u8 * s, uword n_zeros)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700695{
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. */
706static u8 *
Dave Barachc3799992016-08-15 11:12:27 -0400707format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700708{
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 }
Dave Barachc3799992016-08-15 11:12:27 -0400729
Ed Warnickecb9cada2015-12-08 15:45:58 -0700730 if (x < 0)
731 {
732 x = -x;
733 sign = 1;
734 }
735
Chris Lukebb18ee62017-07-09 14:30:25 -0400736 /* Check for not-a-number. */
737 if (isnan (x))
738 return format (s, "%cNaN", sign ? '-' : '+');
739
Ed Warnickecb9cada2015-12-08 15:45:58 -0700740 /* Check for infinity. */
Chris Lukebb18ee62017-07-09 14:30:25 -0400741 if (isinf (x))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700742 return format (s, "%cinfinity", sign ? '-' : '+');
743
744 x = normalize (x, &expon, &prec);
745
746 /* Not enough digits to print anything: so just print 0 */
Dave Barachc3799992016-08-15 11:12:27 -0400747 if ((word) - expon > (word) n_fraction_digits
Ed Warnickecb9cada2015-12-08 15:45:58 -0700748 && (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
Dave Barachc3799992016-08-15 11:12:27 -0400794 && n_fraction_done + 1 == n_fraction_digits && digit < 9)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700795 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;
Dave Barachc3799992016-08-15 11:12:27 -0400803 if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700804 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 }
Dave Barachc3799992016-08-15 11:12:27 -0400815
816done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700817 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 {
Dave Barachc3799992016-08-15 11:12:27 -0400825 if (!added_decimal_point)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700826 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
Dave Barachc3799992016-08-15 11:12:27 -0400836
837/*
838 * fd.io coding-style-patch-verification: ON
839 *
840 * Local Variables:
841 * eval: (c-set-style "gnu")
842 * End:
843 */