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