5ca2ed51ff6b81ef38eb1b2b63cb44430de3001a
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
1 #
2 # Copyright (c) 2020 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 #
17 # Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
18 # binary API format
19 #
20
21 """
22 This module creates C code for core VPP, VPP plugins and client side VAT and
23 VAT2 tests.
24 """
25
26 import datetime
27 import itertools
28 import os
29 import time
30 import sys
31 from io import StringIO
32 import shutil
33
34 process_imports = False
35
36
37 ###############################################################################
38 class ToJSON:
39     """Class to generate functions converting from VPP binary API to JSON."""
40
41     _dispatch = {}
42     noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
43     is_number = {
44         "u8": None,
45         "i8": None,
46         "u16": None,
47         "i16": None,
48         "u32": None,
49         "i32": None,
50         "u64": None,
51         "i64": None,
52         "f64": None,
53     }
54
55     def __init__(self, module, types, defines, imported_types, stream):
56         self.stream = stream
57         self.module = module
58         self.defines = defines
59         self.types = types
60         self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
61         self.defines_hash = {d.name: d for d in defines}
62
63     def header(self):
64         """Output the top boilerplate."""
65         write = self.stream.write
66         write("#ifndef included_{}_api_tojson_h\n".format(self.module))
67         write("#define included_{}_api_tojson_h\n".format(self.module))
68         write("#include <vppinfra/cJSON.h>\n\n")
69         write("#include <vppinfra/jsonformat.h>\n\n")
70         if self.module == "interface_types":
71             write("#define vl_printfun\n")
72             write("#include <vnet/interface_types.api.h>\n\n")
73
74     def footer(self):
75         """Output the bottom boilerplate."""
76         write = self.stream.write
77         write("#endif\n")
78
79     def get_base_type(self, t):
80         vt_type = None
81         try:
82             vt = self.types_hash[t]
83             if vt.type == "Using" and "length" not in vt.alias:
84                 vt_type = vt.alias["type"]
85         except KeyError:
86             vt = t
87         return vt, vt_type
88
89     def get_json_func(self, t):
90         """Given the type, returns the function to use to create a
91         cJSON object"""
92         vt, vt_type = self.get_base_type(t)
93
94         if t in self.is_number or vt_type in self.is_number:
95             return "cJSON_AddNumberToObject", "", False
96         if t == "bool":
97             return "cJSON_AddBoolToObject", "", False
98
99         # Lookup type name check if it's enum
100         if vt.type == "Enum" or vt.type == "EnumFlag":
101             return "{t}_tojson".format(t=t), "", True
102         return "{t}_tojson".format(t=t), "&", True
103
104     def get_json_array_func(self, t):
105         """Given a type returns the function to create a cJSON object
106         for arrays."""
107         if t in self.is_number:
108             return "cJSON_CreateNumber", ""
109         if t == "bool":
110             return "cJSON_CreateBool", ""
111         vt, vt_type = self.get_base_type(t)
112         if vt.type == "Enum" or vt.type == "EnumFlag":
113             return "{t}_tojson".format(t=t), ""
114         return "{t}_tojson".format(t=t), "&"
115
116     def print_string(self, o):
117         """Create cJSON object from vl_api_string_t"""
118         write = self.stream.write
119         if o.modern_vla:
120             write(
121                 '    vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'.format(
122                     n=o.fieldname
123                 )
124             )
125         else:
126
127             write(
128                 '    cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'.format(
129                     n=o.fieldname
130                 )
131             )
132
133     def print_field(self, o):
134         """Called for every field in a typedef or define."""
135         write = self.stream.write
136         if o.fieldname in self.noprint_fields:
137             return
138
139         f, p, newobj = self.get_json_func(o.fieldtype)
140
141         if newobj:
142             write(
143                 '    cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'.format(
144                     f=f, p=p, n=o.fieldname
145                 )
146             )
147         else:
148             write('    {f}(o, "{n}", {p}a->{n});\n'.format(f=f, p=p, n=o.fieldname))
149
150     _dispatch["Field"] = print_field
151
152     def print_array(self, o):
153         """Converts a VPP API array to cJSON array."""
154         write = self.stream.write
155
156         forloop = """\
157     {{
158         int i;
159         cJSON *array = cJSON_AddArrayToObject(o, "{n}");
160         for (i = 0; i < {lfield}; i++) {{
161             cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
162         }}
163     }}
164 """
165
166         if o.fieldtype == "string":
167             self.print_string(o)
168             return
169
170         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
171         if o.fieldtype == "u8":
172             write("    {\n")
173             # What is length field doing here?
174             write(
175                 '    u8 *s = format(0, "0x%U", format_hex_bytes, '
176                 "&a->{n}, {lfield});\n".format(n=o.fieldname, lfield=lfield)
177             )
178             write(
179                 '    cJSON_AddStringToObject(o, "{n}", (char *)s);\n'.format(
180                     n=o.fieldname
181                 )
182             )
183             write("    vec_free(s);\n")
184             write("    }\n")
185             return
186
187         f, p = self.get_json_array_func(o.fieldtype)
188         write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname, f=f, p=p))
189
190     _dispatch["Array"] = print_array
191
192     def print_enum(self, o):
193         """Create cJSON object (string) for VPP API enum"""
194         write = self.stream.write
195         write(
196             "static inline cJSON *vl_api_{name}_t_tojson "
197             "(vl_api_{name}_t a) {{\n".format(name=o.name)
198         )
199
200         write("    switch(a) {\n")
201         for b in o.block:
202             write("    case %s:\n" % b[1])
203             write('        return cJSON_CreateString("{}");\n'.format(b[0]))
204         write('    default: return cJSON_CreateString("Invalid ENUM");\n')
205         write("    }\n")
206         write("    return 0;\n")
207         write("}\n")
208
209     _dispatch["Enum"] = print_enum
210
211     def print_enum_flag(self, o):
212         """Create cJSON object (string) for VPP API enum"""
213         write = self.stream.write
214         write(
215             "static inline cJSON *vl_api_{name}_t_tojson "
216             "(vl_api_{name}_t a) {{\n".format(name=o.name)
217         )
218         write("    cJSON *array = cJSON_CreateArray();\n")
219
220         for b in o.block:
221             if b[1] == 0:
222                 continue
223             write("    if (a & {})\n".format(b[0]))
224             write(
225                 '       cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(
226                     b[0]
227                 )
228             )
229         write("    return array;\n")
230         write("}\n")
231
232     _dispatch["EnumFlag"] = print_enum_flag
233
234     def print_typedef(self, o):
235         """Create cJSON (dictionary) object from VPP API typedef"""
236         write = self.stream.write
237         write(
238             "static inline cJSON *vl_api_{name}_t_tojson "
239             "(vl_api_{name}_t *a) {{\n".format(name=o.name)
240         )
241         write("    cJSON *o = cJSON_CreateObject();\n")
242
243         for t in o.block:
244             self._dispatch[t.type](self, t)
245
246         write("    return o;\n")
247         write("}\n")
248
249     def print_define(self, o):
250         """Create cJSON (dictionary) object from VPP API define"""
251         write = self.stream.write
252         write(
253             "static inline cJSON *vl_api_{name}_t_tojson "
254             "(vl_api_{name}_t *a) {{\n".format(name=o.name)
255         )
256         write("    cJSON *o = cJSON_CreateObject();\n")
257         write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'.format(o.name))
258         write(
259             '    cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'.format(crc=o.crc)
260         )
261
262         for t in o.block:
263             self._dispatch[t.type](self, t)
264
265         write("    return o;\n")
266         write("}\n")
267
268     def print_using(self, o):
269         """Create cJSON (dictionary) object from VPP API aliased type"""
270         if o.manual_print:
271             return
272
273         write = self.stream.write
274         write(
275             "static inline cJSON *vl_api_{name}_t_tojson "
276             "(vl_api_{name}_t *a) {{\n".format(name=o.name)
277         )
278
279         write('    u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'.format(o.name))
280         write("    cJSON *o = cJSON_CreateString((char *)s);\n")
281         write("    vec_free(s);\n")
282         write("    return o;\n")
283         write("}\n")
284
285     _dispatch["Typedef"] = print_typedef
286     _dispatch["Define"] = print_define
287     _dispatch["Using"] = print_using
288     _dispatch["Union"] = print_typedef
289
290     def generate_function(self, t):
291         """Main entry point"""
292         write = self.stream.write
293         if t.manual_print:
294             write("/* Manual print {} */\n".format(t.name))
295             return
296         self._dispatch[t.type](self, t)
297
298     def generate_types(self):
299         """Main entry point"""
300         for t in self.types:
301             self.generate_function(t)
302
303     def generate_defines(self):
304         """Main entry point"""
305         for t in self.defines:
306             self.generate_function(t)
307
308
309 class FromJSON:
310     """
311     Parse JSON objects into VPP API binary message structures.
312     """
313
314     _dispatch = {}
315     noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
316     is_number = {
317         "u8": None,
318         "i8": None,
319         "u16": None,
320         "i16": None,
321         "u32": None,
322         "i32": None,
323         "u64": None,
324         "i64": None,
325         "f64": None,
326     }
327
328     def __init__(self, module, types, defines, imported_types, stream):
329         self.stream = stream
330         self.module = module
331         self.defines = defines
332         self.types = types
333         self.types_hash = {"vl_api_" + d.name + "_t": d for d in types + imported_types}
334         self.defines_hash = {d.name: d for d in defines}
335
336     def header(self):
337         """Output the top boilerplate."""
338         write = self.stream.write
339         write("#ifndef included_{}_api_fromjson_h\n".format(self.module))
340         write("#define included_{}_api_fromjson_h\n".format(self.module))
341         write("#include <vppinfra/cJSON.h>\n\n")
342         write("#include <vppinfra/jsonformat.h>\n\n")
343         write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
344
345     def is_base_type(self, t):
346         """Check if a type is one of the VPP API base types"""
347         if t in self.is_number:
348             return True
349         if t == "bool":
350             return True
351         return False
352
353     def footer(self):
354         """Output the bottom boilerplate."""
355         write = self.stream.write
356         write("#endif\n")
357
358     def print_string(self, o, toplevel=False):
359         """Convert JSON string to vl_api_string_t"""
360         write = self.stream.write
361
362         msgvar = "a" if toplevel else "*mp"
363         msgsize = "l" if toplevel else "*len"
364
365         if o.modern_vla:
366             write("    char *p = cJSON_GetStringValue(item);\n")
367             write("    size_t plen = strlen(p);\n")
368             write(
369                 "    {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n".format(
370                     msgvar=msgvar, msgsize=msgsize
371                 )
372             )
373             write("    if ({msgvar} == 0) goto error;\n".format(msgvar=msgvar))
374             write(
375                 "    vl_api_c_string_to_api_string(p, (void *){msgvar} + "
376                 "{msgsize} - sizeof(vl_api_string_t));\n".format(
377                     msgvar=msgvar, msgsize=msgsize
378                 )
379             )
380             write("    {msgsize} += plen;\n".format(msgsize=msgsize))
381         else:
382             write(
383                 "    strncpy_s((char *)a->{n}, sizeof(a->{n}), "
384                 "cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n".format(
385                     n=o.fieldname
386                 )
387             )
388
389     def print_field(self, o, toplevel=False):
390         """Called for every field in a typedef or define."""
391         write = self.stream.write
392         if o.fieldname in self.noprint_fields:
393             return
394         is_bt = self.is_base_type(o.fieldtype)
395         t = "vl_api_{}".format(o.fieldtype) if is_bt else o.fieldtype
396
397         msgvar = "(void **)&a" if toplevel else "mp"
398         msgsize = "&l" if toplevel else "len"
399
400         if is_bt:
401             write(
402                 "    vl_api_{t}_fromjson(item, &a->{n});\n".format(
403                     t=o.fieldtype, n=o.fieldname
404                 )
405             )
406         else:
407             write(
408                 "    if ({t}_fromjson({msgvar}, "
409                 "{msgsize}, item, &a->{n}) < 0) goto error;\n".format(
410                     t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize
411                 )
412             )
413
414     _dispatch["Field"] = print_field
415
416     def print_array(self, o, toplevel=False):
417         """Convert JSON array to VPP API array"""
418         write = self.stream.write
419
420         forloop = """\
421     {{
422         int i;
423         cJSON *array = cJSON_GetObjectItem(o, "{n}");
424         int size = cJSON_GetArraySize(array);
425         if (size != {lfield}) goto error;
426         for (i = 0; i < size; i++) {{
427             cJSON *e = cJSON_GetArrayItem(array, i);
428             {call}
429         }}
430     }}
431 """
432         forloop_vla = """\
433     {{
434         int i;
435         cJSON *array = cJSON_GetObjectItem(o, "{n}");
436         int size = cJSON_GetArraySize(array);
437         {lfield} = size;
438         {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
439         {t} *d = (void *){realloc} + {msgsize};
440         {msgsize} += sizeof({t}) * size;
441         for (i = 0; i < size; i++) {{
442             cJSON *e = cJSON_GetArrayItem(array, i);
443             {call}
444         }}
445     }}
446 """
447         t = o.fieldtype
448         if o.fieldtype == "string":
449             self.print_string(o, toplevel)
450             return
451
452         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
453         msgvar = "(void **)&a" if toplevel else "mp"
454         realloc = "a" if toplevel else "*mp"
455         msgsize = "l" if toplevel else "*len"
456
457         if o.fieldtype == "u8":
458             if o.lengthfield:
459                 write('    s = u8string_fromjson(o, "{}");\n'.format(o.fieldname))
460                 write("    if (!s) goto error;\n")
461                 write("    {} = vec_len(s);\n".format(lfield))
462
463                 write(
464                     "    {realloc} = cJSON_realloc({realloc}, {msgsize} + "
465                     "vec_len(s), {msgsize});\n".format(
466                         msgvar=msgvar, msgsize=msgsize, realloc=realloc
467                     )
468                 )
469                 write(
470                     "    memcpy((void *){realloc} + {msgsize}, s, "
471                     "vec_len(s));\n".format(realloc=realloc, msgsize=msgsize)
472                 )
473                 write("    {msgsize} += vec_len(s);\n".format(msgsize=msgsize))
474
475                 write("    vec_free(s);\n")
476             else:
477                 write(
478                     '    if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'.format(
479                         n=o.fieldname
480                     )
481                 )
482             return
483
484         is_bt = self.is_base_type(o.fieldtype)
485
486         if o.lengthfield:
487             if is_bt:
488                 call = "vl_api_{t}_fromjson(e, &d[i]);".format(t=o.fieldtype)
489             else:
490                 call = "if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; ".format(
491                     t=o.fieldtype, msgvar=msgvar
492                 )
493             write(
494                 forloop_vla.format(
495                     lfield=lfield,
496                     t=o.fieldtype,
497                     n=o.fieldname,
498                     call=call,
499                     realloc=realloc,
500                     msgsize=msgsize,
501                 )
502             )
503         else:
504             if is_bt:
505                 call = "vl_api_{t}_fromjson(e, &a->{n}[i]);".format(t=t, n=o.fieldname)
506             else:
507                 call = "if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;".format(
508                     t, msgvar, o.fieldname
509                 )
510             write(
511                 forloop.format(
512                     lfield=lfield,
513                     t=t,
514                     n=o.fieldname,
515                     call=call,
516                     msgvar=msgvar,
517                     realloc=realloc,
518                     msgsize=msgsize,
519                 )
520             )
521
522     _dispatch["Array"] = print_array
523
524     def print_enum(self, o):
525         """Convert to JSON enum(string) to VPP API enum (int)"""
526         write = self.stream.write
527         write(
528             "static inline int vl_api_{n}_t_fromjson"
529             "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
530         )
531         write("    char *p = cJSON_GetStringValue(o);\n")
532         for b in o.block:
533             write(
534                 '    if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'.format(
535                     b[0], b[1]
536                 )
537             )
538         write("    *a = 0;\n")
539         write("    return -1;\n")
540         write("}\n")
541
542     _dispatch["Enum"] = print_enum
543
544     def print_enum_flag(self, o):
545         """Convert to JSON enum(string) to VPP API enum (int)"""
546         write = self.stream.write
547         write(
548             "static inline int vl_api_{n}_t_fromjson "
549             "(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n".format(n=o.name)
550         )
551         write("   int i;\n")
552         write("   *a = 0;\n")
553         write("   for (i = 0; i < cJSON_GetArraySize(o); i++) {\n")
554         write("       cJSON *e = cJSON_GetArrayItem(o, i);\n")
555         write("       char *p = cJSON_GetStringValue(e);\n")
556         write("       if (!p) return -1;\n")
557         for b in o.block:
558             write('       if (strcmp(p, "{}") == 0) *a |= {};\n'.format(b[0], b[1]))
559         write("    }\n")
560         write("   return 0;\n")
561         write("}\n")
562
563     _dispatch["EnumFlag"] = print_enum_flag
564
565     def print_typedef(self, o):
566         """Convert from JSON object to VPP API binary representation"""
567         write = self.stream.write
568
569         write(
570             "static inline int vl_api_{name}_t_fromjson (void **mp, "
571             "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
572         )
573         write("    cJSON *item __attribute__ ((unused));\n")
574         write("    u8 *s __attribute__ ((unused));\n")
575         for t in o.block:
576             if t.type == "Field" and t.is_lengthfield:
577                 continue
578             write('\n    item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
579             write("    if (!item) goto error;\n")
580             self._dispatch[t.type](self, t)
581
582         write("\n    return 0;\n")
583         write("\n  error:\n")
584         write("    return -1;\n")
585         write("}\n")
586
587     def print_union(self, o):
588         """Convert JSON object to VPP API binary union"""
589         write = self.stream.write
590
591         write(
592             "static inline int vl_api_{name}_t_fromjson (void **mp, "
593             "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
594         )
595         write("    cJSON *item __attribute__ ((unused));\n")
596         write("    u8 *s __attribute__ ((unused));\n")
597         for t in o.block:
598             if t.type == "Field" and t.is_lengthfield:
599                 continue
600             write('    item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
601             write("    if (item) {\n")
602             self._dispatch[t.type](self, t)
603             write("    };\n")
604         write("\n    return 0;\n")
605         write("\n  error:\n")
606         write("    return -1;\n")
607         write("}\n")
608
609     def print_define(self, o):
610         """Convert JSON object to VPP API message"""
611         write = self.stream.write
612         error = 0
613         write(
614             "static inline vl_api_{name}_t *vl_api_{name}_t_fromjson "
615             "(cJSON *o, int *len) {{\n".format(name=o.name)
616         )
617         write("    cJSON *item __attribute__ ((unused));\n")
618         write("    u8 *s __attribute__ ((unused));\n")
619         write("    int l = sizeof(vl_api_{}_t);\n".format(o.name))
620         write("    vl_api_{}_t *a = cJSON_malloc(l);\n".format(o.name))
621         write("\n")
622
623         for t in o.block:
624             if t.fieldname in self.noprint_fields:
625                 continue
626             if t.type == "Field" and t.is_lengthfield:
627                 continue
628             write('    item = cJSON_GetObjectItem(o, "{}");\n'.format(t.fieldname))
629             write("    if (!item) goto error;\n")
630             error += 1
631             self._dispatch[t.type](self, t, toplevel=True)
632             write("\n")
633
634         write("    *len = l;\n")
635         write("    return a;\n")
636
637         if error:
638             write("\n  error:\n")
639             write("    cJSON_free(a);\n")
640             write("    return 0;\n")
641         write("}\n")
642
643     def print_using(self, o):
644         """Convert JSON field to VPP type alias"""
645         write = self.stream.write
646
647         if o.manual_print:
648             return
649
650         t = o.using
651         write(
652             "static inline int vl_api_{name}_t_fromjson (void **mp, "
653             "int *len, cJSON *o, vl_api_{name}_t *a) {{\n".format(name=o.name)
654         )
655         if "length" in o.alias:
656             if t.fieldtype != "u8":
657                 raise ValueError(
658                     "Error in processing type {} for {}".format(t.fieldtype, o.name)
659                 )
660             write(
661                 "    vl_api_u8_string_fromjson(o, (u8 *)a, {});\n".format(
662                     o.alias["length"]
663                 )
664             )
665         else:
666             write("    vl_api_{t}_fromjson(o, ({t} *)a);\n".format(t=t.fieldtype))
667
668         write("    return 0;\n")
669         write("}\n")
670
671     _dispatch["Typedef"] = print_typedef
672     _dispatch["Define"] = print_define
673     _dispatch["Using"] = print_using
674     _dispatch["Union"] = print_union
675
676     def generate_function(self, t):
677         """Main entry point"""
678         write = self.stream.write
679         if t.manual_print:
680             write("/* Manual print {} */\n".format(t.name))
681             return
682         self._dispatch[t.type](self, t)
683
684     def generate_types(self):
685         """Main entry point"""
686         for t in self.types:
687             self.generate_function(t)
688
689     def generate_defines(self):
690         """Main entry point"""
691         for t in self.defines:
692             self.generate_function(t)
693
694
695 def generate_tojson(s, modulename, stream):
696     """Generate all functions to convert from API to JSON"""
697     write = stream.write
698
699     write("/* Imported API files */\n")
700     for i in s["Import"]:
701         f = i.filename.replace("plugins/", "")
702         write("#include <{}_tojson.h>\n".format(f))
703
704     pp = ToJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
705     pp.header()
706     pp.generate_types()
707     pp.generate_defines()
708     pp.footer()
709     return ""
710
711
712 def generate_fromjson(s, modulename, stream):
713     """Generate all functions to convert from JSON to API"""
714     write = stream.write
715     write("/* Imported API files */\n")
716     for i in s["Import"]:
717         f = i.filename.replace("plugins/", "")
718         write("#include <{}_fromjson.h>\n".format(f))
719
720     pp = FromJSON(modulename, s["types"], s["Define"], s["imported"]["types"], stream)
721     pp.header()
722     pp.generate_types()
723     pp.generate_defines()
724     pp.footer()
725
726     return ""
727
728
729 ###############################################################################
730
731
732 DATESTRING = datetime.datetime.utcfromtimestamp(
733     int(os.environ.get("SOURCE_DATE_EPOCH", time.time()))
734 )
735 TOP_BOILERPLATE = """\
736 /*
737  * VLIB API definitions {datestring}
738  * Input file: {input_filename}
739  * Automatically generated: please edit the input file NOT this file!
740  */
741
742 #include <stdbool.h>
743 #if defined(vl_msg_id)||defined(vl_union_id) \\
744     || defined(vl_printfun) ||defined(vl_endianfun) \\
745     || defined(vl_api_version)||defined(vl_typedefs) \\
746     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
747     || defined(vl_api_version_tuple) || defined(vl_calcsizefun)
748 /* ok, something was selected */
749 #else
750 #warning no content included from {input_filename}
751 #endif
752
753 #define VL_API_PACKED(x) x __attribute__ ((packed))
754 """
755
756 BOTTOM_BOILERPLATE = """\
757 /****** API CRC (whole file) *****/
758
759 #ifdef vl_api_version
760 vl_api_version({input_filename}, {file_crc:#08x})
761
762 #endif
763 """
764
765
766 def msg_ids(s):
767     """Generate macro to map API message id to handler"""
768     output = """\
769
770 /****** Message ID / handler enum ******/
771
772 #ifdef vl_msg_id
773 """
774
775     for t in s["Define"]:
776         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % (
777             t.name.upper(),
778             t.name,
779         )
780     output += "#endif"
781
782     return output
783
784
785 def msg_names(s):
786     """Generate calls to name mapping macro"""
787     output = """\
788
789 /****** Message names ******/
790
791 #ifdef vl_msg_name
792 """
793
794     for t in s["Define"]:
795         dont_trace = 0 if t.dont_trace else 1
796         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
797     output += "#endif"
798
799     return output
800
801
802 def msg_name_crc_list(s, suffix):
803     """Generate list of names to CRC mappings"""
804     output = """\
805
806 /****** Message name, crc list ******/
807
808 #ifdef vl_msg_name_crc_list
809 """
810     output += "#define foreach_vl_msg_name_crc_%s " % suffix
811
812     for t in s["Define"]:
813         output += "\\\n_(VL_API_%s, %s, %08x) " % (t.name.upper(), t.name, t.crc)
814     output += "\n#endif"
815
816     return output
817
818
819 def api2c(fieldtype):
820     """Map between API type names and internal VPP type names"""
821     mappingtable = {
822         "string": "vl_api_string_t",
823     }
824     if fieldtype in mappingtable:
825         return mappingtable[fieldtype]
826     return fieldtype
827
828
829 def typedefs(filename):
830     """Include in the main files to the types file"""
831     output = """\
832
833 /****** Typedefs ******/
834
835 #ifdef vl_typedefs
836 #include "{include}.api_types.h"
837 #endif
838 """.format(
839         include=filename
840     )
841     return output
842
843
844 FORMAT_STRINGS = {
845     "u8": "%u",
846     "bool": "%u",
847     "i8": "%d",
848     "u16": "%u",
849     "i16": "%d",
850     "u32": "%u",
851     "i32": "%ld",
852     "u64": "%llu",
853     "i64": "%lld",
854     "f64": "%.2f",
855 }
856
857
858 class Printfun:
859     """Functions for pretty printing VPP API messages"""
860
861     _dispatch = {}
862     noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
863
864     def __init__(self, stream):
865         self.stream = stream
866
867     @staticmethod
868     def print_string(o, stream):
869         """Pretty print a vl_api_string_t"""
870         write = stream.write
871         if o.modern_vla:
872             write("    if (vl_api_string_len(&a->{f}) > 0) {{\n".format(f=o.fieldname))
873             write(
874                 '        s = format(s, "\\n%U{f}: %U", '
875                 "format_white_space, indent, "
876                 "vl_api_format_string, (&a->{f}));\n".format(f=o.fieldname)
877             )
878             write("    } else {\n")
879             write(
880                 '        s = format(s, "\\n%U{f}:", '
881                 "format_white_space, indent);\n".format(f=o.fieldname)
882             )
883             write("    }\n")
884         else:
885             write(
886                 '    s = format(s, "\\n%U{f}: %s", '
887                 "format_white_space, indent, a->{f});\n".format(f=o.fieldname)
888             )
889
890     def print_field(self, o, stream):
891         """Pretty print API field"""
892         write = stream.write
893         if o.fieldname in self.noprint_fields:
894             return
895         if o.fieldtype in FORMAT_STRINGS:
896             f = FORMAT_STRINGS[o.fieldtype]
897             write(
898                 '    s = format(s, "\\n%U{n}: {f}", '
899                 "format_white_space, indent, a->{n});\n".format(n=o.fieldname, f=f)
900             )
901         else:
902             write(
903                 '    s = format(s, "\\n%U{n}: %U", '
904                 "format_white_space, indent, "
905                 "format_{t}, &a->{n}, indent);\n".format(n=o.fieldname, t=o.fieldtype)
906             )
907
908     _dispatch["Field"] = print_field
909
910     def print_array(self, o, stream):
911         """Pretty print API array"""
912         write = stream.write
913
914         forloop = """\
915     for (i = 0; i < {lfield}; i++) {{
916         s = format(s, "\\n%U{n}: %U",
917                    format_white_space, indent, format_{t}, &a->{n}[i], indent);
918     }}
919 """
920
921         forloop_format = """\
922     for (i = 0; i < {lfield}; i++) {{
923         s = format(s, "\\n%U{n}: {t}",
924                    format_white_space, indent, a->{n}[i]);
925     }}
926 """
927
928         if o.fieldtype == "string":
929             self.print_string(o, stream)
930             return
931
932         if o.fieldtype == "u8":
933             if o.lengthfield:
934                 write(
935                     '    s = format(s, "\\n%U{n}: %U", format_white_space, '
936                     "indent, format_hex_bytes, a->{n}, a->{lfield});\n".format(
937                         n=o.fieldname, lfield=o.lengthfield
938                     )
939                 )
940             else:
941                 write(
942                     '    s = format(s, "\\n%U{n}: %U", format_white_space, '
943                     "indent, format_hex_bytes, a, {lfield});\n".format(
944                         n=o.fieldname, lfield=o.length
945                     )
946                 )
947             return
948
949         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
950         if o.fieldtype in FORMAT_STRINGS:
951             write(
952                 forloop_format.format(
953                     lfield=lfield, t=FORMAT_STRINGS[o.fieldtype], n=o.fieldname
954                 )
955             )
956         else:
957             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
958
959     _dispatch["Array"] = print_array
960
961     @staticmethod
962     def print_alias(k, v, stream):
963         """Pretty print type alias"""
964         write = stream.write
965         if "length" in v.alias and v.alias["length"] and v.alias["type"] == "u8":
966             write(
967                 '    return format(s, "%U", format_hex_bytes, a, {});\n'.format(
968                     v.alias["length"]
969                 )
970             )
971         elif v.alias["type"] in FORMAT_STRINGS:
972             write(
973                 '    return format(s, "{}", *a);\n'.format(
974                     FORMAT_STRINGS[v.alias["type"]]
975                 )
976             )
977         else:
978             write('    return format(s, "{} (print not implemented)");\n'.format(k))
979
980     @staticmethod
981     def print_enum(o, stream):
982         """Pretty print API enum"""
983         write = stream.write
984         write("    switch(*a) {\n")
985         for b in o:
986             write("    case %s:\n" % b[1])
987             write('        return format(s, "{}");\n'.format(b[0]))
988         write("    }\n")
989
990     _dispatch["Enum"] = print_enum
991     _dispatch["EnumFlag"] = print_enum
992
993     def print_obj(self, o, stream):
994         """Entry point"""
995         write = stream.write
996
997         if o.type in self._dispatch:
998             self._dispatch[o.type](self, o, stream)
999         else:
1000             write(
1001                 '    s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1002                     o.type, o.fieldtype, o.fieldname
1003                 )
1004             )
1005
1006
1007 def printfun(objs, stream, modulename):
1008     """Main entry point for pretty print function generation"""
1009     write = stream.write
1010
1011     h = """\
1012 /****** Print functions *****/
1013 #ifdef vl_printfun
1014 #ifndef included_{module}_printfun
1015 #define included_{module}_printfun
1016
1017 #ifdef LP64
1018 #define _uword_fmt \"%lld\"
1019 #define _uword_cast (long long)
1020 #else
1021 #define _uword_fmt \"%ld\"
1022 #define _uword_cast long
1023 #endif
1024
1025 #include "{module}.api_tojson.h"
1026 #include "{module}.api_fromjson.h"
1027
1028 """
1029
1030     signature = """\
1031 static inline u8 *vl_api_{name}_t_format (u8 *s,  va_list *args)
1032 {{
1033     __attribute__((unused)) vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1034     u32 indent __attribute__((unused)) = 2;
1035     int i __attribute__((unused));
1036 """
1037
1038     h = h.format(module=modulename)
1039     write(h)
1040
1041     pp = Printfun(stream)
1042     for t in objs:
1043         if t.manual_print:
1044             write("/***** manual: vl_api_%s_t_format *****/\n\n" % t.name)
1045             continue
1046         write(signature.format(name=t.name, suffix=""))
1047         write("    /* Message definition: vl_api_{}_t: */\n".format(t.name))
1048         write('    s = format(s, "vl_api_%s_t:");\n' % t.name)
1049         for o in t.block:
1050             pp.print_obj(o, stream)
1051         write("    return s;\n")
1052         write("}\n\n")
1053
1054     write("\n#endif")
1055     write("\n#endif /* vl_printfun */\n")
1056
1057     return ""
1058
1059
1060 def printfun_types(objs, stream, modulename):
1061     """Pretty print API types"""
1062     write = stream.write
1063     pp = Printfun(stream)
1064
1065     h = """\
1066 /****** Print functions *****/
1067 #ifdef vl_printfun
1068 #ifndef included_{module}_printfun_types
1069 #define included_{module}_printfun_types
1070
1071 """
1072     h = h.format(module=modulename)
1073     write(h)
1074
1075     signature = """\
1076 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1077 {{
1078     vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1079     u32 indent __attribute__((unused)) = va_arg (*args, u32);
1080     int i __attribute__((unused));
1081     indent += 2;
1082 """
1083
1084     for t in objs:
1085         if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1086             write(signature.format(name=t.name))
1087             pp.print_enum(t.block, stream)
1088             write("    return s;\n")
1089             write("}\n\n")
1090             continue
1091
1092         if t.manual_print:
1093             write("/***** manual: vl_api_%s_t_format *****/\n\n" % t.name)
1094             continue
1095
1096         if t.__class__.__name__ == "Using":
1097             write(signature.format(name=t.name))
1098             pp.print_alias(t.name, t, stream)
1099             write("}\n\n")
1100             continue
1101
1102         write(signature.format(name=t.name))
1103         for o in t.block:
1104             pp.print_obj(o, stream)
1105
1106         write("    return s;\n")
1107         write("}\n\n")
1108
1109     write("\n#endif")
1110     write("\n#endif /* vl_printfun_types */\n")
1111
1112
1113 def generate_imports(imports):
1114     """Add #include matching the API import statements"""
1115     output = "/* Imported API files */\n"
1116     output += "#ifndef vl_api_version\n"
1117
1118     for i in imports:
1119         s = i.filename.replace("plugins/", "")
1120         output += "#include <{}.h>\n".format(s)
1121     output += "#endif\n"
1122     return output
1123
1124
1125 ENDIAN_STRINGS = {
1126     "u16": "clib_net_to_host_u16",
1127     "u32": "clib_net_to_host_u32",
1128     "u64": "clib_net_to_host_u64",
1129     "i16": "clib_net_to_host_i16",
1130     "i32": "clib_net_to_host_i32",
1131     "i64": "clib_net_to_host_i64",
1132     "f64": "clib_net_to_host_f64",
1133 }
1134
1135
1136 def endianfun_array(o):
1137     """Generate endian functions for arrays"""
1138     forloop = """\
1139     for (i = 0; i < {length}; i++) {{
1140         a->{name}[i] = {format}(a->{name}[i]);
1141     }}
1142 """
1143
1144     forloop_format = """\
1145     for (i = 0; i < {length}; i++) {{
1146         {type}_endian(&a->{name}[i]);
1147     }}
1148 """
1149
1150     output = ""
1151     if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool":
1152         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1153     else:
1154         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
1155         if o.fieldtype in ENDIAN_STRINGS:
1156             output += forloop.format(
1157                 length=lfield, format=ENDIAN_STRINGS[o.fieldtype], name=o.fieldname
1158             )
1159         else:
1160             output += forloop_format.format(
1161                 length=lfield, type=o.fieldtype, name=o.fieldname
1162             )
1163     return output
1164
1165
1166 NO_ENDIAN_CONVERSION = {"client_index": None}
1167
1168
1169 def endianfun_obj(o):
1170     """Generate endian conversion function for type"""
1171     output = ""
1172     if o.type == "Array":
1173         return endianfun_array(o)
1174     if o.type != "Field":
1175         output += '    s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1176             o.type, o.fieldtype, o.fieldname
1177         )
1178         return output
1179     if o.fieldname in NO_ENDIAN_CONVERSION:
1180         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1181         return output
1182     if o.fieldtype in ENDIAN_STRINGS:
1183         output += "    a->{name} = {format}(a->{name});\n".format(
1184             name=o.fieldname, format=ENDIAN_STRINGS[o.fieldtype]
1185         )
1186     elif o.fieldtype.startswith("vl_api_"):
1187         output += "    {type}_endian(&a->{name});\n".format(
1188             type=o.fieldtype, name=o.fieldname
1189         )
1190     else:
1191         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1192
1193     return output
1194
1195
1196 def endianfun(objs, modulename):
1197     """Main entry point for endian function generation"""
1198     output = """\
1199
1200 /****** Endian swap functions *****/\n\
1201 #ifdef vl_endianfun
1202 #ifndef included_{module}_endianfun
1203 #define included_{module}_endianfun
1204
1205 #undef clib_net_to_host_uword
1206 #ifdef LP64
1207 #define clib_net_to_host_uword clib_net_to_host_u64
1208 #else
1209 #define clib_net_to_host_uword clib_net_to_host_u32
1210 #endif
1211
1212 """
1213     output = output.format(module=modulename)
1214
1215     signature = """\
1216 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1217 {{
1218     int i __attribute__((unused));
1219 """
1220
1221     for t in objs:
1222         if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1223             output += signature.format(name=t.name)
1224             if t.enumtype in ENDIAN_STRINGS:
1225                 output += "    *a = {}(*a);\n".format(ENDIAN_STRINGS[t.enumtype])
1226             else:
1227                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
1228                     name=t.name
1229                 )
1230
1231             output += "}\n\n"
1232             continue
1233
1234         if t.manual_endian:
1235             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
1236             continue
1237
1238         if t.__class__.__name__ == "Using":
1239             output += signature.format(name=t.name)
1240             if "length" in t.alias and t.alias["length"] and t.alias["type"] == "u8":
1241                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
1242                     name=t.name
1243                 )
1244             elif t.alias["type"] in FORMAT_STRINGS:
1245                 output += "    *a = {}(*a);\n".format(ENDIAN_STRINGS[t.alias["type"]])
1246             else:
1247                 output += "    /* Not Implemented yet {} */".format(t.name)
1248             output += "}\n\n"
1249             continue
1250
1251         output += signature.format(name=t.name)
1252
1253         for o in t.block:
1254             output += endianfun_obj(o)
1255         output += "}\n\n"
1256
1257     output += "\n#endif"
1258     output += "\n#endif /* vl_endianfun */\n\n"
1259
1260     return output
1261
1262
1263 def calc_size_fun(objs, modulename):
1264     """Main entry point for calculate size function generation"""
1265     output = """\
1266
1267 /****** Calculate size functions *****/\n\
1268 #ifdef vl_calcsizefun
1269 #ifndef included_{module}_calcsizefun
1270 #define included_{module}_calcsizefun
1271
1272 """
1273     output = output.format(module=modulename)
1274
1275     signature = """\
1276 /* calculate message size of message in network byte order */
1277 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1278 {{
1279 """
1280
1281     for o in objs:
1282         tname = o.__class__.__name__
1283
1284         output += signature.format(name=o.name)
1285         output += f"      return sizeof(*a)"
1286         if tname == "Using":
1287             if "length" in o.alias:
1288                 try:
1289                     tmp = int(o.alias["length"])
1290                     if tmp == 0:
1291                         raise (f"Unexpected length '0' for alias {o}")
1292                 except:
1293                     # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1294                     print("culprit:")
1295                     print(o)
1296                     print(dir(o.alias))
1297                     print(o.alias)
1298                     raise
1299         elif tname == "Enum" or tname == "EnumFlag":
1300             pass
1301         else:
1302             for b in o.block:
1303                 if b.type == "Option":
1304                     continue
1305                 elif b.type == "Field":
1306                     if b.fieldtype.startswith("vl_api_"):
1307                         output += f" - sizeof(a->{b.fieldname})"
1308                         output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1309                 elif b.type == "Array":
1310                     if b.lengthfield:
1311                         m = list(
1312                             filter(lambda x: x.fieldname == b.lengthfield, o.block)
1313                         )
1314                         if len(m) != 1:
1315                             raise Exception(
1316                                 f"Expected 1 match for field '{b.lengthfield}', got '{m}'"
1317                             )
1318                         lf = m[0]
1319                         if lf.fieldtype in ENDIAN_STRINGS:
1320                             output += f" + {ENDIAN_STRINGS[lf.fieldtype]}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1321                         elif lf.fieldtype == "u8":
1322                             output += (
1323                                 f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1324                             )
1325                         else:
1326                             raise Exception(
1327                                 f"Don't know how to endian swap {lf.fieldtype}"
1328                             )
1329                     else:
1330                         # Fixed length strings decay to nul terminated u8
1331                         if b.fieldtype == "string":
1332                             if b.modern_vla:
1333                                 output += f" + vl_api_string_len(&a->{b.fieldname})"
1334
1335         output += ";\n"
1336         output += "}\n\n"
1337     output += "\n#endif"
1338     output += "\n#endif /* vl_calcsizefun */\n\n"
1339
1340     return output
1341
1342
1343 def version_tuple(s, module):
1344     """Generate semantic version string"""
1345     output = """\
1346 /****** Version tuple *****/
1347
1348 #ifdef vl_api_version_tuple
1349
1350 """
1351     if "version" in s["Option"]:
1352         v = s["Option"]["version"]
1353         (major, minor, patch) = v.split(".")
1354         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % (
1355             module,
1356             major,
1357             minor,
1358             patch,
1359         )
1360
1361     output += "\n#endif /* vl_api_version_tuple */\n\n"
1362
1363     return output
1364
1365
1366 def generate_include_enum(s, module, stream):
1367     """Generate <name>.api_enum.h"""
1368     write = stream.write
1369
1370     if "Define" in s:
1371         write("typedef enum {\n")
1372         for t in s["Define"]:
1373             write("   VL_API_{},\n".format(t.name.upper()))
1374         write("   VL_MSG_{}_LAST\n".format(module.upper()))
1375         write("}} vl_api_{}_enum_t;\n".format(module))
1376
1377
1378 def generate_include_counters(s, stream):
1379     """Include file for the counter data model types."""
1380     write = stream.write
1381
1382     for counters in s:
1383         csetname = counters.name
1384         write("typedef enum {\n")
1385         for c in counters.block:
1386             write("   {}_ERROR_{},\n".format(csetname.upper(), c["name"].upper()))
1387         write("   {}_N_ERROR\n".format(csetname.upper()))
1388         write("}} vl_counter_{}_enum_t;\n".format(csetname))
1389
1390         write("extern vlib_error_desc_t {}_error_counters[];\n".format(csetname))
1391
1392
1393 def generate_include_types(s, module, stream):
1394     """Generate separate API _types file."""
1395     write = stream.write
1396
1397     write("#ifndef included_{module}_api_types_h\n".format(module=module))
1398     write("#define included_{module}_api_types_h\n".format(module=module))
1399
1400     if "version" in s["Option"]:
1401         v = s["Option"]["version"]
1402         (major, minor, patch) = v.split(".")
1403         write(
1404             "#define VL_API_{m}_API_VERSION_MAJOR {v}\n".format(
1405                 m=module.upper(), v=major
1406             )
1407         )
1408         write(
1409             "#define VL_API_{m}_API_VERSION_MINOR {v}\n".format(
1410                 m=module.upper(), v=minor
1411             )
1412         )
1413         write(
1414             "#define VL_API_{m}_API_VERSION_PATCH {v}\n".format(
1415                 m=module.upper(), v=patch
1416             )
1417         )
1418
1419     if "Import" in s:
1420         write("/* Imported API files */\n")
1421         for i in s["Import"]:
1422             filename = i.filename.replace("plugins/", "")
1423             write("#include <{}_types.h>\n".format(filename))
1424
1425     for o in itertools.chain(s["types"], s["Define"]):
1426         tname = o.__class__.__name__
1427         if tname == "Using":
1428             if "length" in o.alias:
1429                 write(
1430                     "typedef %s vl_api_%s_t[%s];\n"
1431                     % (o.alias["type"], o.name, o.alias["length"])
1432                 )
1433             else:
1434                 write("typedef %s vl_api_%s_t;\n" % (o.alias["type"], o.name))
1435         elif tname == "Enum" or tname == "EnumFlag":
1436             if o.enumtype == "u32":
1437                 write("typedef enum {\n")
1438             else:
1439                 write("typedef enum __attribute__((packed)) {\n")
1440
1441             for b in o.block:
1442                 write("    %s = %s,\n" % (b[0], b[1]))
1443             write("} vl_api_%s_t;\n" % o.name)
1444             if o.enumtype != "u32":
1445                 size1 = "sizeof(vl_api_%s_t)" % o.name
1446                 size2 = "sizeof(%s)" % o.enumtype
1447                 err_str = "size of API enum %s is wrong" % o.name
1448                 write('STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str))
1449         else:
1450             if tname == "Union":
1451                 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
1452             else:
1453                 write(
1454                     ("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") % o.name
1455                 )
1456             for b in o.block:
1457                 if b.type == "Option":
1458                     continue
1459                 if b.type == "Field":
1460                     write("    %s %s;\n" % (api2c(b.fieldtype), b.fieldname))
1461                 elif b.type == "Array":
1462                     if b.lengthfield:
1463                         write("    %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname))
1464                     else:
1465                         # Fixed length strings decay to nul terminated u8
1466                         if b.fieldtype == "string":
1467                             if b.modern_vla:
1468                                 write(
1469                                     "    {} {};\n".format(
1470                                         api2c(b.fieldtype), b.fieldname
1471                                     )
1472                                 )
1473                             else:
1474                                 write("    u8 {}[{}];\n".format(b.fieldname, b.length))
1475                         else:
1476                             write(
1477                                 "    %s %s[%s];\n"
1478                                 % (api2c(b.fieldtype), b.fieldname, b.length)
1479                             )
1480                 else:
1481                     raise ValueError(
1482                         "Error in processing type {} for {}".format(b, o.name)
1483                     )
1484
1485             write("} vl_api_%s_t;\n" % o.name)
1486             write(
1487                 f"#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n"
1488             )
1489
1490     for t in s["Define"]:
1491         write(
1492             '#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'.format(
1493                 n=t.name, ID=t.name.upper(), crc=t.crc
1494             )
1495         )
1496
1497     write("\n#endif\n")
1498
1499
1500 def generate_c_boilerplate(services, defines, counters, file_crc, module, stream):
1501     """VPP side plugin."""
1502     write = stream.write
1503     define_hash = {d.name: d for d in defines}
1504
1505     hdr = """\
1506 #define vl_endianfun            /* define message structures */
1507 #include "{module}.api.h"
1508 #undef vl_endianfun
1509
1510 #define vl_calcsizefun
1511 #include "{module}.api.h"
1512 #undef vl_calsizefun
1513
1514 /* instantiate all the print functions we know about */
1515 #define vl_printfun
1516 #include "{module}.api.h"
1517 #undef vl_printfun
1518
1519 """
1520
1521     write(hdr.format(module=module))
1522     write("static u16\n")
1523     write("setup_message_id_table (void) {\n")
1524     write("   api_main_t *am = my_api_main;\n")
1525     write("   vl_msg_api_msg_config_t c;\n")
1526     write(
1527         '   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1528         "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
1529     )
1530
1531     for d in defines:
1532         write(
1533             '   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1534             "                                VL_API_{ID} + msg_id_base);\n".format(
1535                 n=d.name, ID=d.name.upper(), crc=d.crc
1536             )
1537         )
1538     for s in services:
1539         d = define_hash[s.caller]
1540         write(
1541             "   c = (vl_msg_api_msg_config_t) "
1542             " {{.id = VL_API_{ID} + msg_id_base,\n"
1543             '   .name = "{n}",\n'
1544             "   .handler = vl_api_{n}_t_handler,\n"
1545             "   .endian = vl_api_{n}_t_endian,\n"
1546             "   .format_fn = vl_api_{n}_t_format,\n"
1547             "   .traced = 1,\n"
1548             "   .replay = 1,\n"
1549             "   .tojson = vl_api_{n}_t_tojson,\n"
1550             "   .fromjson = vl_api_{n}_t_fromjson,\n"
1551             "   .calc_size = vl_api_{n}_t_calc_size,\n"
1552             "   .is_autoendian = {auto}}};\n".format(
1553                 n=s.caller, ID=s.caller.upper(), auto=d.autoendian
1554             )
1555         )
1556         write("   vl_msg_api_config (&c);\n")
1557         try:
1558             d = define_hash[s.reply]
1559             write(
1560                 "   c = (vl_msg_api_msg_config_t) "
1561                 "{{.id = VL_API_{ID} + msg_id_base,\n"
1562                 '  .name = "{n}",\n'
1563                 "  .handler = 0,\n"
1564                 "  .endian = vl_api_{n}_t_endian,\n"
1565                 "  .format_fn = vl_api_{n}_t_format,\n"
1566                 "  .traced = 1,\n"
1567                 "  .replay = 1,\n"
1568                 "  .tojson = vl_api_{n}_t_tojson,\n"
1569                 "  .fromjson = vl_api_{n}_t_fromjson,\n"
1570                 "  .calc_size = vl_api_{n}_t_calc_size,\n"
1571                 "  .is_autoendian = {auto}}};\n".format(
1572                     n=s.reply, ID=s.reply.upper(), auto=d.autoendian
1573                 )
1574             )
1575             write("   vl_msg_api_config (&c);\n")
1576         except KeyError:
1577             pass
1578
1579     write("   return msg_id_base;\n")
1580     write("}\n")
1581
1582     severity = {
1583         "error": "VL_COUNTER_SEVERITY_ERROR",
1584         "info": "VL_COUNTER_SEVERITY_INFO",
1585         "warn": "VL_COUNTER_SEVERITY_WARN",
1586     }
1587
1588     for cnt in counters:
1589         csetname = cnt.name
1590         write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
1591         for c in cnt.block:
1592             write("  {\n")
1593             write('   .name = "{}",\n'.format(c["name"]))
1594             write('   .desc = "{}",\n'.format(c["description"]))
1595             write("   .severity = {},\n".format(severity[c["severity"]]))
1596             write("  },\n")
1597         write("};\n")
1598
1599
1600 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
1601     """Generate code for legacy style VAT. To be deleted."""
1602     write = stream.write
1603
1604     define_hash = {d.name: d for d in defines}
1605
1606     hdr = """\
1607 #define vl_endianfun            /* define message structures */
1608 #include "{module}.api.h"
1609 #undef vl_endianfun
1610
1611 #define vl_calcsizefun
1612 #include "{module}.api.h"
1613 #undef vl_calsizefun
1614
1615 /* instantiate all the print functions we know about */
1616 #define vl_printfun
1617 #include "{module}.api.h"
1618 #undef vl_printfun
1619
1620 """
1621
1622     write(hdr.format(module=module))
1623     for s in services:
1624         try:
1625             d = define_hash[s.reply]
1626         except KeyError:
1627             continue
1628         if d.manual_print:
1629             write(
1630                 "/*\n"
1631                 " * Manual definition requested for: \n"
1632                 " * vl_api_{n}_t_handler()\n"
1633                 " */\n".format(n=s.reply)
1634             )
1635             continue
1636         if not define_hash[s.caller].autoreply:
1637             write(
1638                 "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
1639                     n=s.reply
1640                 )
1641             )
1642             continue
1643         write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
1644         write("static void\n")
1645         write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
1646         write("   vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
1647         write("   i32 retval = ntohl(mp->retval);\n")
1648         write("   if (vam->async_mode) {\n")
1649         write("      vam->async_errors += (retval < 0);\n")
1650         write("   } else {\n")
1651         write("      vam->retval = retval;\n")
1652         write("      vam->result_ready = 1;\n")
1653         write("   }\n")
1654         write("}\n")
1655         write("#endif\n")
1656
1657         for e in s.events:
1658             if define_hash[e].manual_print:
1659                 continue
1660             write("static void\n")
1661             write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
1662             write('    vlib_cli_output(0, "{n} event called:");\n'.format(n=e))
1663             write(
1664                 '    vlib_cli_output(0, "%U", vl_api_{n}_t_format, mp);\n'.format(n=e)
1665             )
1666             write("}\n")
1667
1668     write("static void\n")
1669     write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
1670     for s in services:
1671         write(
1672             "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1673             "    .id = VL_API_{ID} + msg_id_base,\n"
1674             '    .name = "{n}",\n'
1675             "    .handler = vl_api_{n}_t_handler,\n"
1676             "    .endian = vl_api_{n}_t_endian,\n"
1677             "    .format_fn = vl_api_{n}_t_format,\n"
1678             "    .size = sizeof(vl_api_{n}_t),\n"
1679             "    .traced = 1,\n"
1680             "    .tojson = vl_api_{n}_t_tojson,\n"
1681             "    .fromjson = vl_api_{n}_t_fromjson,\n"
1682             "    .calc_size = vl_api_{n}_t_calc_size,\n"
1683             "   }});".format(n=s.reply, ID=s.reply.upper())
1684         )
1685         write(
1686             '   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
1687                 n=s.caller
1688             )
1689         )
1690         try:
1691             write(
1692                 '   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
1693                     n=s.caller, help=define_hash[s.caller].options["vat_help"]
1694                 )
1695             )
1696         except KeyError:
1697             pass
1698
1699         # Events
1700         for e in s.events:
1701             write(
1702                 "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1703                 "    .id = VL_API_{ID} + msg_id_base,\n"
1704                 '    .name = "{n}",\n'
1705                 "    .handler = vl_api_{n}_t_handler,\n"
1706                 "    .endian = vl_api_{n}_t_endian,\n"
1707                 "    .format_fn = vl_api_{n}_t_format,\n"
1708                 "    .size = sizeof(vl_api_{n}_t),\n"
1709                 "    .traced = 1,\n"
1710                 "    .tojson = vl_api_{n}_t_tojson,\n"
1711                 "    .fromjson = vl_api_{n}_t_fromjson,\n"
1712                 "    .calc_size = vl_api_{n}_t_calc_size,\n"
1713                 "   }});".format(n=e, ID=e.upper())
1714             )
1715
1716     write("}\n")
1717     write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
1718     write("{\n")
1719     write("   {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
1720     write("   mainp->vat_main = vam;\n")
1721     write(
1722         "   mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
1723         '                       ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
1724     )
1725     write("   if (mainp->msg_id_base == (u16) ~0)\n")
1726     write(
1727         '      return clib_error_return (0, "{} plugin not loaded...");\n'.format(
1728             module
1729         )
1730     )
1731     write("   setup_message_id_table (vam, mainp->msg_id_base);\n")
1732     write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
1733     write("    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
1734     write("#endif\n")
1735     write("   return 0;\n")
1736     write("}\n")
1737
1738
1739 def apifunc(func):
1740     """Check if a method is generated already."""
1741
1742     def _f(module, d, processed, *args):
1743         if d.name in processed:
1744             return None
1745         processed[d.name] = True
1746         return func(module, d, *args)
1747
1748     return _f
1749
1750
1751 def c_test_api_service(s, dump, stream):
1752     """Generate JSON code for a service."""
1753     write = stream.write
1754
1755     req_reply_template = """\
1756 static cJSON *
1757 api_{n} (cJSON *o)
1758 {{
1759   vl_api_{n}_t *mp;
1760   int len;
1761   if (!o) return 0;
1762   mp = vl_api_{n}_t_fromjson(o, &len);
1763   if (!mp) {{
1764     fprintf(stderr, "Failed converting JSON to API\\n");
1765     return 0;
1766   }}
1767
1768   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1769   vl_api_{n}_t_endian(mp);
1770   vac_write((char *)mp, len);
1771   cJSON_free(mp);
1772
1773   /* Read reply */
1774   char *p;
1775   int l;
1776   vac_read(&p, &l, 5); // XXX: Fix timeout
1777   if (p == 0 || l == 0) return 0;
1778     // XXX Will fail in case of event received. Do loop
1779   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1780     fprintf(stderr, "Mismatched reply\\n");
1781     return 0;
1782   }}
1783   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1784   vl_api_{r}_t_endian(rmp);
1785   return vl_api_{r}_t_tojson(rmp);
1786 }}
1787
1788 """
1789     dump_details_template = """\
1790 static cJSON *
1791 api_{n} (cJSON *o)
1792 {{
1793   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1794   int len;
1795   if (!o) return 0;
1796   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1797   if (!mp) {{
1798       fprintf(stderr, "Failed converting JSON to API\\n");
1799       return 0;
1800   }}
1801   mp->_vl_msg_id = msg_id;
1802   vl_api_{n}_t_endian(mp);
1803   vac_write((char *)mp, len);
1804   cJSON_free(mp);
1805
1806   vat2_control_ping(123); // FIX CONTEXT
1807   cJSON *reply = cJSON_CreateArray();
1808
1809   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1810   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1811
1812   while (1) {{
1813     /* Read reply */
1814     char *p;
1815     int l;
1816     vac_read(&p, &l, 5); // XXX: Fix timeout
1817     if (p == 0 || l == 0) {{
1818       cJSON_free(reply);
1819       return 0;
1820     }}
1821
1822     /* Message can be one of [_details, control_ping_reply
1823      * or unrelated event]
1824      */
1825     u16 reply_msg_id = ntohs(*((u16 *)p));
1826     if (reply_msg_id == ping_reply_msg_id) {{
1827         break;
1828     }}
1829
1830     if (reply_msg_id == details_msg_id) {{
1831         if (l < sizeof(vl_api_{r}_t)) {{
1832             cJSON_free(reply);
1833             return 0;
1834         }}
1835         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1836         vl_api_{r}_t_endian(rmp);
1837         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1838     }}
1839   }}
1840   return reply;
1841 }}
1842
1843 """
1844     gets_details_reply_template = """\
1845 static cJSON *
1846 api_{n} (cJSON *o)
1847 {{
1848     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1849   int len = 0;
1850   if (!o) return 0;
1851   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1852   if (!mp) {{
1853     fprintf(stderr, "Failed converting JSON to API\\n");
1854     return 0;
1855   }}
1856   mp->_vl_msg_id = msg_id;
1857
1858   vl_api_{n}_t_endian(mp);
1859   vac_write((char *)mp, len);
1860   cJSON_free(mp);
1861
1862   cJSON *reply = cJSON_CreateArray();
1863
1864   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1865   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1866
1867   while (1) {{
1868     /* Read reply */
1869     char *p;
1870     int l;
1871     vac_read(&p, &l, 5); // XXX: Fix timeout
1872
1873     /* Message can be one of [_details, control_ping_reply
1874      * or unrelated event]
1875      */
1876     u16 msg_id = ntohs(*((u16 *)p));
1877     if (msg_id == reply_msg_id) {{
1878         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1879         vl_api_{r}_t_endian(rmp);
1880         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1881         break;
1882     }}
1883
1884     if (msg_id == details_msg_id) {{
1885         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1886         vl_api_{d}_t_endian(rmp);
1887         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1888     }}
1889   }}
1890   return reply;
1891 }}
1892
1893 """
1894
1895     if dump:
1896         if s.stream_message:
1897             write(
1898                 gets_details_reply_template.format(
1899                     n=s.caller,
1900                     r=s.reply,
1901                     N=s.caller.upper(),
1902                     R=s.reply.upper(),
1903                     d=s.stream_message,
1904                     D=s.stream_message.upper(),
1905                 )
1906             )
1907         else:
1908             write(
1909                 dump_details_template.format(
1910                     n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1911                 )
1912             )
1913     else:
1914         write(
1915             req_reply_template.format(
1916                 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1917             )
1918         )
1919
1920
1921 def generate_c_test2_boilerplate(services, defines, module, stream):
1922     """Generate code for VAT2 plugin."""
1923     write = stream.write
1924
1925     define_hash = {d.name: d for d in defines}
1926     # replies = {}
1927
1928     hdr = """\
1929 #include <vlibapi/api.h>
1930 #include <vlibmemory/api.h>
1931 #include <vppinfra/error.h>
1932 #include <vnet/ip/ip_format_fns.h>
1933 #include <vnet/ethernet/ethernet_format_fns.h>
1934
1935 #define vl_typedefs             /* define message structures */
1936 #include <vlibmemory/vl_memory_api_h.h>
1937 #include <vlibmemory/vlib.api_types.h>
1938 #include <vlibmemory/vlib.api.h>
1939 #undef vl_typedefs
1940
1941 #include "{module}.api_enum.h"
1942 #include "{module}.api_types.h"
1943
1944 #define vl_endianfun            /* define message structures */
1945 #include "{module}.api.h"
1946 #undef vl_endianfun
1947
1948 #define vl_calcsizefun
1949 #include "{module}.api.h"
1950 #undef vl_calsizefun
1951
1952 #define vl_printfun
1953 #include "{module}.api.h"
1954 #undef vl_printfun
1955
1956 #include "{module}.api_tojson.h"
1957 #include "{module}.api_fromjson.h"
1958 #include <vpp-api/client/vppapiclient.h>
1959
1960 #include <vat2/vat2_helpers.h>
1961
1962 """
1963
1964     write(hdr.format(module=module))
1965
1966     for s in services:
1967         if s.reply not in define_hash:
1968             continue
1969         c_test_api_service(s, s.stream, stream)
1970
1971     write(
1972         "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
1973     )
1974     # write('__attribute__((constructor))')
1975     write("clib_error_t *\n")
1976     write("vat2_register_plugin (void) {\n")
1977     for s in services:
1978         if s.reply not in define_hash:
1979             continue
1980         crc = define_hash[s.caller].crc
1981         write(
1982             '   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
1983                 n=s.caller, crc=crc
1984             )
1985         )
1986     write("   return 0;\n")
1987     write("}\n")
1988
1989
1990 #
1991 # Plugin entry point
1992 #
1993 def run(output_dir, apifilename, s):
1994     """Main plugin entry point."""
1995     stream = StringIO()
1996
1997     if not output_dir:
1998         sys.stderr.write("Missing --outputdir argument")
1999         return None
2000
2001     basename = os.path.basename(apifilename)
2002     filename, _ = os.path.splitext(basename)
2003     modulename = filename.replace(".", "_")
2004     filename_enum = os.path.join(output_dir + "/" + basename + "_enum.h")
2005     filename_types = os.path.join(output_dir + "/" + basename + "_types.h")
2006     filename_c = os.path.join(output_dir + "/" + basename + ".c")
2007     filename_c_test = os.path.join(output_dir + "/" + basename + "_test.c")
2008     filename_c_test2 = os.path.join(output_dir + "/" + basename + "_test2.c")
2009     filename_c_tojson = os.path.join(output_dir + "/" + basename + "_tojson.h")
2010     filename_c_fromjson = os.path.join(output_dir + "/" + basename + "_fromjson.h")
2011
2012     # Generate separate types file
2013     st = StringIO()
2014     generate_include_types(s, modulename, st)
2015     with open(filename_types, "w") as fd:
2016         st.seek(0)
2017         shutil.copyfileobj(st, fd)
2018     st.close()
2019
2020     # Generate separate enum file
2021     st = StringIO()
2022     st.write("#ifndef included_{}_api_enum_h\n".format(modulename))
2023     st.write("#define included_{}_api_enum_h\n".format(modulename))
2024     generate_include_enum(s, modulename, st)
2025     generate_include_counters(s["Counters"], st)
2026     st.write("#endif\n")
2027     with open(filename_enum, "w") as fd:
2028         st.seek(0)
2029         shutil.copyfileobj(st, fd)
2030     st.close()
2031
2032     # Generate separate C file
2033     st = StringIO()
2034     generate_c_boilerplate(
2035         s["Service"], s["Define"], s["Counters"], s["file_crc"], modulename, st
2036     )
2037     with open(filename_c, "w") as fd:
2038         st.seek(0)
2039         shutil.copyfileobj(st, fd)
2040     st.close()
2041
2042     # Generate separate C test file
2043     st = StringIO()
2044     plugin = bool("plugin" in apifilename)
2045     generate_c_test_boilerplate(
2046         s["Service"], s["Define"], s["file_crc"], modulename, plugin, st
2047     )
2048     with open(filename_c_test, "w") as fd:
2049         st.seek(0)
2050         shutil.copyfileobj(st, fd)
2051     st.close()
2052
2053     # Fully autogenerated VATv2 C test file
2054     st = StringIO()
2055     generate_c_test2_boilerplate(s["Service"], s["Define"], modulename, st)
2056     with open(filename_c_test2, "w") as fd:
2057         st.seek(0)
2058         shutil.copyfileobj(st, fd)
2059     st.close()  #
2060
2061     # Generate separate JSON file
2062     st = StringIO()
2063     generate_tojson(s, modulename, st)
2064     with open(filename_c_tojson, "w") as fd:
2065         st.seek(0)
2066         shutil.copyfileobj(st, fd)
2067     st.close()
2068     st = StringIO()
2069     generate_fromjson(s, modulename, st)
2070     with open(filename_c_fromjson, "w") as fd:
2071         st.seek(0)
2072         shutil.copyfileobj(st, fd)
2073     st.close()
2074
2075     output = TOP_BOILERPLATE.format(datestring=DATESTRING, input_filename=basename)
2076     output += generate_imports(s["Import"])
2077     output += msg_ids(s)
2078     output += msg_names(s)
2079     output += msg_name_crc_list(s, filename)
2080     output += typedefs(modulename)
2081     printfun_types(s["types"], stream, modulename)
2082     printfun(s["Define"], stream, modulename)
2083     output += stream.getvalue()
2084     stream.close()
2085     output += endianfun(s["types"] + s["Define"], modulename)
2086     output += calc_size_fun(s["types"] + s["Define"], modulename)
2087     output += version_tuple(s, basename)
2088     output += BOTTOM_BOILERPLATE.format(input_filename=basename, file_crc=s["file_crc"])
2089
2090     return output