summaryrefslogtreecommitdiffstats
path: root/src/vppinfra/format.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vppinfra/format.c')
-rw-r--r--src/vppinfra/format.c814
1 files changed, 814 insertions, 0 deletions
diff --git a/src/vppinfra/format.c b/src/vppinfra/format.c
new file mode 100644
index 00000000000..78e52e9a2ad
--- /dev/null
+++ b/src/vppinfra/format.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*------------------------------------------------------------------
+ * format.c -- see notice below
+ *
+ * October 2003, Eliot Dresselhaus
+ *
+ * Modifications to this file Copyright (c) 2003 by cisco Systems, Inc.
+ * All rights reserved.
+ *------------------------------------------------------------------
+ */
+
+/*
+ Copyright (c) 2001, 2002, 2003, 2006 Eliot Dresselhaus
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdarg.h> /* va_start, etc */
+
+#ifdef CLIB_UNIX
+#include <unistd.h>
+#include <stdio.h>
+#endif
+
+#ifdef CLIB_STANDALONE
+#include <vppinfra/standalone_stdio.h>
+#endif
+
+#include <vppinfra/mem.h>
+#include <vppinfra/format.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/error.h>
+#include <vppinfra/string.h>
+#include <vppinfra/os.h> /* os_puts */
+
+typedef struct
+{
+ /* Output number in this base. */
+ u8 base;
+
+ /* Number of show of 64 bit number. */
+ u8 n_bits;
+
+ /* Signed or unsigned. */
+ u8 is_signed;
+
+ /* Output digits uppercase (not lowercase) %X versus %x. */
+ u8 uppercase_digits;
+} format_integer_options_t;
+
+static u8 *format_integer (u8 * s, u64 number,
+ format_integer_options_t * options);
+static u8 *format_float (u8 * s, f64 x, uword n_digits_to_print,
+ uword output_style);
+
+typedef struct
+{
+ /* String justification: + => right, - => left, = => center. */
+ uword justify;
+
+ /* Width of string (before and after decimal point for numbers).
+ 0 => natural width. */
+ uword width[2];
+
+ /* Long => 'l', long long 'L', int 0. */
+ uword how_long;
+
+ /* Pad character. Defaults to space. */
+ uword pad_char;
+} format_info_t;
+
+static u8 *
+justify (u8 * s, format_info_t * fi, uword s_len_orig)
+{
+ uword i0, l0, l1;
+
+ i0 = s_len_orig;
+ l0 = i0 + fi->width[0];
+ l1 = vec_len (s);
+
+ /* If width is zero user returned width. */
+ if (l0 == i0)
+ l0 = l1;
+
+ if (l1 > l0)
+ _vec_len (s) = l0;
+ else if (l0 > l1)
+ {
+ uword n = l0 - l1;
+ uword n_left = 0, n_right = 0;
+
+ switch (fi->justify)
+ {
+ case '-':
+ n_right = n;
+ break;
+
+ case '+':
+ n_left = n;
+ break;
+
+ case '=':
+ n_right = n_left = n / 2;
+ if (n % 2)
+ n_left++;
+ break;
+ }
+ if (n_left > 0)
+ {
+ vec_insert (s, n_left, i0);
+ memset (s + i0, fi->pad_char, n_left);
+ l1 = vec_len (s);
+ }
+ if (n_right > 0)
+ {
+ vec_resize (s, n_right);
+ memset (s + l1, fi->pad_char, n_right);
+ }
+ }
+ return s;
+}
+
+static u8 *
+do_percent (u8 ** _s, u8 * fmt, va_list * va)
+{
+ u8 *s = *_s;
+ uword c;
+
+ u8 *f = fmt;
+
+ format_info_t fi = {
+ .justify = '+',
+ .width = {0},
+ .pad_char = ' ',
+ .how_long = 0,
+ };
+
+ uword i;
+
+ ASSERT (f[0] == '%');
+
+ switch (c = *++f)
+ {
+ case '%':
+ /* %% => % */
+ vec_add1 (s, c);
+ f++;
+ goto done;
+
+ case '-':
+ case '+':
+ case '=':
+ fi.justify = c;
+ c = *++f;
+ break;
+ }
+
+ /* Parse width0 . width1. */
+ {
+ uword is_first_digit = 1;
+
+ fi.width[0] = fi.width[1] = 0;
+ for (i = 0; i < 2; i++)
+ {
+ if (c == '0' && i == 0 && is_first_digit)
+ fi.pad_char = '0';
+ is_first_digit = 0;
+ if (c == '*')
+ {
+ fi.width[i] = va_arg (*va, int);
+ c = *++f;
+ }
+ else
+ {
+ while (c >= '0' && c <= '9')
+ {
+ fi.width[i] = 10 * fi.width[i] + (c - '0');
+ c = *++f;
+ }
+ }
+ if (c != '.')
+ break;
+ c = *++f;
+ }
+ }
+
+ /* Parse %l* and %L* */
+ switch (c)
+ {
+ case 'w':
+ /* word format. */
+ fi.how_long = 'w';
+ c = *++f;
+ break;
+
+ case 'L':
+ case 'l':
+ fi.how_long = c;
+ c = *++f;
+ if (c == 'l' && *f == 'l')
+ {
+ fi.how_long = 'L';
+ c = *++f;
+ }
+ break;
+ }
+
+ /* Finally we are ready for format letter. */
+ if (c != 0)
+ {
+ uword s_initial_len = vec_len (s);
+ format_integer_options_t o = {
+ .is_signed = 0,
+ .base = 10,
+ .n_bits = BITS (uword),
+ .uppercase_digits = 0,
+ };
+
+ f++;
+
+ switch (c)
+ {
+ default:
+ {
+ /* Try to give a helpful error message. */
+ vec_free (s);
+ s = format (s, "**** CLIB unknown format `%%%c' ****", c);
+ goto done;
+ }
+
+ case 'c':
+ vec_add1 (s, va_arg (*va, int));
+ break;
+
+ case 'p':
+ vec_add1 (s, '0');
+ vec_add1 (s, 'x');
+
+ o.is_signed = 0;
+ o.n_bits = BITS (uword *);
+ o.base = 16;
+ o.uppercase_digits = 0;
+
+ s = format_integer (s, pointer_to_uword (va_arg (*va, void *)), &o);
+ break;
+
+ case 'x':
+ case 'X':
+ case 'u':
+ case 'd':
+ {
+ u64 number;
+
+ o.base = 10;
+ if (c == 'x' || c == 'X')
+ o.base = 16;
+ o.is_signed = c == 'd';
+ o.uppercase_digits = c == 'X';
+
+ switch (fi.how_long)
+ {
+ case 'L':
+ number = va_arg (*va, unsigned long long);
+ o.n_bits = BITS (unsigned long long);
+ break;
+
+ case 'l':
+ number = va_arg (*va, long);
+ o.n_bits = BITS (long);
+ break;
+
+ case 'w':
+ number = va_arg (*va, word);
+ o.n_bits = BITS (uword);
+ break;
+
+ default:
+ number = va_arg (*va, int);
+ o.n_bits = BITS (int);
+ break;
+ }
+
+ s = format_integer (s, number, &o);
+ }
+ break;
+
+ case 's':
+ case 'S':
+ {
+ char *cstring = va_arg (*va, char *);
+ uword len;
+
+ if (!cstring)
+ {
+ cstring = "(nil)";
+ len = 5;
+ }
+ else if (fi.width[1] != 0)
+ len = clib_min (strlen (cstring), fi.width[1]);
+ else
+ len = strlen (cstring);
+
+ /* %S => format string as C identifier (replace _ with space). */
+ if (c == 'S')
+ {
+ for (i = 0; i < len; i++)
+ vec_add1 (s, cstring[i] == '_' ? ' ' : cstring[i]);
+ }
+ else
+ vec_add (s, cstring, len);
+ }
+ break;
+
+ case 'v':
+ {
+ u8 *v = va_arg (*va, u8 *);
+ uword len;
+
+ if (fi.width[1] != 0)
+ len = clib_min (vec_len (v), fi.width[1]);
+ else
+ len = vec_len (v);
+
+ vec_add (s, v, len);
+ }
+ break;
+
+ case 'f':
+ case 'g':
+ case 'e':
+ /* Floating point. */
+ ASSERT (fi.how_long == 0 || fi.how_long == 'l');
+ s = format_float (s, va_arg (*va, double), fi.width[1], c);
+ break;
+
+ case 'U':
+ /* User defined function. */
+ {
+ typedef u8 *(user_func_t) (u8 * s, va_list * args);
+ user_func_t *u = va_arg (*va, user_func_t *);
+
+ s = (*u) (s, va);
+ }
+ break;
+ }
+
+ s = justify (s, &fi, s_initial_len);
+ }
+
+done:
+ *_s = s;
+ return f;
+}
+
+u8 *
+va_format (u8 * s, const char *fmt, va_list * va)
+{
+ u8 *f = (u8 *) fmt, *g;
+ u8 c;
+
+ g = f;
+ while (1)
+ {
+ c = *f;
+
+ if (!c)
+ break;
+
+ if (c == '%')
+ {
+ if (f > g)
+ vec_add (s, g, f - g);
+ f = g = do_percent (&s, f, va);
+ }
+ else
+ {
+ f++;
+ }
+ }
+
+ if (f > g)
+ vec_add (s, g, f - g);
+
+ return s;
+}
+
+u8 *
+format (u8 * s, const char *fmt, ...)
+{
+ va_list va;
+ va_start (va, fmt);
+ s = va_format (s, fmt, &va);
+ va_end (va);
+ return s;
+}
+
+word
+va_fformat (FILE * f, char *fmt, va_list * va)
+{
+ word ret;
+ u8 *s;
+
+ s = va_format (0, fmt, va);
+
+#ifdef CLIB_UNIX
+ if (f)
+ {
+ ret = fwrite (s, vec_len (s), 1, f);
+ }
+ else
+#endif /* CLIB_UNIX */
+ {
+ ret = 0;
+ os_puts (s, vec_len (s), /* is_error */ 0);
+ }
+
+ vec_free (s);
+ return ret;
+}
+
+word
+fformat (FILE * f, char *fmt, ...)
+{
+ va_list va;
+ word ret;
+
+ va_start (va, fmt);
+ ret = va_fformat (f, fmt, &va);
+ va_end (va);
+
+ return (ret);
+}
+
+#ifdef CLIB_UNIX
+word
+fdformat (int fd, char *fmt, ...)
+{
+ word ret;
+ u8 *s;
+ va_list va;
+
+ va_start (va, fmt);
+ s = va_format (0, fmt, &va);
+ va_end (va);
+
+ ret = write (fd, s, vec_len (s));
+ vec_free (s);
+ return ret;
+}
+#endif
+
+/* Format integral type. */
+static u8 *
+format_integer (u8 * s, u64 number, format_integer_options_t * options)
+{
+ u64 q;
+ u32 r;
+ u8 digit_buffer[128];
+ u8 *d = digit_buffer + sizeof (digit_buffer);
+ word c, base;
+
+ if (options->is_signed && (i64) number < 0)
+ {
+ number = -number;
+ vec_add1 (s, '-');
+ }
+
+ if (options->n_bits < BITS (number))
+ number &= ((u64) 1 << options->n_bits) - 1;
+
+ base = options->base;
+
+ while (1)
+ {
+ q = number / base;
+ r = number % base;
+
+ if (r < 10 + 26 + 26)
+ {
+ if (r < 10)
+ c = '0' + r;
+ else if (r < 10 + 26)
+ c = 'a' + (r - 10);
+ else
+ c = 'A' + (r - 10 - 26);
+
+ if (options->uppercase_digits
+ && base <= 10 + 26 && c >= 'a' && c <= 'z')
+ c += 'A' - 'a';
+
+ *--d = c;
+ }
+ else /* will never happen, warning be gone */
+ {
+ *--d = '?';
+ }
+
+ if (q == 0)
+ break;
+
+ number = q;
+ }
+
+ vec_add (s, d, digit_buffer + sizeof (digit_buffer) - d);
+ return s;
+}
+
+/* Floating point formatting. */
+/* Deconstruct IEEE 64 bit number into sign exponent and fraction. */
+#define f64_down(f,sign,expon,fraction) \
+do { \
+ union { u64 u; f64 f; } _f64_down_tmp; \
+ _f64_down_tmp.f = (f); \
+ (sign) = (_f64_down_tmp.u >> 63); \
+ (expon) = ((_f64_down_tmp.u >> 52) & 0x7ff) - 1023; \
+ (fraction) = ((_f64_down_tmp.u << 12) >> 12) | ((u64) 1 << 52); \
+} while (0)
+
+/* Construct IEEE 64 bit number. */
+static f64
+f64_up (uword sign, word expon, u64 fraction)
+{
+ union
+ {
+ u64 u;
+ f64 f;
+ } tmp;
+
+ tmp.u = (u64) ((sign) != 0) << 63;
+
+ expon += 1023;
+ if (expon > 1023)
+ expon = 1023;
+ if (expon < 0)
+ expon = 0;
+ tmp.u |= (u64) expon << 52;
+
+ tmp.u |= fraction & (((u64) 1 << 52) - 1);
+
+ return tmp.f;
+}
+
+/* Returns approximate precision of number given its exponent. */
+static f64
+f64_precision (int base2_expon)
+{
+ static int n_bits = 0;
+
+ if (!n_bits)
+ {
+ /* Compute number of significant bits in floating point representation. */
+ f64 one = 0;
+ f64 small = 1;
+
+ while (one != 1)
+ {
+ small *= .5;
+ n_bits++;
+ one = 1 + small;
+ }
+ }
+
+ return f64_up (0, base2_expon - n_bits, 0);
+}
+
+/* Return x 10^n */
+static f64
+times_power_of_ten (f64 x, int n)
+{
+ if (n >= 0)
+ {
+ static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, };
+ while (n >= 8)
+ {
+ x *= 1e+8;
+ n -= 8;
+ }
+ return x * t[n];
+ }
+ else
+ {
+ static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, };
+ while (n <= -8)
+ {
+ x *= 1e-8;
+ n += 8;
+ }
+ return x * t[-n];
+ }
+
+}
+
+/* Write x = y * 10^expon with 1 < y < 10. */
+static f64
+normalize (f64 x, word * expon_return, f64 * prec_return)
+{
+ word expon2, expon10;
+ CLIB_UNUSED (u64 fraction);
+ CLIB_UNUSED (word sign);
+ f64 prec;
+
+ f64_down (x, sign, expon2, fraction);
+
+ expon10 =
+ .5 +
+ expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
+
+ prec = f64_precision (expon2);
+ x = times_power_of_ten (x, -expon10);
+ prec = times_power_of_ten (prec, -expon10);
+
+ while (x < 1)
+ {
+ x *= 10;
+ prec *= 10;
+ expon10--;
+ }
+
+ while (x > 10)
+ {
+ x *= .1;
+ prec *= .1;
+ expon10++;
+ }
+
+ if (x + prec >= 10)
+ {
+ x = 1;
+ expon10++;
+ }
+
+ *expon_return = expon10;
+ *prec_return = prec;
+
+ return x;
+}
+
+static u8 *
+add_some_zeros (u8 * s, uword n_zeros)
+{
+ while (n_zeros > 0)
+ {
+ vec_add1 (s, '0');
+ n_zeros--;
+ }
+ return s;
+}
+
+/* Format a floating point number with the given number of fractional
+ digits (e.g. 1.2345 with 2 fraction digits yields "1.23") and output style. */
+static u8 *
+format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
+{
+ f64 prec;
+ word sign, expon, n_fraction_done, added_decimal_point;
+ /* Position of decimal point relative to where we are. */
+ word decimal_point;
+
+ /* Default number of digits to print when its not specified. */
+ if (n_fraction_digits == ~0)
+ n_fraction_digits = 7;
+ n_fraction_done = 0;
+ decimal_point = 0;
+ added_decimal_point = 0;
+ sign = expon = 0;
+
+ /* Special case: zero. */
+ if (x == 0)
+ {
+ do_zero:
+ vec_add1 (s, '0');
+ goto done;
+ }
+
+ if (x < 0)
+ {
+ x = -x;
+ sign = 1;
+ }
+
+ /* Check for infinity. */
+ if (x == x / 2)
+ return format (s, "%cinfinity", sign ? '-' : '+');
+
+ x = normalize (x, &expon, &prec);
+
+ /* Not enough digits to print anything: so just print 0 */
+ if ((word) - expon > (word) n_fraction_digits
+ && (output_style == 'f' || (output_style == 'g')))
+ goto do_zero;
+
+ if (sign)
+ vec_add1 (s, '-');
+
+ if (output_style == 'f'
+ || (output_style == 'g' && expon > -10 && expon < 10))
+ {
+ if (expon < 0)
+ {
+ /* Add decimal point and leading zeros. */
+ vec_add1 (s, '.');
+ n_fraction_done = clib_min (-(expon + 1), n_fraction_digits);
+ s = add_some_zeros (s, n_fraction_done);
+ decimal_point = -n_fraction_done;
+ added_decimal_point = 1;
+ }
+ else
+ decimal_point = expon + 1;
+ }
+ else
+ {
+ /* Exponential output style. */
+ decimal_point = 1;
+ output_style = 'e';
+ }
+
+ while (1)
+ {
+ uword digit;
+
+ /* Number is smaller than precision: call it zero. */
+ if (x < prec)
+ break;
+
+ digit = x;
+ x -= digit;
+ if (x + prec >= 1)
+ {
+ digit++;
+ x -= 1;
+ }
+
+ /* Round last printed digit. */
+ if (decimal_point <= 0
+ && n_fraction_done + 1 == n_fraction_digits && digit < 9)
+ digit += x >= .5;
+
+ vec_add1 (s, '0' + digit);
+
+ /* Move rightwards towards/away from decimal point. */
+ decimal_point--;
+
+ n_fraction_done += decimal_point < 0;
+ if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
+ break;
+
+ if (decimal_point == 0 && x != 0)
+ {
+ vec_add1 (s, '.');
+ added_decimal_point = 1;
+ }
+
+ x *= 10;
+ prec *= 10;
+ }
+
+done:
+ if (decimal_point > 0)
+ {
+ s = add_some_zeros (s, decimal_point);
+ decimal_point = 0;
+ }
+
+ if (n_fraction_done < n_fraction_digits)
+ {
+ if (!added_decimal_point)
+ vec_add1 (s, '.');
+ s = add_some_zeros (s, n_fraction_digits - n_fraction_done);
+ }
+
+ if (output_style == 'e')
+ s = format (s, "e%wd", expon);
+
+ return s;
+}
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */