Reorganize source tree to use single autotools instance
[vpp.git] / src / vppinfra / format.c
diff --git a/src/vppinfra/format.c b/src/vppinfra/format.c
new file mode 100644 (file)
index 0000000..78e52e9
--- /dev/null
@@ -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:
+ */