+process_imports = False
+
+
+###############################################################################
+class ToJSON:
+ """Class to generate functions converting from VPP binary API to JSON."""
+
+ _dispatch = {}
+ noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
+ is_number = {
+ "u8": None,
+ "i8": None,
+ "u16": None,
+ "i16": None,
+ "u32": None,
+ "i32": None,
+ "u64": None,
+ "i64": None,
+ "f64": None,
+ }
+
+ def __init__(self, module, types, defines, imported_types, stream):
+ self.stream = stream
+ self.module = module
+ self.defines = defines
+ self.types = types
+ self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
+ self.defines_hash = {d.name: d for d in defines}
+
+ def header(self):
+ """Output the top boilerplate."""
+ write = self.stream.write
+ write("#ifndef included_{}_api_tojson_h\n".format(self.module))
+ write("#define included_{}_api_tojson_h\n".format(self.module))
+ write("#include <vppinfra/cJSON.h>\n\n")
+ write("#include <vppinfra/jsonformat.h>\n\n")
+ if self.module == "interface_types":
+ write("#define vl_printfun\n")
+ write("#include <vnet/interface_types.api.h>\n\n")
+
+ def footer(self):
+ """Output the bottom boilerplate."""
+ write = self.stream.write
+ write("#endif\n")
+
+ def get_base_type(self, t):
+ vt_type = None
+ try:
+ vt = self.types_hash[t]
+ if vt.type == "Using" and "length" not in vt.alias:
+ vt_type = vt.alias["type"]
+ except KeyError:
+ vt = t
+ return vt, vt_type
+
+ def get_json_func(self, t):
+ """Given the type, returns the function to use to create a
+ cJSON object"""
+ vt, vt_type = self.get_base_type(t)
+
+ if t in self.is_number or vt_type in self.is_number:
+ return "cJSON_AddNumberToObject", "", False
+ if t == "bool":
+ return "cJSON_AddBoolToObject", "", False
+
+ # Lookup type name check if it's enum
+ if vt.type == "Enum" or vt.type == "EnumFlag":
+ return "{t}_tojson".format(t=t), "", True
+ return "{t}_tojson".format(t=t), "&", True
+
+ def get_json_array_func(self, t):
+ """Given a type returns the function to create a cJSON object
+ for arrays."""
+ if t in self.is_number:
+ return "cJSON_CreateNumber", ""
+ if t == "bool":
+ return "cJSON_CreateBool", ""
+ vt, vt_type = self.get_base_type(t)
+ if vt.type == "Enum" or vt.type == "EnumFlag":
+ return "{t}_tojson".format(t=t), ""
+ return "{t}_tojson".format(t=t), "&"
+
+ def print_string(self, o):
+ """Create cJSON object from vl_api_string_t"""
+ write = self.stream.write
+ if o.modern_vla:
+ write(
+ ' vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'.format(
+ n=o.fieldname
+ )
+ )
+ else:
+
+ write(
+ ' cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'.format(
+ n=o.fieldname
+ )
+ )
+
+ def print_field(self, o):
+ """Called for every field in a typedef or define."""
+ write = self.stream.write
+ if o.fieldname in self.noprint_fields:
+ return
+
+ f, p, newobj = self.get_json_func(o.fieldtype)
+
+ if newobj:
+ write(
+ ' cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'.format(
+ f=f, p=p, n=o.fieldname
+ )
+ )
+ else:
+ write(' {f}(o, "{n}", {p}a->{n});\n'.format(f=f, p=p, n=o.fieldname))
+
+ _dispatch["Field"] = print_field
+
+ def print_array(self, o):
+ """Converts a VPP API array to cJSON array."""
+ write = self.stream.write
+
+ forloop = """\
+ {{
+ int i;
+ cJSON *array = cJSON_AddArrayToObject(o, "{n}");
+ for (i = 0; i < {lfield}; i++) {{
+ cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
+ }}
+ }}
+"""
+
+ if o.fieldtype == "string":
+ self.print_string(o)
+ return
+
+ lfield = "a->" + o.lengthfield if o.lengthfield else o.length
+ if o.fieldtype == "u8":
+ write(" {\n")
+ # What is length field doing here?
+ write(
+ ' u8 *s = format(0, "0x%U", format_hex_bytes, '
+ "&a->{n}, {lfield});\n".format(n=o.fieldname, lfield=lfield)
+ )
+ write(
+ ' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'.format(
+ n=o.fieldname
+ )
+ )
+ write(" vec_free(s);\n")
+ write(" }\n")
+ return
+
+ f, p = self.get_json_array_func(o.fieldtype)
+ write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname, f=f, p=p))
+
+ _dispatch["Array"] = print_array
+
+ def print_enum(self, o):
+ """Create cJSON object (string) for VPP API enum"""
+ write = self.stream.write
+ write(
+ "static inline cJSON *vl_api_{name}_t_tojson "
+ "(vl_api_{name}_t a) {{\n".format(name=o.name)
+ )
+
+ write(" switch(a) {\n")
+ for b in o.block:
+ write(" case %s:\n" % b[1])
+ write(' return cJSON_CreateString("{}");\n'.format(b[0]))
+ write(' default: return cJSON_CreateString("Invalid ENUM");\n')
+ write(" }\n")
+ write(" return 0;\n")
+ write("}\n")
+
+ _dispatch["Enum"] = print_enum
+
+ def print_enum_flag(self, o):
+ """Create cJSON object (string) for VPP API enum"""
+ write = self.stream.write
+ write(
+ "static inline cJSON *vl_api_{name}_t_tojson "
+ "(vl_api_{name}_t a) {{\n".format(name=o.name)
+ )
+ write(" cJSON *array = cJSON_CreateArray();\n")
+
+ for b in o.block:
+ if b[1] == 0:
+ continue
+ write(" if (a & {})\n".format(b[0]))
+ write(
+ ' cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(
+ b[0]
+ )
+ )
+ write(" return array;\n")
+ write("}\n")
+
+ _dispatch["EnumFlag"] = print_enum_flag
+
+ def print_typedef(self, o):
+ """Create cJSON (dictionary) object from VPP API typedef"""
+ write = self.stream.write
+ write(
+ "static inline cJSON *vl_api_{name}_t_tojson "
+ "(vl_api_{name}_t *a) {{\n".format(name=o.name)
+ )
+ write(" cJSON *o = cJSON_CreateObject();\n")
+
+ for t in o.block:
+ self._dispatch[t.type](self, t)
+
+ write(" return o;\n")
+ write("}\n")
+
+ def print_define(self, o):
+ """Create cJSON (dictionary) object from VPP API define"""
+ write = self.stream.write
+ write(
+ "static inline cJSON *vl_api_{name}_t_tojson "
+ "(vl_api_{name}_t *a) {{\n".format(name=o.name)
+ )
+ write(" cJSON *o = cJSON_CreateObject();\n")
+ write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n'.format(o.name))
+ write(
+ ' cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'.format(crc=o.crc)
+ )
+
+ for t in o.block:
+ self._dispatch[t.type](self, t)
+
+ write(" return o;\n")
+ write("}\n")
+
+ def print_using(self, o):
+ """Create cJSON (dictionary) object from VPP API aliased type"""
+ if o.manual_print:
+ return
+
+ write = self.stream.write
+ write(
+ "static inline cJSON *vl_api_{name}_t_tojson "
+ "(vl_api_{name}_t *a) {{\n".format(name=o.name)
+ )
+
+ write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'.format(o.name))
+ write(" cJSON *o = cJSON_CreateString((char *)s);\n")
+ write(" vec_free(s);\n")
+ write(" return o;\n")
+ write("}\n")
+
+ _dispatch["Typedef"] = print_typedef
+ _dispatch["Define"] = print_define
+ _dispatch["Using"] = print_using
+ _dispatch["Union"] = print_typedef
+
+ def generate_function(self, t):
+ """Main entry point"""
+ write = self.stream.write
+ if t.manual_print:
+ write("/* Manual print {} */\n".format(t.name))
+ return
+ self._dispatch[t.type](self, t)
+
+ def generate_types(self):
+ """Main entry point"""
+ for t in self.types:
+ self.generate_function(t)
+
+ def generate_defines(self):
+ """Main entry point"""
+ for t in self.defines:
+ self.generate_function(t)
+
+
+class FromJSON:
+ """
+ Parse JSON objects into VPP API binary message structures.
+ """
+
+ _dispatch = {}
+ noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
+ is_number = {
+ "u8": None,
+ "i8": None,
+ "u16": None,
+ "i16": None,
+ "u32": None,
+ "i32": None,
+ "u64": None,
+ "i64": None,
+ "f64": None,
+ }
+
+ def __init__(self, module, types, defines, imported_types, stream):
+ self.stream = stream
+ self.module = module
+ self.defines = defines
+ self.types = types
+ self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
+ self.defines_hash = {d.name: d for d in defines}
+
+ def header(self):
+ """Output the top boilerplate."""
+ write = self.stream.write
+ write("#ifndef included_{}_api_fromjson_h\n".format(self.module))
+ write("#define included_{}_api_fromjson_h\n".format(self.module))
+ write("#include <vppinfra/cJSON.h>\n\n")
+ write("#include <vppinfra/jsonformat.h>\n\n")
+ write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
+
+ def is_base_type(self, t):
+ """Check if a type is one of the VPP API base types"""
+ if t in self.is_number:
+ return True
+ if t == "bool":
+ return True
+ return False
+
+ def footer(self):
+ """Output the bottom boilerplate."""
+ write = self.stream.write
+ write("#endif\n")
+
+ def print_string(self, o, toplevel=False):
+ """Convert JSON string to vl_api_string_t"""
+ write = self.stream.write
+
+ msgvar = "a" if toplevel else "*mp"
+ msgsize = "l" if toplevel else "*len"
+
+ if o.modern_vla:
+ write(" char *p = cJSON_GetStringValue(item);\n")
+ write(" size_t plen = strlen(p);\n")
+ write(
+ " {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n".format(
+ msgvar=msgvar, msgsize=msgsize
+ )
+ )
+ write(" if ({msgvar} == 0) goto error;\n".format(msgvar=msgvar))
+ write(
+ " vl_api_c_string_to_api_string(p, (void *){msgvar} + "
+ "{msgsize} - sizeof(vl_api_string_t));\n".format(
+ msgvar=msgvar, msgsize=msgsize
+ )
+ )
+ write(" {msgsize} += plen;\n".format(msgsize=msgsize))
+ else:
+ write(
+ " strncpy_s((char *)a->{n}, sizeof(a->{n}), "
+ "cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n".format(
+ n=o.fieldname
+ )
+ )
+
+ def print_field(self, o, toplevel=False):
+ """Called for every field in a typedef or define."""
+ write = self.stream.write
+ if o.fieldname in self.noprint_fields:
+ return
+ is_bt = self.is_base_type(o.fieldtype)
+ t = "vl_api_{}".format(o.fieldtype) if is_bt else o.fieldtype
+
+ msgvar = "(void **)&a" if toplevel else "mp"
+ msgsize = "&l" if toplevel else "len"
+
+ if is_bt:
+ write(
+ " vl_api_{t}_fromjson(item, &a->{n});\n".format(
+ t=o.fieldtype, n=o.fieldname
+ )
+ )
+ else:
+ write(
+ " if ({t}_fromjson({msgvar}, "
+ "{msgsize}, item, &a->{n}) < 0) goto error;\n".format(
+ t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize
+ )
+ )
+
+ _dispatch["Field"] = print_field
+
+ def print_array(self, o, toplevel=False):
+ """Convert JSON array to VPP API array"""
+ write = self.stream.write
+
+ forloop = """\
+ {{
+ int i;
+ cJSON *array = cJSON_GetObjectItem(o, "{n}");
+ int size = cJSON_GetArraySize(array);
+ if (size != {lfield}) goto error;
+ for (i = 0; i < size; i++) {{
+ cJSON *e = cJSON_GetArrayItem(array, i);
+ {call}
+ }}
+ }}
+"""
+ forloop_vla = """\
+ {{
+ int i;
+ cJSON *array = cJSON_GetObjectItem(o, "{n}");
+ int size = cJSON_GetArraySize(array);
+ {lfield} = size;
+ {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
+ {t} *d = (void *){realloc} + {msgsize};
+ {msgsize} += sizeof({t}) * size;
+ for (i = 0; i < size; i++) {{
+ cJSON *e = cJSON_GetArrayItem(array, i);
+ {call}
+ }}
+ }}
+"""
+ t = o.fieldtype
+ if o.fieldtype == "string":
+ self.print_string(o, toplevel)
+ return
+
+ lfield = "a->" + o.lengthfield if o.lengthfield else o.length
+ msgvar = "(void **)&a" if toplevel else "mp"
+ realloc = "a" if toplevel else "*mp"
+ msgsize = "l" if toplevel else "*len"
+
+ if o.fieldtype == "u8":
+ if o.lengthfield:
+ write(' s = u8string_fromjson(o, "{}");\n'.format(o.fieldname))
+ write(" if (!s) goto error;\n")
+ write(" {} = vec_len(s);\n".format(lfield))
+
+ write(
+ " {realloc} = cJSON_realloc({realloc}, {msgsize} + "
+ "vec_len(s), {msgsize});\n".format(
+ msgvar=msgvar, msgsize=msgsize, realloc=realloc
+ )
+ )
+ write(
+ " memcpy((void *){realloc} + {msgsize}, s, "
+ "vec_len(s));\n".format(realloc=realloc, msgsize=msgsize)
+ )
+ write(" {msgsize} += vec_len(s);\n".format(msgsize=msgsize))
+
+ write(" vec_free(s);\n")
+ else:
+ write(
+ ' if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'.format(
+ n=o.fieldname
+ )
+ )
+ return
+
+ is_bt = self.is_base_type(o.fieldtype)
+
+ if o.lengthfield:
+ if is_bt:
+ call = "vl_api_{t}_fromjson(e, &d[i]);".format(t=o.fieldtype)
+ else:
+ call = "if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; ".format(
+ t=o.fieldtype, msgvar=msgvar
+ )
+ write(
+ forloop_vla.format(
+ lfield=lfield,
+ t=o.fieldtype,
+ n=o.fieldname,
+ call=call,
+ realloc=realloc,
+ msgsize=msgsize,
+ )
+ )
+ else:
+ if is_bt:
+ call = "vl_api_{t}_fromjson(e, &a->{n}[i]);".format(t=t, n=o.fieldname)
+ else:
+ call = "if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;".format(
+ t, msgvar, o.fieldname
+ )
+ write(
+ forloop.format(
+ lfield=lfield,
+ t=t,
+ n=o.fieldname,
+ call=call,
+ msgvar=msgvar,
+ realloc=realloc,
+ msgsize=msgsize,
+ )
+ )
+
+ _dispatch["Array"] = print_array
+
+ def print_enum(self, o):
+ """Convert to JSON enum(string) to VPP API enum (int)"""
+ write = self.stream.write
+ write(
+ "static inline int vl_api_{n}_t_fromjson"
+ "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
+ )
+ write(" char *p = cJSON_GetStringValue(o);\n")
+ for b in o.block:
+ write(
+ ' if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'.format(
+ b[0], b[1]
+ )
+ )
+ write(" *a = 0;\n")
+ write(" return -1;\n")
+ write("}\n")
+
+ _dispatch["Enum"] = print_enum
+
+ def print_enum_flag(self, o):
+ """Convert to JSON enum(string) to VPP API enum (int)"""
+ write = self.stream.write
+ write(
+ "static inline int vl_api_{n}_t_fromjson "
+ "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
+ )
+ write(" int i;\n")
+ write(" *a = 0;\n")
+ write(" for (i = 0; i < cJSON_GetArraySize(o); i++) {\n")
+ write(" cJSON *e = cJSON_GetArrayItem(o, i);\n")
+ write(" char *p = cJSON_GetStringValue(e);\n")
+ write(" if (!p) return -1;\n")
+ for b in o.block:
+ write(' if (strcmp(p, "{}") == 0) *a |= {};\n'.format(b[0], b[1]))
+ write(" }\n")
+ write(" return 0;\n")
+ write("}\n")
+
+ _dispatch["EnumFlag"] = print_enum_flag
+
+ def print_typedef(self, o):
+ """Convert from JSON object to VPP API binary representation"""
+ write = self.stream.write
+
+ write(
+ "static inline int vl_api_{name}_t_fromjson (void **mp, "
+ "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
+ )
+ write(" cJSON *item __attribute__ ((unused));\n")
+ write(" u8 *s __attribute__ ((unused));\n")
+ for t in o.block:
+ if t.type == "Field" and t.is_lengthfield:
+ continue
+ write('\n item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
+ write(" if (!item) goto error;\n")
+ self._dispatch[t.type](self, t)
+
+ write("\n return 0;\n")
+ write("\n error:\n")
+ write(" return -1;\n")
+ write("}\n")
+
+ def print_union(self, o):
+ """Convert JSON object to VPP API binary union"""
+ write = self.stream.write
+
+ write(
+ "static inline int vl_api_{name}_t_fromjson (void **mp, "
+ "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
+ )
+ write(" cJSON *item __attribute__ ((unused));\n")
+ write(" u8 *s __attribute__ ((unused));\n")
+ for t in o.block:
+ if t.type == "Field" and t.is_lengthfield:
+ continue
+ write(' item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
+ write(" if (item) {\n")
+ self._dispatch[t.type](self, t)
+ write(" };\n")
+ write("\n return 0;\n")
+ write("\n error:\n")
+ write(" return -1;\n")
+ write("}\n")
+
+ def print_define(self, o):
+ """Convert JSON object to VPP API message"""
+ write = self.stream.write
+ error = 0
+ write(
+ "static inline vl_api_{name}_t *vl_api_{name}_t_fromjson "
+ "(cJSON *o, int *len) {{\n".format(name=o.name)
+ )
+ write(" cJSON *item __attribute__ ((unused));\n")
+ write(" u8 *s __attribute__ ((unused));\n")
+ write(" int l = sizeof(vl_api_{}_t);\n".format(o.name))
+ write(" vl_api_{}_t *a = cJSON_malloc(l);\n".format(o.name))
+ write("\n")
+
+ for t in o.block:
+ if t.fieldname in self.noprint_fields:
+ continue
+ if t.type == "Field" and t.is_lengthfield:
+ continue
+ write(' item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
+ write(" if (!item) goto error;\n")
+ error += 1
+ self._dispatch[t.type](self, t, toplevel=True)
+ write("\n")
+
+ write(" *len = l;\n")
+ write(" return a;\n")
+
+ if error:
+ write("\n error:\n")
+ write(" cJSON_free(a);\n")
+ write(" return 0;\n")
+ write("}\n")
+
+ def print_using(self, o):
+ """Convert JSON field to VPP type alias"""
+ write = self.stream.write
+
+ if o.manual_print:
+ return
+
+ t = o.using
+ write(
+ "static inline int vl_api_{name}_t_fromjson (void **mp, "
+ "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
+ )
+ if "length" in o.alias:
+ if t.fieldtype != "u8":
+ raise ValueError(
+ "Error in processing type {} for {}".format(t.fieldtype, o.name)
+ )
+ write(
+ " vl_api_u8_string_fromjson(o, (u8 *)a, {});\n".format(
+ o.alias["length"]
+ )
+ )
+ else:
+ write(" vl_api_{t}_fromjson(o, ({t} *)a);\n".format(t=t.fieldtype))
+
+ write(" return 0;\n")
+ write("}\n")
+
+ _dispatch["Typedef"] = print_typedef
+ _dispatch["Define"] = print_define
+ _dispatch["Using"] = print_using
+ _dispatch["Union"] = print_union
+
+ def generate_function(self, t):
+ """Main entry point"""
+ write = self.stream.write
+ if t.manual_print:
+ write("/* Manual print {} */\n".format(t.name))
+ return
+ self._dispatch[t.type](self, t)
+
+ def generate_types(self):
+ """Main entry point"""
+ for t in self.types:
+ self.generate_function(t)
+
+ def generate_defines(self):
+ """Main entry point"""
+ for t in self.defines:
+ self.generate_function(t)
+
+
+def generate_tojson(s, modulename, stream):
+ """Generate all functions to convert from API to JSON"""
+ write = stream.write
+
+ write("/* Imported API files */\n")
+ for i in s["Import"]:
+ f = i.filename.replace("plugins/", "")
+ write("#include <{}_tojson.h>\n".format(f))
+
+ pp = ToJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
+ pp.header()
+ pp.generate_types()
+ pp.generate_defines()
+ pp.footer()
+ return ""
+
+
+def generate_fromjson(s, modulename, stream):
+ """Generate all functions to convert from JSON to API"""
+ write = stream.write
+ write("/* Imported API files */\n")
+ for i in s["Import"]:
+ f = i.filename.replace("plugins/", "")
+ write("#include <{}_fromjson.h>\n".format(f))
+
+ pp = FromJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
+ pp.header()
+ pp.generate_types()
+ pp.generate_defines()
+ pp.footer()
+
+ return ""
+
+
+###############################################################################
+
+
+DATESTRING = datetime.datetime.utcfromtimestamp(
+ int(os.environ.get("SOURCE_DATE_EPOCH", time.time()))
+)
+TOP_BOILERPLATE = """\