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