FD.io VPP  v19.04.2-12-g66b1689
Vector Packet Processing
format.c
Go to the documentation of this file.
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. */
71 
72  /* Number of show of 64 bit number. */
74 
75  /* Signed or unsigned. */
77 
78  /* Output digits uppercase (not lowercase) %X versus %x. */
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. */
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. */
98 
99  /* Pad character. Defaults to space. */
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);
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 #ifdef __COVERITY__
416  if (s == 0)
417  return (u8 *) "liar liar pants on fire s can't be zero!";
418 #endif
419 
420  return s;
421 }
422 
423 u8 *
424 format (u8 * s, const char *fmt, ...)
425 {
426  va_list va;
427  va_start (va, fmt);
428  s = va_format (s, fmt, &va);
429  va_end (va);
430 #ifdef __COVERITY__
431  if (s == 0)
432  return (u8 *) "liar liar pants on fire s can't be zero!";
433 #endif
434  return s;
435 }
436 
437 word
438 va_fformat (FILE * f, char *fmt, va_list * va)
439 {
440  word ret;
441  u8 *s;
442 
443  s = va_format (0, fmt, va);
444 
445 #ifdef CLIB_UNIX
446  if (f)
447  {
448  ret = fwrite (s, vec_len (s), 1, f);
449  }
450  else
451 #endif /* CLIB_UNIX */
452  {
453  ret = 0;
454  os_puts (s, vec_len (s), /* is_error */ 0);
455  }
456 
457  vec_free (s);
458  return ret;
459 }
460 
461 word
462 fformat (FILE * f, char *fmt, ...)
463 {
464  va_list va;
465  word ret;
466 
467  va_start (va, fmt);
468  ret = va_fformat (f, fmt, &va);
469  va_end (va);
470 
471  return (ret);
472 }
473 
474 #ifdef CLIB_UNIX
475 void
476 fformat_append_cr (FILE * ofp, const char *fmt, ...)
477 {
478  va_list va;
479 
480  va_start (va, fmt);
481  (void) va_fformat (ofp, (char *) fmt, &va);
482  va_end (va);
483  fformat (ofp, "\n");
484 }
485 
486 word
487 fdformat (int fd, char *fmt, ...)
488 {
489  word ret;
490  u8 *s;
491  va_list va;
492 
493  va_start (va, fmt);
494  s = va_format (0, fmt, &va);
495  va_end (va);
496 
497  ret = write (fd, s, vec_len (s));
498  vec_free (s);
499  return ret;
500 }
501 #endif
502 
503 /* Format integral type. */
504 static u8 *
506 {
507  u64 q;
508  u32 r;
509  u8 digit_buffer[128];
510  u8 *d = digit_buffer + sizeof (digit_buffer);
511  word c, base;
512 
513  if (options->is_signed && (i64) number < 0)
514  {
515  number = -number;
516  vec_add1 (s, '-');
517  }
518 
519  if (options->n_bits < BITS (number))
520  number &= ((u64) 1 << options->n_bits) - 1;
521 
522  base = options->base;
523 
524  while (1)
525  {
526  q = number / base;
527  r = number % base;
528 
529  if (r < 10 + 26 + 26)
530  {
531  if (r < 10)
532  c = '0' + r;
533  else if (r < 10 + 26)
534  c = 'a' + (r - 10);
535  else
536  c = 'A' + (r - 10 - 26);
537 
538  if (options->uppercase_digits
539  && base <= 10 + 26 && c >= 'a' && c <= 'z')
540  c += 'A' - 'a';
541 
542  *--d = c;
543  }
544  else /* will never happen, warning be gone */
545  {
546  *--d = '?';
547  }
548 
549  if (q == 0)
550  break;
551 
552  number = q;
553  }
554 
555  vec_add (s, d, digit_buffer + sizeof (digit_buffer) - d);
556  return s;
557 }
558 
559 /* Floating point formatting. */
560 /* Deconstruct IEEE 64 bit number into sign exponent and fraction. */
561 #define f64_down(f,sign,expon,fraction) \
562 do { \
563  union { u64 u; f64 f; } _f64_down_tmp; \
564  _f64_down_tmp.f = (f); \
565  (sign) = (_f64_down_tmp.u >> 63); \
566  (expon) = ((_f64_down_tmp.u >> 52) & 0x7ff) - 1023; \
567  (fraction) = ((_f64_down_tmp.u << 12) >> 12) | ((u64) 1 << 52); \
568 } while (0)
569 
570 /* Construct IEEE 64 bit number. */
571 static f64
572 f64_up (uword sign, word expon, u64 fraction)
573 {
574  union
575  {
576  u64 u;
577  f64 f;
578  } tmp;
579 
580  tmp.u = (u64) ((sign) != 0) << 63;
581 
582  expon += 1023;
583  if (expon > 1023)
584  expon = 1023;
585  if (expon < 0)
586  expon = 0;
587  tmp.u |= (u64) expon << 52;
588 
589  tmp.u |= fraction & (((u64) 1 << 52) - 1);
590 
591  return tmp.f;
592 }
593 
594 /* Returns approximate precision of number given its exponent. */
595 static f64
596 f64_precision (int base2_expon)
597 {
598  static int n_bits = 0;
599 
600  if (!n_bits)
601  {
602  /* Compute number of significant bits in floating point representation. */
603  f64 one = 0;
604  f64 small = 1;
605 
606  while (one != 1)
607  {
608  small *= .5;
609  n_bits++;
610  one = 1 + small;
611  }
612  }
613 
614  return f64_up (0, base2_expon - n_bits, 0);
615 }
616 
617 /* Return x 10^n */
618 static f64
620 {
621  if (n >= 0)
622  {
623  static f64 t[8] = { 1e+0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, };
624  while (n >= 8)
625  {
626  x *= 1e+8;
627  n -= 8;
628  }
629  return x * t[n];
630  }
631  else
632  {
633  static f64 t[8] = { 1e-0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, };
634  while (n <= -8)
635  {
636  x *= 1e-8;
637  n += 8;
638  }
639  return x * t[-n];
640  }
641 
642 }
643 
644 /* Write x = y * 10^expon with 1 < y < 10. */
645 static f64
646 normalize (f64 x, word * expon_return, f64 * prec_return)
647 {
648  word expon2, expon10;
649  CLIB_UNUSED (u64 fraction);
650  CLIB_UNUSED (word sign);
651  f64 prec;
652 
653  f64_down (x, sign, expon2, fraction);
654 
655  expon10 =
656  .5 +
657  expon2 * .301029995663981195213738894724493 /* Log (2) / Log (10) */ ;
658 
659  prec = f64_precision (expon2);
660  x = times_power_of_ten (x, -expon10);
661  prec = times_power_of_ten (prec, -expon10);
662 
663  while (x < 1)
664  {
665  x *= 10;
666  prec *= 10;
667  expon10--;
668  }
669 
670  while (x > 10)
671  {
672  x *= .1;
673  prec *= .1;
674  expon10++;
675  }
676 
677  if (x + prec >= 10)
678  {
679  x = 1;
680  expon10++;
681  }
682 
683  *expon_return = expon10;
684  *prec_return = prec;
685 
686  return x;
687 }
688 
689 static u8 *
690 add_some_zeros (u8 * s, uword n_zeros)
691 {
692  while (n_zeros > 0)
693  {
694  vec_add1 (s, '0');
695  n_zeros--;
696  }
697  return s;
698 }
699 
700 /* Format a floating point number with the given number of fractional
701  digits (e.g. 1.2345 with 2 fraction digits yields "1.23") and output style. */
702 static u8 *
703 format_float (u8 * s, f64 x, uword n_fraction_digits, uword output_style)
704 {
705  f64 prec;
706  word sign, expon, n_fraction_done, added_decimal_point;
707  /* Position of decimal point relative to where we are. */
708  word decimal_point;
709 
710  /* Default number of digits to print when its not specified. */
711  if (n_fraction_digits == ~0)
712  n_fraction_digits = 7;
713  n_fraction_done = 0;
714  decimal_point = 0;
715  added_decimal_point = 0;
716  sign = expon = 0;
717 
718  /* Special case: zero. */
719  if (x == 0)
720  {
721  do_zero:
722  vec_add1 (s, '0');
723  goto done;
724  }
725 
726  if (x < 0)
727  {
728  x = -x;
729  sign = 1;
730  }
731 
732  /* Check for not-a-number. */
733  if (isnan (x))
734  return format (s, "%cNaN", sign ? '-' : '+');
735 
736  /* Check for infinity. */
737  if (isinf (x))
738  return format (s, "%cinfinity", sign ? '-' : '+');
739 
740  x = normalize (x, &expon, &prec);
741 
742  /* Not enough digits to print anything: so just print 0 */
743  if ((word) - expon > (word) n_fraction_digits
744  && (output_style == 'f' || (output_style == 'g')))
745  goto do_zero;
746 
747  if (sign)
748  vec_add1 (s, '-');
749 
750  if (output_style == 'f'
751  || (output_style == 'g' && expon > -10 && expon < 10))
752  {
753  if (expon < 0)
754  {
755  /* Add decimal point and leading zeros. */
756  vec_add1 (s, '.');
757  n_fraction_done = clib_min (-(expon + 1), n_fraction_digits);
758  s = add_some_zeros (s, n_fraction_done);
759  decimal_point = -n_fraction_done;
760  added_decimal_point = 1;
761  }
762  else
763  decimal_point = expon + 1;
764  }
765  else
766  {
767  /* Exponential output style. */
768  decimal_point = 1;
769  output_style = 'e';
770  }
771 
772  while (1)
773  {
774  uword digit;
775 
776  /* Number is smaller than precision: call it zero. */
777  if (x < prec)
778  break;
779 
780  digit = x;
781  x -= digit;
782  if (x + prec >= 1)
783  {
784  digit++;
785  x -= 1;
786  }
787 
788  /* Round last printed digit. */
789  if (decimal_point <= 0
790  && n_fraction_done + 1 == n_fraction_digits && digit < 9)
791  digit += x >= .5;
792 
793  vec_add1 (s, '0' + digit);
794 
795  /* Move rightwards towards/away from decimal point. */
796  decimal_point--;
797 
798  n_fraction_done += decimal_point < 0;
799  if (decimal_point <= 0 && n_fraction_done >= n_fraction_digits)
800  break;
801 
802  if (decimal_point == 0 && x != 0)
803  {
804  vec_add1 (s, '.');
805  added_decimal_point = 1;
806  }
807 
808  x *= 10;
809  prec *= 10;
810  }
811 
812 done:
813  if (decimal_point > 0)
814  {
815  s = add_some_zeros (s, decimal_point);
816  decimal_point = 0;
817  }
818 
819  if (n_fraction_done < n_fraction_digits)
820  {
821  if (!added_decimal_point)
822  vec_add1 (s, '.');
823  s = add_some_zeros (s, n_fraction_digits - n_fraction_done);
824  }
825 
826  if (output_style == 'e')
827  s = format (s, "e%wd", expon);
828 
829  return s;
830 }
831 
832 
833 /*
834  * fd.io coding-style-patch-verification: ON
835  *
836  * Local Variables:
837  * eval: (c-set-style "gnu")
838  * End:
839  */
#define clib_min(x, y)
Definition: clib.h:295
#define CLIB_UNUSED(x)
Definition: clib.h:82
word fdformat(int fd, char *fmt,...)
Definition: format.c:487
void os_puts(u8 *string, uword length, uword is_error)
Definition: unix-misc.c:191
Optimized string handling code, including c11-compliant "safe C library" variants.
unsigned long u64
Definition: types.h:89
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
static u8 * format_float(u8 *s, f64 x, uword n_digits_to_print, uword output_style)
Definition: format.c:703
void fformat_append_cr(FILE *ofp, const char *fmt,...)
Definition: format.c:476
int i
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
u8 * va_format(u8 *s, const char *fmt, va_list *va)
Definition: format.c:387
uword justify
Definition: format.c:90
unsigned char u8
Definition: types.h:56
double f64
Definition: types.h:142
static f64 times_power_of_ten(f64 x, int n)
Definition: format.c:619
#define isinf(x)
Definition: math.h:60
#define vec_add(V, E, N)
Add N elements to end of vector V (no header, unspecified alignment)
Definition: vec.h:598
static f64 f64_precision(int base2_expon)
Definition: format.c:596
i64 word
Definition: types.h:111
static u8 * format_integer(u8 *s, u64 number, format_integer_options_t *options)
Definition: format.c:505
#define vec_resize(V, N)
Resize a vector (no header, unspecified alignment) Add N elements to end of given vector V...
Definition: vec.h:242
unsigned int u32
Definition: types.h:88
#define vec_insert(V, N, M)
Insert N vector elements starting at element M, initialize new elements to zero (no header...
Definition: vec.h:685
static f64 f64_up(uword sign, word expon, u64 fraction)
Definition: format.c:572
signed long i64
Definition: types.h:78
word fformat(FILE *f, char *fmt,...)
Definition: format.c:462
u8 len
Definition: ip_types.api:49
svmdb_client_t * c
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
static f64 normalize(f64 x, word *expon_return, f64 *prec_return)
Definition: format.c:646
uword pad_char
Definition: format.c:100
static const u8 * do_percent(u8 **_s, const u8 *fmt, va_list *va)
Definition: format.c:155
#define ASSERT(truth)
#define isnan(x)
Definition: math.h:56
static u8 * justify(u8 *s, format_info_t *fi, uword s_len_orig)
Definition: format.c:104
static uword pointer_to_uword(const void *p)
Definition: types.h:131
uword how_long
Definition: format.c:97
uword width[2]
Definition: format.c:94
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
word va_fformat(FILE *f, char *fmt, va_list *va)
Definition: format.c:438
u64 uword
Definition: types.h:112
#define f64_down(f, sign, expon, fraction)
Definition: format.c:561
static u8 * add_some_zeros(u8 *s, uword n_zeros)
Definition: format.c:690
#define BITS(x)
Definition: clib.h:61
CLIB vectors are ubiquitous dynamically resized arrays with by user defined "headers".