summaryrefslogtreecommitdiffstats
path: root/src/vat/json_format.c
blob: 208fdd1442f7527bc1ab1220c6dd4ea697e118ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .
/*
 *------------------------------------------------------------------
 * json_format.c
 *
 * 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.
 *------------------------------------------------------------------

*/
#include <inttypes.h>
#include <vat/json_format.h>
#include <vnet/ip/ip.h>
#include <vppinfra/vec.h>

#define VAT_TAB_WIDTH               2

typedef struct vat_print_ctx_s
{
  FILE *ofp;
  u32 indent;
} vat_print_ctx_t;

/* Format an IP4 address. */
static u8 *
vat_json_format_ip4_address (u8 * s, va_list * args)
{
  u8 *a = va_arg (*args, u8 *);
  return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
}

/* Format an IP6 address. */
static u8 *
vat_json_format_ip6_address (u8 * s, va_list * args)
{
  ip6_address_t *a = va_arg (*args, ip6_address_t *);
  u32 i, i_max_n_zero, max_n_zeros, i_first_zero, n_zeros, last_double_colon;

  i_max_n_zero = ARRAY_LEN (a->as_u16);
  max_n_zeros = 0;
  i_first_zero = i_max_n_zero;
  n_zeros = 0;
  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
    {
      u32 is_zero = a->as_u16[i] == 0;
      if (is_zero && i_first_zero >= ARRAY_LEN (a->as_u16))
	{
	  i_first_zero = i;
	  n_zeros = 0;
	}
      n_zeros += is_zero;
      if ((!is_zero && n_zeros > max_n_zeros)
	  || (i + 1 >= ARRAY_LEN (a->as_u16) && n_zeros > max_n_zeros))
	{
	  i_max_n_zero = i_first_zero;
	  max_n_zeros = n_zeros;
	  i_first_zero = ARRAY_LEN (a->as_u16);
	  n_zeros = 0;
	}
    }

  last_double_colon = 0;
  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
    {
      if (i == i_max_n_zero && max_n_zeros > 1)
	{
	  s = format (s, "::");
	  i += max_n_zeros - 1;
	  last_double_colon = 1;
	}
      else
	{
	  s = format (s, "%s%x",
		      (last_double_colon || i == 0) ? "" : ":",
		      clib_net_to_host_u16 (a->as_u16[i]));
	  last_double_colon = 0;
	}
    }

  return s;
}

static void
vat_json_indent_print (vat_print_ctx_t * ctx)
{
  int i;
  for (i = 0; i < ctx->indent * VAT_TAB_WIDTH; i++)
    {
      fformat (ctx->ofp, " ");
    }
}

static void
vat_json_indent_line (vat_print_ctx_t * ctx, char *fmt, ...)
{
  va_list va;

  vat_json_indent_print (ctx);
  va_start (va, fmt);
  va_fformat (ctx->ofp, fmt, &va);
  va_end (va);
}

static u8
is_num_only (vat_json_node_t * p)
{
  vat_json_node_t *elem;
  vec_foreach (elem, p)
  {
    if (VAT_JSON_INT != elem->type && VAT_JSON_UINT != elem->type)
      {
	return 0;
      }
  }
  return 1;
}

static void
vat_json_print_internal (vat_print_ctx_t * ctx, vat_json_node_t * node)
{
#define P(fmt,...) fformat(ctx->ofp, fmt, ##__VA_ARGS__)
#define PL(fmt,...) fformat(ctx->ofp, fmt"\n", ##__VA_ARGS__)
#define PPL(fmt,...) vat_json_indent_line(ctx, fmt"\n", ##__VA_ARGS__)
#define PP(fmt,...) vat_json_indent_line(ctx, fmt, ##__VA_ARGS__)
#define INCR (ctx->indent++)
#define DECR (ctx->indent--)

  vat_json_pair_t *pair;
  u32 i, count;
  vat_json_node_t *elem;
  u8 num_only = 0;

  if (!node)
    {
      return;
    }

  switch (node->type)
    {
    case VAT_JSON_OBJECT:
      count = vec_len (node->pairs);
      if (count >= 1)
	{
	  PL ("{");
	  INCR;
	  for (i = 0; i < count; i++)
	    {
	      pair = &node->pairs[i];
	      PP ("\"%s\": ", pair->name);
	      vat_json_print_internal (ctx, &pair->value);
	      if (i < count - 1)
		{
		  P (",");
		}
	      PL ();
	    }
	  DECR;
	  PP ("}");
	}
      else
	{
	  P ("{}");
	}
      break;
    case VAT_JSON_ARRAY:
      num_only = is_num_only (node->array);
      count = vec_len (node->array);
      if (count >= 1)
	{
	  if (num_only)
	    P ("[");
	  else
	    PL ("[ ");
	  INCR;
	  for (i = 0; i < count; i++)
	    {
	      elem = &node->array[i];
	      if (!num_only)
		{
		  vat_json_indent_print (ctx);
		}
	      vat_json_print_internal (ctx, elem);
	      if (i < count - 1)
		{
		  if (num_only)
		    {
		      P (", ");
		    }
		  else
		    {
		      P (",");
		    }
		}
	      if (!num_only)
		PL ();
	    }
	  DECR;
	  if (!num_only)
	    PP ("]");
	  else
	    P ("]");
	}
      else
	{
	  P ("[]");
	}
      break;
    case VAT_JSON_INT:
      P ("%d", node->sint);
      break;
    case VAT_JSON_UINT:
      P ("%" PRIu64, node->uint);
      break;
    case VAT_JSON_REAL:
      P ("%f", node->real);
      break;
    case VAT_JSON_STRING:
      P ("\"%s\"", node->string);
      break;
    case VAT_JSON_IPV4:
      P ("\"%U\"", vat_json_format_ip4_address, &node->ip4);
      break;
    case VAT_JSON_IPV6:
      P ("\"%U\"", vat_json_format_ip6_address, &node->ip6);
      break;
    default:
      break;
    }
#undef PPL
#undef PP
#undef PL
#undef P
}

void
vat_json_print (FILE * ofp, vat_json_node_t * node)
{
  vat_print_ctx_t ctx;
  clib_memset (&ctx, 0, sizeof ctx);
  ctx.indent = 0;
  ctx.ofp = ofp;
  fformat (ofp, "\n");
  vat_json_print_internal (&ctx, node);
  fformat (ofp, "\n");
}

void
vat_json_free (vat_json_node_t * node)
{
  int i = 0;

  if (NULL == node)
    {
      return;
    }
  switch (node->type)
    {
    case VAT_JSON_OBJECT:
      for (i = 0; i < vec_len (node->pairs); i++)
	{
	  vat_json_free (&node->pairs[i].value);
	}
      if (NULL != node->pairs)
	{
	  vec_free (node->pairs);
	}
      break;
    case VAT_JSON_ARRAY:
      for (i = 0; i < vec_len (node->array); i++)
	{
	  vat_json_free (&node->array[i]);
	}
      if (NULL != node->array)
	{
	  vec_free (node->array);
	}
      break;
    case VAT_JSON_STRING:
      if (NULL != node->string)
	{
	  vec_free (node->string);
	}
      break;
    default:
      break;
    }
}

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */