vapi: improve vl_api_string_t handling
[vpp.git] / src / vpp-api / vapi / vapi_c_gen.py
1 #!/usr/bin/env python3
2
3 import argparse
4 import inspect
5 import os
6 import sys
7 import logging
8 from vapi_json_parser import (
9     Field,
10     Struct,
11     Enum,
12     Union,
13     Message,
14     JsonParser,
15     SimpleType,
16     StructType,
17     Alias,
18 )
19
20
21 class CField(Field):
22     def get_c_name(self):
23         return "vapi_type_%s" % self.name
24
25     def get_c_def(self):
26         if self.type.get_c_name() == "string":
27             if self.len:
28                 return "u8 %s[%d];" % (self.name, self.len)
29             else:
30                 return "vl_api_string_t %s;" % (self.name)
31         else:
32             if self.len is not None and type(self.len) != dict:
33                 return "%s %s[%d];" % (self.type.get_c_name(), self.name, self.len)
34             else:
35                 return "%s %s;" % (self.type.get_c_name(), self.name)
36
37     def get_swap_to_be_code(self, struct, var):
38         if self.len is not None and type(self.len) != dict:
39             if self.len > 0:
40                 return (
41                     "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"
42                     " while(0);"
43                     % (self.len, self.type.get_swap_to_be_code(struct, "%s[i]" % var))
44                 )
45             else:
46                 if self.nelem_field.needs_byte_swap():
47                     nelem_field = "%s(%s%s)" % (
48                         self.nelem_field.type.get_swap_to_host_func_name(),
49                         struct,
50                         self.nelem_field.name,
51                     )
52                 else:
53                     nelem_field = "%s%s" % (struct, self.nelem_field.name)
54                 return (
55                     "do { unsigned i; for (i = 0; i < %s; ++i) { %s } }"
56                     " while(0);"
57                     % (
58                         nelem_field,
59                         self.type.get_swap_to_be_code(struct, "%s[i]" % var),
60                     )
61                 )
62         return self.type.get_swap_to_be_code(struct, "%s" % var)
63
64     def get_swap_to_host_code(self, struct, var):
65         if self.len is not None and type(self.len) != dict:
66             if self.len > 0:
67                 return (
68                     "do { unsigned i; for (i = 0; i < %d; ++i) { %s } }"
69                     " while(0);"
70                     % (self.len, self.type.get_swap_to_host_code(struct, "%s[i]" % var))
71                 )
72             else:
73                 # nelem_field already swapped to host here...
74                 return (
75                     "do { unsigned i; for (i = 0; i < %s%s; ++i) { %s } }"
76                     " while(0);"
77                     % (
78                         struct,
79                         self.nelem_field.name,
80                         self.type.get_swap_to_host_code(struct, "%s[i]" % var),
81                     )
82                 )
83         return self.type.get_swap_to_host_code(struct, "%s" % var)
84
85     def needs_byte_swap(self):
86         return self.type.needs_byte_swap()
87
88     def get_vla_parameter_name(self, path):
89         return "%s_%s_array_size" % ("_".join(path), self.name)
90
91     def get_vla_field_name(self, path):
92         return ".".join(path + [self.nelem_field.name])
93
94     def get_alloc_vla_param_names(self, path):
95         if self.is_vla():
96             result = [self.get_vla_parameter_name(path)]
97         else:
98             result = []
99         if self.type.has_vla():
100             t = self.type.get_alloc_vla_param_names(path + [self.name])
101             result.extend(t)
102         return result
103
104     def get_vla_calc_size_code(self, prefix, path, is_alloc):
105         if self.is_vla():
106             result = [
107                 "sizeof(%s.%s[0]) * %s"
108                 % (
109                     ".".join([prefix] + path),
110                     self.name,
111                     self.get_vla_parameter_name(path)
112                     if is_alloc
113                     else "%s.%s" % (prefix, self.get_vla_field_name(path)),
114                 )
115             ]
116         else:
117             result = []
118         if self.type.has_vla():
119             t = self.type.get_vla_calc_size_code(prefix, path + [self.name], is_alloc)
120             result.extend(t)
121         return result
122
123     def get_vla_assign_code(self, prefix, path):
124         result = []
125         if self.is_vla():
126             result.append(
127                 "%s.%s = %s"
128                 % (
129                     ".".join([prefix] + path),
130                     self.nelem_field.name,
131                     self.get_vla_parameter_name(path),
132                 )
133             )
134         if self.type.has_vla():
135             t = self.type.get_vla_assign_code(prefix, path + [self.name])
136             result.extend(t)
137         return result
138
139
140 class CAlias(CField):
141     def get_c_name(self):
142         return "vapi_type_%s" % self.name
143
144     def get_c_def(self):
145         if self.len is not None:
146             return "typedef %s vapi_type_%s[%d];" % (
147                 self.type.get_c_name(),
148                 self.name,
149                 self.len,
150             )
151         else:
152             return "typedef %s vapi_type_%s;" % (self.type.get_c_name(), self.name)
153
154
155 class CStruct(Struct):
156     def get_c_def(self):
157         return "\n".join(
158             [
159                 "typedef struct __attribute__((__packed__)) {\n%s"
160                 % ("\n".join(["  %s" % x.get_c_def() for x in self.fields])),
161                 "} %s;" % self.get_c_name(),
162             ]
163         )
164
165     def get_vla_assign_code(self, prefix, path):
166         return [
167             x
168             for f in self.fields
169             if f.has_vla()
170             for x in f.get_vla_assign_code(prefix, path)
171         ]
172
173     def get_alloc_vla_param_names(self, path):
174         return [
175             x
176             for f in self.fields
177             if f.has_vla()
178             for x in f.get_alloc_vla_param_names(path)
179         ]
180
181     def get_vla_calc_size_code(self, prefix, path, is_alloc):
182         return [
183             x
184             for f in self.fields
185             if f.has_vla()
186             for x in f.get_vla_calc_size_code(prefix, path, is_alloc)
187         ]
188
189
190 class CSimpleType(SimpleType):
191     swap_to_be_dict = {
192         "i16": "htobe16",
193         "u16": "htobe16",
194         "i32": "htobe32",
195         "u32": "htobe32",
196         "i64": "htobe64",
197         "u64": "htobe64",
198     }
199
200     swap_to_host_dict = {
201         "i16": "be16toh",
202         "u16": "be16toh",
203         "i32": "be32toh",
204         "u32": "be32toh",
205         "i64": "be64toh",
206         "u64": "be64toh",
207     }
208
209     __packed = "__attribute__((packed))"
210     pack_dict = {
211         "i8": __packed,
212         "u8": __packed,
213         "i16": __packed,
214         "u16": __packed,
215     }
216
217     def get_c_name(self):
218         return self.name
219
220     def get_swap_to_be_func_name(self):
221         return self.swap_to_be_dict[self.name]
222
223     def get_swap_to_host_func_name(self):
224         return self.swap_to_host_dict[self.name]
225
226     def get_packed_string(self):
227         return self.pack_dict[self.name]
228
229     def get_swap_to_be_code(self, struct, var, cast=None):
230         x = "%s%s" % (struct, var)
231         return "%s = %s%s(%s);" % (
232             x,
233             "(%s)" % cast if cast else "",
234             self.get_swap_to_be_func_name(),
235             x,
236         )
237
238     def get_swap_to_host_code(self, struct, var, cast=None):
239         x = "%s%s" % (struct, var)
240         return "%s = %s%s(%s);" % (
241             x,
242             "(%s)" % cast if cast else "",
243             self.get_swap_to_host_func_name(),
244             x,
245         )
246
247     def needs_byte_swap(self):
248         try:
249             self.get_swap_to_host_func_name()
250             return True
251         except KeyError:
252             pass
253         return False
254
255     def get_packed(self):
256         return self.pack_dict.get(self.name, "")
257
258
259 class CEnum(Enum):
260     def get_c_name(self):
261         return "vapi_enum_%s" % self.name
262
263     def get_c_def(self):
264         return "typedef enum {\n%s\n} %s %s;" % (
265             "\n".join(["  %s = %s," % (i, j) for i, j in self.value_pairs]),
266             self.type.get_packed(),
267             self.get_c_name(),
268         )
269
270     def needs_byte_swap(self):
271         return self.type.needs_byte_swap()
272
273     def get_swap_to_be_code(self, struct, var):
274         return self.type.get_swap_to_be_code(struct, var, self.get_c_name())
275
276     def get_swap_to_host_code(self, struct, var):
277         return self.type.get_swap_to_host_code(struct, var, self.get_c_name())
278
279
280 class CUnion(Union):
281     def get_c_name(self):
282         return "vapi_union_%s" % self.name
283
284     def get_c_def(self):
285         return "typedef union {\n%s\n} %s;" % (
286             "\n".join(["  %s %s;" % (i.get_c_name(), j) for i, j in self.type_pairs]),
287             self.get_c_name(),
288         )
289
290     def needs_byte_swap(self):
291         return False
292
293
294 class CStructType(StructType, CStruct):
295     def get_c_name(self):
296         if self.name == "vl_api_string_t":
297             return "vl_api_string_t"
298         return "vapi_type_%s" % self.name
299
300     def get_swap_to_be_func_name(self):
301         return "%s_hton" % self.get_c_name()
302
303     def get_swap_to_host_func_name(self):
304         return "%s_ntoh" % self.get_c_name()
305
306     def get_swap_to_be_func_decl(self):
307         return "void %s(%s *msg)" % (self.get_swap_to_be_func_name(), self.get_c_name())
308
309     def get_swap_to_be_func_def(self):
310         return "%s\n{\n%s\n}" % (
311             self.get_swap_to_be_func_decl(),
312             "\n".join(
313                 [
314                     "  %s" % p.get_swap_to_be_code("msg->", "%s" % p.name)
315                     for p in self.fields
316                     if p.needs_byte_swap()
317                 ]
318             ),
319         )
320
321     def get_swap_to_host_func_decl(self):
322         return "void %s(%s *msg)" % (
323             self.get_swap_to_host_func_name(),
324             self.get_c_name(),
325         )
326
327     def get_swap_to_host_func_def(self):
328         return "%s\n{\n%s\n}" % (
329             self.get_swap_to_host_func_decl(),
330             "\n".join(
331                 [
332                     "  %s" % p.get_swap_to_host_code("msg->", "%s" % p.name)
333                     for p in self.fields
334                     if p.needs_byte_swap()
335                 ]
336             ),
337         )
338
339     def get_swap_to_be_code(self, struct, var):
340         return "%s(&%s%s);" % (self.get_swap_to_be_func_name(), struct, var)
341
342     def get_swap_to_host_code(self, struct, var):
343         return "%s(&%s%s);" % (self.get_swap_to_host_func_name(), struct, var)
344
345     def needs_byte_swap(self):
346         for f in self.fields:
347             if f.needs_byte_swap():
348                 return True
349         return False
350
351
352 class CMessage(Message):
353     def __init__(self, logger, definition, json_parser):
354         super(CMessage, self).__init__(logger, definition, json_parser)
355         self.payload_members = [
356             "  %s" % p.get_c_def() for p in self.fields if p.type != self.header
357         ]
358
359     def has_payload(self):
360         return len(self.payload_members) > 0
361
362     def get_msg_id_name(self):
363         return "vapi_msg_id_%s" % self.name
364
365     def get_c_name(self):
366         return "vapi_msg_%s" % self.name
367
368     def get_payload_struct_name(self):
369         return "vapi_payload_%s" % self.name
370
371     def get_alloc_func_name(self):
372         return "vapi_alloc_%s" % self.name
373
374     def get_alloc_vla_param_names(self):
375         return [
376             x
377             for f in self.fields
378             if f.has_vla()
379             for x in f.get_alloc_vla_param_names([])
380         ]
381
382     def get_alloc_func_decl(self):
383         return "%s* %s(struct vapi_ctx_s *ctx%s)" % (
384             self.get_c_name(),
385             self.get_alloc_func_name(),
386             "".join([", size_t %s" % n for n in self.get_alloc_vla_param_names()]),
387         )
388
389     def get_alloc_func_def(self):
390         extra = []
391         if self.header.has_field("client_index"):
392             extra.append("  msg->header.client_index = vapi_get_client_index(ctx);")
393         if self.header.has_field("context"):
394             extra.append("  msg->header.context = 0;")
395         return "\n".join(
396             [
397                 "%s" % self.get_alloc_func_decl(),
398                 "{",
399                 "  %s *msg = NULL;" % self.get_c_name(),
400                 "  const size_t size = sizeof(%s)%s;"
401                 % (
402                     self.get_c_name(),
403                     "".join(
404                         [
405                             " + %s" % x
406                             for f in self.fields
407                             if f.has_vla()
408                             for x in f.get_vla_calc_size_code(
409                                 "msg->payload", [], is_alloc=True
410                             )
411                         ]
412                     ),
413                 ),
414                 "  /* cast here required to play nicely with C++ world ... */",
415                 "  msg = (%s*)vapi_msg_alloc(ctx, size);" % self.get_c_name(),
416                 "  if (!msg) {",
417                 "    return NULL;",
418                 "  }",
419             ]
420             + extra
421             + [
422                 "  msg->header._vl_msg_id = vapi_lookup_vl_msg_id(ctx, %s);"
423                 % self.get_msg_id_name(),
424                 "".join(
425                     [
426                         "  %s;\n" % line
427                         for f in self.fields
428                         if f.has_vla()
429                         for line in f.get_vla_assign_code("msg->payload", [])
430                     ]
431                 ),
432                 "  return msg;",
433                 "}",
434             ]
435         )
436
437     def get_calc_msg_size_func_name(self):
438         return "vapi_calc_%s_msg_size" % self.name
439
440     def get_calc_msg_size_func_decl(self):
441         return "uword %s(%s *msg)" % (
442             self.get_calc_msg_size_func_name(),
443             self.get_c_name(),
444         )
445
446     def get_calc_msg_size_func_def(self):
447         return "\n".join(
448             [
449                 "%s" % self.get_calc_msg_size_func_decl(),
450                 "{",
451                 "  return sizeof(*msg)%s;"
452                 % "".join(
453                     [
454                         " + %s" % x
455                         for f in self.fields
456                         if f.has_vla()
457                         for x in f.get_vla_calc_size_code(
458                             "msg->payload", [], is_alloc=False
459                         )
460                     ]
461                 ),
462                 "}",
463             ]
464         )
465
466     def get_verify_msg_size_func_name(self):
467         return f"vapi_verify_{self.name}_msg_size"
468
469     def get_verify_msg_size_func_decl(self):
470         return "int %s(%s *msg, uword buf_size)" % (
471             self.get_verify_msg_size_func_name(),
472             self.get_c_name(),
473         )
474
475     def get_verify_msg_size_func_def(self):
476         return inspect.cleandoc(
477             f"""
478             {self.get_verify_msg_size_func_decl()}
479             {{
480               if (sizeof({self.get_c_name()}) > buf_size)
481                 {{
482                   VAPI_ERR("Truncated '{self.name}' msg received, received %lu"
483                     "bytes, expected %lu bytes.", buf_size,
484                     sizeof({self.get_c_name()}));
485                   return -1;
486                 }}
487               if ({self.get_calc_msg_size_func_name()}(msg) > buf_size)
488                 {{
489                   VAPI_ERR("Truncated '{self.name}' msg received, received %lu"
490                     "bytes, expected %lu bytes.", buf_size,
491                     {self.get_calc_msg_size_func_name()}(msg));
492                   return -1;
493                 }}
494               return 0;
495             }}
496         """
497         )
498
499     def get_c_def(self):
500         if self.has_payload():
501             return "\n".join(
502                 [
503                     "typedef struct __attribute__ ((__packed__)) {",
504                     "%s " % "\n".join(self.payload_members),
505                     "} %s;" % self.get_payload_struct_name(),
506                     "",
507                     "typedef struct __attribute__ ((__packed__)) {",
508                     (
509                         "  %s %s;" % (self.header.get_c_name(), self.fields[0].name)
510                         if self.header is not None
511                         else ""
512                     ),
513                     "  %s payload;" % self.get_payload_struct_name(),
514                     "} %s;" % self.get_c_name(),
515                 ]
516             )
517         else:
518             return "\n".join(
519                 [
520                     "typedef struct __attribute__ ((__packed__)) {",
521                     (
522                         "  %s %s;" % (self.header.get_c_name(), self.fields[0].name)
523                         if self.header is not None
524                         else ""
525                     ),
526                     "} %s;" % self.get_c_name(),
527                 ]
528             )
529
530     def get_swap_payload_to_host_func_name(self):
531         return "%s_payload_ntoh" % self.get_c_name()
532
533     def get_swap_payload_to_be_func_name(self):
534         return "%s_payload_hton" % self.get_c_name()
535
536     def get_swap_payload_to_host_func_decl(self):
537         return "void %s(%s *payload)" % (
538             self.get_swap_payload_to_host_func_name(),
539             self.get_payload_struct_name(),
540         )
541
542     def get_swap_payload_to_be_func_decl(self):
543         return "void %s(%s *payload)" % (
544             self.get_swap_payload_to_be_func_name(),
545             self.get_payload_struct_name(),
546         )
547
548     def get_swap_payload_to_be_func_def(self):
549         return "%s\n{\n%s\n}" % (
550             self.get_swap_payload_to_be_func_decl(),
551             "\n".join(
552                 [
553                     "  %s" % p.get_swap_to_be_code("payload->", "%s" % p.name)
554                     for p in self.fields
555                     if p.needs_byte_swap() and p.type != self.header
556                 ]
557             ),
558         )
559
560     def get_swap_payload_to_host_func_def(self):
561         return "%s\n{\n%s\n}" % (
562             self.get_swap_payload_to_host_func_decl(),
563             "\n".join(
564                 [
565                     "  %s" % p.get_swap_to_host_code("payload->", "%s" % p.name)
566                     for p in self.fields
567                     if p.needs_byte_swap() and p.type != self.header
568                 ]
569             ),
570         )
571
572     def get_swap_to_host_func_name(self):
573         return "%s_ntoh" % self.get_c_name()
574
575     def get_swap_to_be_func_name(self):
576         return "%s_hton" % self.get_c_name()
577
578     def get_swap_to_host_func_decl(self):
579         return "void %s(%s *msg)" % (
580             self.get_swap_to_host_func_name(),
581             self.get_c_name(),
582         )
583
584     def get_swap_to_be_func_decl(self):
585         return "void %s(%s *msg)" % (self.get_swap_to_be_func_name(), self.get_c_name())
586
587     def get_swap_to_be_func_def(self):
588         return "\n".join(
589             [
590                 "%s" % self.get_swap_to_be_func_decl(),
591                 "{",
592                 (
593                     '  VAPI_DBG("Swapping `%s\'@%%p to big endian", msg);'
594                     % self.get_c_name()
595                 ),
596                 "  %s(&msg->header);" % self.header.get_swap_to_be_func_name()
597                 if self.header is not None
598                 else "",
599                 "  %s(&msg->payload);" % self.get_swap_payload_to_be_func_name()
600                 if self.has_payload()
601                 else "",
602                 "}",
603             ]
604         )
605
606     def get_swap_to_host_func_def(self):
607         return "\n".join(
608             [
609                 "%s" % self.get_swap_to_host_func_decl(),
610                 "{",
611                 (
612                     '  VAPI_DBG("Swapping `%s\'@%%p to host byte order", msg);'
613                     % self.get_c_name()
614                 ),
615                 "  %s(&msg->header);" % self.header.get_swap_to_host_func_name()
616                 if self.header is not None
617                 else "",
618                 "  %s(&msg->payload);" % self.get_swap_payload_to_host_func_name()
619                 if self.has_payload()
620                 else "",
621                 "}",
622             ]
623         )
624
625     def get_op_func_name(self):
626         return "vapi_%s" % self.name
627
628     def get_op_func_decl(self):
629         stream_param_lines = []
630         if self.has_stream_msg:
631             stream_param_lines = [
632                 "vapi_error_e (*details_callback)(struct vapi_ctx_s *ctx",
633                 "                                 void *callback_ctx",
634                 "                                 vapi_error_e rv",
635                 "                                 bool is_last",
636                 "                                 %s *details)"
637                 % self.stream_msg.get_payload_struct_name(),
638                 "void *details_callback_ctx",
639             ]
640
641         return "vapi_error_e %s(%s)" % (
642             self.get_op_func_name(),
643             ",\n  ".join(
644                 [
645                     "struct vapi_ctx_s *ctx",
646                     "%s *msg" % self.get_c_name(),
647                     "vapi_error_e (*reply_callback)(struct vapi_ctx_s *ctx",
648                     "                               void *callback_ctx",
649                     "                               vapi_error_e rv",
650                     "                               bool is_last",
651                     "                               %s *reply)"
652                     % self.reply.get_payload_struct_name(),
653                 ]
654                 + [
655                     "void *reply_callback_ctx",
656                 ]
657                 + stream_param_lines
658             ),
659         )
660
661     def get_op_func_def(self):
662         param_check_lines = ["  if (!msg || !reply_callback) {"]
663         store_request_lines = [
664             "    vapi_store_request(ctx, req_context, %s, %s, "
665             % (
666                 self.reply.get_msg_id_name(),
667                 "VAPI_REQUEST_DUMP" if self.reply_is_stream else "VAPI_REQUEST_REG",
668             ),
669             "                       (vapi_cb_t)reply_callback, reply_callback_ctx);",
670         ]
671         if self.has_stream_msg:
672             param_check_lines = [
673                 "  if (!msg || !reply_callback || !details_callback) {"
674             ]
675             store_request_lines = [
676                 f"    vapi_store_request(ctx, req_context, {self.stream_msg.get_msg_id_name()}, VAPI_REQUEST_STREAM, ",
677                 "                       (vapi_cb_t)details_callback, details_callback_ctx);",
678                 f"    vapi_store_request(ctx, req_context, {self.reply.get_msg_id_name()}, VAPI_REQUEST_REG, ",
679                 "                       (vapi_cb_t)reply_callback, reply_callback_ctx);",
680             ]
681
682         return "\n".join(
683             [
684                 "%s" % self.get_op_func_decl(),
685                 "{",
686             ]
687             + param_check_lines
688             + [
689                 "    return VAPI_EINVAL;",
690                 "  }",
691                 "  if (vapi_is_nonblocking(ctx) && vapi_requests_full(ctx)) {",
692                 "    return VAPI_EAGAIN;",
693                 "  }",
694                 "  vapi_error_e rv;",
695                 "  if (VAPI_OK != (rv = vapi_producer_lock (ctx))) {",
696                 "    return rv;",
697                 "  }",
698                 "  u32 req_context = vapi_gen_req_context(ctx);",
699                 "  msg->header.context = req_context;",
700                 "  %s(msg);" % self.get_swap_to_be_func_name(),
701                 (
702                     "  if (VAPI_OK == (rv = vapi_send_with_control_ping "
703                     "(ctx, msg, req_context))) {"
704                     if (self.reply_is_stream and not self.has_stream_msg)
705                     else "  if (VAPI_OK == (rv = vapi_send (ctx, msg))) {"
706                 ),
707             ]
708             + store_request_lines
709             + [
710                 "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
711                 "      abort (); /* this really shouldn't happen */",
712                 "    }",
713                 "    if (vapi_is_nonblocking(ctx)) {",
714                 "      rv = VAPI_OK;",
715                 "    } else {",
716                 "      rv = vapi_dispatch(ctx);",
717                 "    }",
718                 "  } else {",
719                 "    %s(msg);" % self.get_swap_to_host_func_name(),
720                 "    if (VAPI_OK != vapi_producer_unlock (ctx)) {",
721                 "      abort (); /* this really shouldn't happen */",
722                 "    }",
723                 "  }",
724                 "  return rv;",
725                 "}",
726                 "",
727             ]
728         )
729
730     def get_event_cb_func_decl(self):
731         if not self.is_reply and not self.is_event:
732             raise Exception("Cannot register event callback for non-reply message")
733         if self.has_payload():
734             return "\n".join(
735                 [
736                     "void vapi_set_%s_event_cb (" % self.get_c_name(),
737                     "  struct vapi_ctx_s *ctx, ",
738                     (
739                         "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
740                         "void *callback_ctx, %s *payload),"
741                         % self.get_payload_struct_name()
742                     ),
743                     "  void *callback_ctx)",
744                 ]
745             )
746         else:
747             return "\n".join(
748                 [
749                     "void vapi_set_%s_event_cb (" % self.get_c_name(),
750                     "  struct vapi_ctx_s *ctx, ",
751                     "  vapi_error_e (*callback)(struct vapi_ctx_s *ctx, "
752                     "void *callback_ctx),",
753                     "  void *callback_ctx)",
754                 ]
755             )
756
757     def get_event_cb_func_def(self):
758         if not self.is_reply and not self.is_event:
759             raise Exception("Cannot register event callback for non-reply function")
760         return "\n".join(
761             [
762                 "%s" % self.get_event_cb_func_decl(),
763                 "{",
764                 (
765                     "  vapi_set_event_cb(ctx, %s, (vapi_event_cb)callback, "
766                     "callback_ctx);" % self.get_msg_id_name()
767                 ),
768                 "}",
769             ]
770         )
771
772     def get_c_metadata_struct_name(self):
773         return "__vapi_metadata_%s" % self.name
774
775     def get_c_constructor(self):
776         has_context = False
777         if self.header is not None:
778             has_context = self.header.has_field("context")
779         return "\n".join(
780             [
781                 "static void __attribute__((constructor)) __vapi_constructor_%s()"
782                 % self.name,
783                 "{",
784                 '  static const char name[] = "%s";' % self.name,
785                 '  static const char name_with_crc[] = "%s_%s";'
786                 % (self.name, self.crc[2:]),
787                 "  static vapi_message_desc_t %s = {"
788                 % self.get_c_metadata_struct_name(),
789                 "    name,",
790                 "    sizeof(name) - 1,",
791                 "    name_with_crc,",
792                 "    sizeof(name_with_crc) - 1,",
793                 "    true," if has_context else "    false,",
794                 "    offsetof(%s, context)," % self.header.get_c_name()
795                 if has_context
796                 else "    0,",
797                 ("    offsetof(%s, payload)," % self.get_c_name())
798                 if self.has_payload()
799                 else "    VAPI_INVALID_MSG_ID,",
800                 "    (verify_msg_size_fn_t)%s," % self.get_verify_msg_size_func_name(),
801                 "    (generic_swap_fn_t)%s," % self.get_swap_to_be_func_name(),
802                 "    (generic_swap_fn_t)%s," % self.get_swap_to_host_func_name(),
803                 "    VAPI_INVALID_MSG_ID,",
804                 "  };",
805                 "",
806                 "  %s = vapi_register_msg(&%s);"
807                 % (self.get_msg_id_name(), self.get_c_metadata_struct_name()),
808                 '  VAPI_DBG("Assigned msg id %%d to %s", %s);'
809                 % (self.name, self.get_msg_id_name()),
810                 "}",
811             ]
812         )
813
814
815 def emit_definition(parser, json_file, emitted, o):
816     if o in emitted:
817         return
818     if o.name in ("msg_header1_t", "msg_header2_t"):
819         return
820     if hasattr(o, "depends"):
821         for x in o.depends:
822             emit_definition(parser, json_file, emitted, x)
823     if hasattr(o, "reply"):
824         emit_definition(parser, json_file, emitted, o.reply)
825     if hasattr(o, "stream_msg"):
826         emit_definition(parser, json_file, emitted, o.stream_msg)
827     if hasattr(o, "get_c_def"):
828         if (
829             o not in parser.enums_by_json[json_file]
830             and o not in parser.types_by_json[json_file]
831             and o not in parser.unions_by_json[json_file]
832             and o.name not in parser.messages_by_json[json_file]
833             and o not in parser.aliases_by_json[json_file]
834         ):
835             return
836         guard = "defined_%s" % o.get_c_name()
837         print("#ifndef %s" % guard)
838         print("#define %s" % guard)
839         print("%s" % o.get_c_def())
840         print("")
841         function_attrs = "static inline "
842         if o.name in parser.messages_by_json[json_file]:
843             if o.has_payload():
844                 print("%s%s" % (function_attrs, o.get_swap_payload_to_be_func_def()))
845                 print("")
846                 print("%s%s" % (function_attrs, o.get_swap_payload_to_host_func_def()))
847                 print("")
848             print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
849             print("")
850             print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
851             print("")
852             print("%s%s" % (function_attrs, o.get_calc_msg_size_func_def()))
853             print("")
854             print("%s%s" % (function_attrs, o.get_verify_msg_size_func_def()))
855             if not o.is_reply and not o.is_event and not o.is_stream:
856                 print("")
857                 print("%s%s" % (function_attrs, o.get_alloc_func_def()))
858                 print("")
859                 print("%s%s" % (function_attrs, o.get_op_func_def()))
860             print("")
861             print("%s" % o.get_c_constructor())
862             if (o.is_reply or o.is_event) and not o.is_stream:
863                 print("")
864                 print("%s%s;" % (function_attrs, o.get_event_cb_func_def()))
865         elif hasattr(o, "get_swap_to_be_func_def"):
866             print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
867             print("")
868             print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
869         print("#endif")
870         print("")
871     emitted.append(o)
872
873
874 def gen_json_unified_header(parser, logger, j, io, name):
875     d, f = os.path.split(j)
876     logger.info("Generating header `%s'" % name)
877     orig_stdout = sys.stdout
878     sys.stdout = io
879     include_guard = "__included_%s" % (
880         j.replace(".", "_")
881         .replace("/", "_")
882         .replace("-", "_")
883         .replace("+", "_")
884         .replace("@", "_")
885     )
886     print("#ifndef %s" % include_guard)
887     print("#define %s" % include_guard)
888     print("")
889     print("#include <stdlib.h>")
890     print("#include <stddef.h>")
891     print("#include <arpa/inet.h>")
892     print("#include <vapi/vapi_internal.h>")
893     print("#include <vapi/vapi.h>")
894     print("#include <vapi/vapi_dbg.h>")
895     print("")
896     print("#ifdef __cplusplus")
897     print('extern "C" {')
898     print("#endif")
899
900     print("#ifndef __vl_api_string_swap_fns_defined__")
901     print("#define __vl_api_string_swap_fns_defined__")
902     print("")
903     print("#include <vlibapi/api_types.h>")
904     print("")
905     function_attrs = "static inline "
906     o = parser.types["vl_api_string_t"]
907     print("%s%s" % (function_attrs, o.get_swap_to_be_func_def()))
908     print("")
909     print("%s%s" % (function_attrs, o.get_swap_to_host_func_def()))
910     print("")
911     print("#endif //__vl_api_string_swap_fns_defined__")
912
913     if name == "memclnt.api.vapi.h":
914         print("")
915         print(
916             "static inline vapi_error_e vapi_send_with_control_ping "
917             "(vapi_ctx_t ctx, void * msg, u32 context);"
918         )
919     elif name == "vlib.api.vapi.h":
920         print("#include <vapi/memclnt.api.vapi.h>")
921     else:
922         print("#include <vapi/vlib.api.vapi.h>")
923     print("")
924     for m in parser.messages_by_json[j].values():
925         print("extern vapi_msg_id_t %s;" % m.get_msg_id_name())
926     print("")
927     print(
928         "#define DEFINE_VAPI_MSG_IDS_%s\\"
929         % f.replace(".", "_").replace("/", "_").replace("-", "_").upper()
930     )
931     print(
932         "\\\n".join(
933             [
934                 "  vapi_msg_id_t %s;" % m.get_msg_id_name()
935                 for m in parser.messages_by_json[j].values()
936             ]
937         )
938     )
939     print("")
940     print("")
941     emitted = []
942     for e in parser.enums_by_json[j]:
943         emit_definition(parser, j, emitted, e)
944     for u in parser.unions_by_json[j]:
945         emit_definition(parser, j, emitted, u)
946     for t in parser.types_by_json[j]:
947         emit_definition(parser, j, emitted, t)
948     for a in parser.aliases_by_json[j]:
949         emit_definition(parser, j, emitted, a)
950     for m in parser.messages_by_json[j].values():
951         emit_definition(parser, j, emitted, m)
952
953     print("")
954
955     if name == "vlib.api.vapi.h":
956         vapi_send_with_control_ping_function = """
957 static inline vapi_error_e
958 vapi_send_with_control_ping (vapi_ctx_t ctx, void *msg, u32 context)
959 {
960   vapi_msg_control_ping *ping = vapi_alloc_control_ping (ctx);
961   if (!ping)
962     {
963       return VAPI_ENOMEM;
964     }
965   ping->header.context = context;
966   vapi_msg_control_ping_hton (ping);
967   return vapi_send2 (ctx, msg, ping);
968 }
969 """
970         print("%s" % vapi_send_with_control_ping_function)
971         print("")
972
973     print("#ifdef __cplusplus")
974     print("}")
975     print("#endif")
976     print("")
977     print("#endif")
978     sys.stdout = orig_stdout
979
980
981 def json_to_c_header_name(json_name):
982     if json_name.endswith(".json"):
983         return "%s.vapi.h" % os.path.splitext(json_name)[0]
984     raise Exception("Unexpected json name `%s'!" % json_name)
985
986
987 def gen_c_unified_headers(parser, logger, prefix, remove_path):
988     if prefix == "" or prefix is None:
989         prefix = ""
990     else:
991         prefix = "%s/" % prefix
992     for j in parser.json_files:
993         if remove_path:
994             d, f = os.path.split(j)
995         else:
996             f = j
997         with open("%s%s" % (prefix, json_to_c_header_name(f)), "w") as io:
998             gen_json_unified_header(parser, logger, j, io, json_to_c_header_name(f))
999
1000
1001 if __name__ == "__main__":
1002     try:
1003         verbose = int(os.getenv("V", 0))
1004     except:
1005         verbose = 0
1006
1007     if verbose >= 2:
1008         log_level = 10
1009     elif verbose == 1:
1010         log_level = 20
1011     else:
1012         log_level = 40
1013
1014     logging.basicConfig(stream=sys.stdout, level=log_level)
1015     logger = logging.getLogger("VAPI C GEN")
1016     logger.setLevel(log_level)
1017
1018     argparser = argparse.ArgumentParser(description="VPP C API generator")
1019     argparser.add_argument(
1020         "files",
1021         metavar="api-file",
1022         action="append",
1023         type=str,
1024         help="json api file" "(may be specified multiple times)",
1025     )
1026     argparser.add_argument("--prefix", action="store", default=None, help="path prefix")
1027     argparser.add_argument(
1028         "--remove-path", action="store_true", help="remove path from filename"
1029     )
1030     args = argparser.parse_args()
1031
1032     jsonparser = JsonParser(
1033         logger,
1034         args.files,
1035         simple_type_class=CSimpleType,
1036         enum_class=CEnum,
1037         union_class=CUnion,
1038         struct_type_class=CStructType,
1039         field_class=CField,
1040         message_class=CMessage,
1041         alias_class=CAlias,
1042     )
1043
1044     # not using the model of having separate generated header and code files
1045     # with generated symbols present in shared library (per discussion with
1046     # Damjan), to avoid symbol version issues in .so
1047     # gen_c_headers_and_code(jsonparser, logger, args.prefix)
1048
1049     gen_c_unified_headers(jsonparser, logger, args.prefix, args.remove_path)
1050
1051     for e in jsonparser.exceptions:
1052         logger.warning(e)