vppinfra: Improve code portability
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
index 41317f8..c2e1e7d 100755 (executable)
@@ -123,7 +123,6 @@ class ToJSON:
                 )
             )
         else:
-
             write(
                 '    cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'.format(
                     n=o.fieldname
@@ -366,7 +365,7 @@ class FromJSON:
             write("    char *p = cJSON_GetStringValue(item);\n")
             write("    size_t plen = strlen(p);\n")
             write(
-                "    {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n".format(
+                "    {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen);\n".format(
                     msgvar=msgvar, msgsize=msgsize
                 )
             )
@@ -435,7 +434,7 @@ class FromJSON:
         cJSON *array = cJSON_GetObjectItem(o, "{n}");
         int size = cJSON_GetArraySize(array);
         {lfield} = size;
-        {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
+        {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size);
         {t} *d = (void *){realloc} + {msgsize};
         {msgsize} += sizeof({t}) * size;
         for (i = 0; i < size; i++) {{
@@ -462,12 +461,12 @@ class FromJSON:
 
                 write(
                     "    {realloc} = cJSON_realloc({realloc}, {msgsize} + "
-                    "vec_len(s), {msgsize});\n".format(
+                    "vec_len(s));\n".format(
                         msgvar=msgvar, msgsize=msgsize, realloc=realloc
                     )
                 )
                 write(
-                    "    memcpy((void *){realloc} + {msgsize}, s, "
+                    "    clib_memcpy((void *){realloc} + {msgsize}, s, "
                     "vec_len(s));\n".format(realloc=realloc, msgsize=msgsize)
                 )
                 write("    {msgsize} += vec_len(s);\n".format(msgsize=msgsize))
@@ -751,6 +750,17 @@ TOP_BOILERPLATE = """\
 #endif
 
 #define VL_API_PACKED(x) x __attribute__ ((packed))
+
+/*
+ * Note: VL_API_MAX_ARRAY_SIZE is set to an arbitrarily large limit.
+ *
+ * However, any message with a ~2 billion element array is likely to break the
+ * api handling long before this limit causes array element endian issues.
+ *
+ * Applications should be written to create reasonable api messages.
+ */
+#define VL_API_MAX_ARRAY_SIZE 0x7fffffff
+
 """
 
 BOTTOM_BOILERPLATE = """\
@@ -1133,9 +1143,15 @@ ENDIAN_STRINGS = {
 }
 
 
+def get_endian_string(o, fieldtype):
+    """Return proper endian string conversion function"""
+    return ENDIAN_STRINGS[fieldtype]
+
+
 def endianfun_array(o):
     """Generate endian functions for arrays"""
     forloop = """\
+    ASSERT((u32){length} <= (u32)VL_API_MAX_ARRAY_SIZE);
     for (i = 0; i < {length}; i++) {{
         a->{name}[i] = {format}(a->{name}[i]);
     }}
@@ -1143,7 +1159,7 @@ def endianfun_array(o):
 
     forloop_format = """\
     for (i = 0; i < {length}; i++) {{
-        {type}_endian(&a->{name}[i]);
+        {type}_endian(&a->{name}[i], to_net);
     }}
 """
 
@@ -1152,9 +1168,20 @@ def endianfun_array(o):
         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
     else:
         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
+        if o.lengthfield:
+            output += (
+                f"    u32 count = to_net ? clib_host_to_net_u32(a->{o.lengthfield}) : "
+                f"a->{o.lengthfield};\n"
+            )
+            lfield = "count"
+        else:
+            lfield = o.length
+
         if o.fieldtype in ENDIAN_STRINGS:
             output += forloop.format(
-                length=lfield, format=ENDIAN_STRINGS[o.fieldtype], name=o.fieldname
+                length=lfield,
+                format=get_endian_string(o, o.fieldtype),
+                name=o.fieldname,
             )
         else:
             output += forloop_format.format(
@@ -1181,10 +1208,10 @@ def endianfun_obj(o):
         return output
     if o.fieldtype in ENDIAN_STRINGS:
         output += "    a->{name} = {format}(a->{name});\n".format(
-            name=o.fieldname, format=ENDIAN_STRINGS[o.fieldtype]
+            name=o.fieldname, format=get_endian_string(o, o.fieldtype)
         )
     elif o.fieldtype.startswith("vl_api_"):
-        output += "    {type}_endian(&a->{name});\n".format(
+        output += "    {type}_endian(&a->{name}, to_net);\n".format(
             type=o.fieldtype, name=o.fieldname
         )
     else:
@@ -1203,17 +1230,20 @@ def endianfun(objs, modulename):
 #define included_{module}_endianfun
 
 #undef clib_net_to_host_uword
+#undef clib_host_to_net_uword
 #ifdef LP64
 #define clib_net_to_host_uword clib_net_to_host_u64
+#define clib_host_to_net_uword clib_host_to_net_u64
 #else
 #define clib_net_to_host_uword clib_net_to_host_u32
+#define clib_host_to_net_uword clib_host_to_net_u32
 #endif
 
 """
     output = output.format(module=modulename)
 
     signature = """\
-static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
+static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a, bool to_net)
 {{
     int i __attribute__((unused));
 """
@@ -1222,7 +1252,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
         if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
             output += signature.format(name=t.name)
             if t.enumtype in ENDIAN_STRINGS:
-                output += "    *a = {}(*a);\n".format(ENDIAN_STRINGS[t.enumtype])
+                output += "    *a = {}(*a);\n".format(get_endian_string(t, t.enumtype))
             else:
                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
                     name=t.name
@@ -1242,7 +1272,9 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
                     name=t.name
                 )
             elif t.alias["type"] in FORMAT_STRINGS:
-                output += "    *a = {}(*a);\n".format(ENDIAN_STRINGS[t.alias["type"]])
+                output += "    *a = {}(*a);\n".format(
+                    get_endian_string(t, t.alias["type"])
+                )
             else:
                 output += "    /* Not Implemented yet {} */".format(t.name)
             output += "}\n\n"
@@ -1250,11 +1282,6 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
 
         output += signature.format(name=t.name)
 
-        # make Array type appear before the others:
-        # some arrays have dynamic length, and we want to iterate over
-        # them before changing endiann for the length field
-        t.block.sort(key=lambda x: x.type)
-
         for o in t.block:
             output += endianfun_obj(o)
         output += "}\n\n"
@@ -1322,7 +1349,7 @@ static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
                             )
                         lf = m[0]
                         if lf.fieldtype in ENDIAN_STRINGS:
-                            output += f" + {ENDIAN_STRINGS[lf.fieldtype]}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
+                            output += f" + {get_endian_string(b, lf.fieldtype)}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
                         elif lf.fieldtype == "u8":
                             output += (
                                 f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
@@ -1521,6 +1548,7 @@ def generate_c_boilerplate(services, defines, counters, file_crc, module, stream
 #include "{module}.api.h"
 #undef vl_printfun
 
+#include "{module}.api_json.h"
 """
 
     write(hdr.format(module=module))
@@ -1533,6 +1561,7 @@ def generate_c_boilerplate(services, defines, counters, file_crc, module, stream
             '   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
             "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
         )
+        write(f"   vec_add1(am->json_api_repr, (u8 *)json_api_repr_{module});\n")
 
     for d in defines:
         write(
@@ -1797,7 +1826,7 @@ api_{n} (cJSON *o)
   }}
 
   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
-  vl_api_{n}_t_endian(mp);
+  vl_api_{n}_t_endian(mp, 1);
   vac_write((char *)mp, len);
   cJSON_free(mp);
 
@@ -1812,7 +1841,7 @@ api_{n} (cJSON *o)
     return 0;
   }}
   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
-  vl_api_{r}_t_endian(rmp);
+  vl_api_{r}_t_endian(rmp, 0);
   return vl_api_{r}_t_tojson(rmp);
 }}
 
@@ -1830,7 +1859,7 @@ api_{n} (cJSON *o)
       return 0;
   }}
   mp->_vl_msg_id = msg_id;
-  vl_api_{n}_t_endian(mp);
+  vl_api_{n}_t_endian(mp, 1);
   vac_write((char *)mp, len);
   cJSON_free(mp);
 
@@ -1864,7 +1893,7 @@ api_{n} (cJSON *o)
             return 0;
         }}
         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
-        vl_api_{r}_t_endian(rmp);
+        vl_api_{r}_t_endian(rmp, 0);
         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
     }}
   }}
@@ -1886,7 +1915,7 @@ api_{n} (cJSON *o)
   }}
   mp->_vl_msg_id = msg_id;
 
-  vl_api_{n}_t_endian(mp);
+  vl_api_{n}_t_endian(mp, 1);
   vac_write((char *)mp, len);
   cJSON_free(mp);
 
@@ -1907,14 +1936,14 @@ api_{n} (cJSON *o)
     u16 msg_id = ntohs(*((u16 *)p));
     if (msg_id == reply_msg_id) {{
         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
-        vl_api_{r}_t_endian(rmp);
+        vl_api_{r}_t_endian(rmp, 0);
         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
         break;
     }}
 
     if (msg_id == details_msg_id) {{
         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
-        vl_api_{d}_t_endian(rmp);
+        vl_api_{d}_t_endian(rmp, 0);
         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
     }}
   }}