api: vat2 and json autogeneration for api messages 90/29890/13
authorOle Troan <ot@cisco.com>
Wed, 18 Nov 2020 18:17:48 +0000 (19:17 +0100)
committerNeale Ranns <nranns@cisco.com>
Wed, 25 Nov 2020 08:25:50 +0000 (08:25 +0000)
VAT2: A completely auto-generated replacement of VAT.
Reads input message in JSON from stdin and outputs received messages in JSON.

A VAT2 plugin is automatically built for a .api file.
There no longer a need for a separate _test.c.

Example:
vat2 show_version {}
{
        "_msgname":     "show_version_reply",
        "retval":       0,
        "program":      "vpe",
        "version":      "21.01-rc0~411-gf6eb348a6",
        "build_date":   "2020-11-19T09:49:25",
        "build_directory":      "/vpp/autogen3"
}

vat2 sw_interface_dump '{"sw_if_index": -1,
                         "name_filter_valid": 0,
                         "name_filter": ""}'
[{
                "_msgname":     "sw_interface_details",
                "sw_if_index":  0,
                "sup_sw_if_index":      0,
                "l2_address":   "00:00:00:00:00:00",
                "flags":        "Invalid ENUM",
                "type": "IF_API_TYPE_HARDWARE",
                "link_duplex":  "LINK_DUPLEX_API_UNKNOWN",
                "link_speed":   0,
                "link_mtu":     0,
                "mtu":  [0, 0, 0, 0],
                "sub_id":       0,
                "sub_number_of_tags":   0,
                "sub_outer_vlan_id":    0,
                "sub_inner_vlan_id":    0,
                "sub_if_flags": "Invalid ENUM",
                "vtr_op":       0,
                "vtr_push_dot1q":       0,
                "vtr_tag1":     0,
                "vtr_tag2":     0,
                "outer_tag":    0,
                "b_dmac":       "00:00:00:00:00:00",
                "b_smac":       "00:00:00:00:00:00",
                "b_vlanid":     0,
                "i_sid":        0,
                "interface_name":       "local0",
                "interface_dev_type":   "local",
                "tag":  ""
        }]

This is the first phase and vat2 is not integrated in packaging yet.

Type: feature
Signed-off-by: Ole Troan <ot@cisco.com>
Change-Id: Ib45ddeafb180ea7da8c5dc274a9274d7a4edc876
Signed-off-by: Ole Troan <ot@cisco.com>
45 files changed:
extras/rpm/Makefile
src/CMakeLists.txt
src/cmake/api.cmake
src/cmake/library.cmake
src/cmake/plugin.cmake
src/plugins/acl/CMakeLists.txt
src/plugins/acl/acl.api
src/plugins/acl/acl.c
src/plugins/acl/acl_test.c
src/plugins/acl/manual_fns.h [deleted file]
src/plugins/dns/dns.api
src/plugins/dns/dns.c
src/plugins/flowprobe/flowprobe.api
src/plugins/flowprobe/flowprobe.c
src/plugins/gbp/CMakeLists.txt
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_api_print.h [deleted file]
src/plugins/lb/api.c
src/plugins/lb/lb.api
src/plugins/map/map_api.c
src/plugins/nat/nat44.api
src/plugins/nat/nat44_api.c
src/plugins/stn/stn.api
src/plugins/stn/stn_api.c
src/tools/vppapigen/vppapigen.py
src/tools/vppapigen/vppapigen_c.py
src/vat2/CMakeLists.txt [new file with mode: 0644]
src/vat2/jsonconvert.c [new file with mode: 0644]
src/vat2/jsonconvert.h [new file with mode: 0644]
src/vat2/main.c [new file with mode: 0644]
src/vat2/plugin.c [new file with mode: 0644]
src/vat2/vat2.h [new file with mode: 0644]
src/vat2/vat2_helpers.h [new file with mode: 0644]
src/vnet/CMakeLists.txt
src/vnet/ip/ip.api
src/vnet/l2/l2.api
src/vnet/l2/l2_api.c
src/vnet/mpls/mpls.api
src/vpp-api/client/client.c
src/vpp-api/client/vppapiclient.h
src/vpp/CMakeLists.txt
src/vppinfra/CMakeLists.txt
src/vppinfra/cJSON.c [new file with mode: 0644]
src/vppinfra/cJSON.h [new file with mode: 0644]

index 0736b7f..b06c9fb 100644 (file)
@@ -30,7 +30,8 @@ SPEC_FILE='vpp.spec'
 
 spec:
        @echo $(TARBALL)
-       mkdir -p $(RPMBUILD)/{RPMS,SRPMS,BUILD,SOURCES,SPECS}
+       mkdir -p $(RPMBUILD)/RPMS $(RPMBUILD)/SRPMS $(RPMBUILD)/BUILD \
+             $(RPMBUILD)/SOURCES $(RPMBUILD)/SPECS
        cp $(TARBALL) $(RPMBUILD)/SOURCES/vpp-$(VERSION)-$(RELEASE).tar.xz
        cp $(SPEC_FILE) $(RPMBUILD)/SPECS
 
index 6fc243c..8947c8a 100644 (file)
@@ -164,7 +164,7 @@ if(VPP_HOST_TOOLS_ONLY)
 elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
   find_package(OpenSSL REQUIRED)
   set(SUBDIRS
-    vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vcl plugins
+    vppinfra svm vlib vlibmemory vlibapi vnet vpp vat vat2 vcl plugins
     vpp-api tools/vppapigen tools/g2 tools/perftool cmake pkg
     tools/appimage
   )
index fe2cad6..f3af687 100644 (file)
@@ -23,13 +23,32 @@ function(vpp_generate_api_c_header file)
   if (VPP_INCLUDE_DIR)
     set(includedir "--includedir" ${VPP_INCLUDE_DIR})
   endif()
-  add_custom_command (OUTPUT ${output_name}
+
+  set(OUTPUT_HEADERS
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}.h"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}_fromjson.h"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}_tojson.h"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}_enum.h"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}_types.h"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}.c"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}_test.c"
+    "${CMAKE_CURRENT_BINARY_DIR}/${file}_test2.c"
+  )
+
+  add_custom_command (
+    OUTPUT ${OUTPUT_HEADERS}
     COMMAND mkdir -p ${output_dir}
     COMMAND ${VPP_APIGEN}
     ARGS ${includedir} --includedir ${CMAKE_SOURCE_DIR} --input ${CMAKE_CURRENT_SOURCE_DIR}/${file} --outputdir ${output_dir} --output ${output_name}
     DEPENDS ${VPP_APIGEN} ${CMAKE_CURRENT_SOURCE_DIR}/${file}
     COMMENT "Generating API header ${output_name}"
   )
+  get_filename_component(barename ${file} NAME)
+  set(t ${barename}_deps)
+  if (NOT TARGET ${t})
+    add_custom_target(${t} ALL DEPENDS ${OUTPUT_HEADERS})
+    add_dependencies(api_headers ${t})
+  endif()
 endfunction()
 
 function(vpp_generate_api_json_header file dir component)
index a5b6c76..3f17e30 100644 (file)
@@ -57,6 +57,8 @@ macro(add_vpp_library lib)
        FILES ${file} ${CMAKE_CURRENT_BINARY_DIR}/${file}.h
        ${CMAKE_CURRENT_BINARY_DIR}/${file}_enum.h
        ${CMAKE_CURRENT_BINARY_DIR}/${file}_types.h
+       ${CMAKE_CURRENT_BINARY_DIR}/${file}_tojson.h
+       ${CMAKE_CURRENT_BINARY_DIR}/${file}_fromjson.h
        DESTINATION include/${lib}/${dir}
        COMPONENT vpp-dev
       )
@@ -93,3 +95,33 @@ function (add_vpp_headers path)
     )
   endforeach()
 endfunction()
+
+macro(add_vpp_test_library lib)
+  cmake_parse_arguments(TEST
+    ""
+    ""
+    ${ARGN}
+  )
+
+  foreach(file ${ARGN})
+    get_filename_component(name ${file} NAME_WE)
+    set(test_lib ${lib}_${name}_plugin)
+    add_library(${test_lib} SHARED ${file}_test2.c)
+    if(NOT VPP_EXTERNAL_PROJECT)
+      add_dependencies(${test_lib} api_headers)
+    endif()
+    include_directories(${CMAKE_CURRENT_BINARY_DIR})
+    set_target_properties(${test_lib} PROPERTIES NO_SONAME 1)
+    set_target_properties(${test_lib} PROPERTIES
+      PREFIX ""
+      LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/vat2_plugins)
+
+    # Later: Install and package
+    # install .so
+    #install(
+    #  TARGETS ${test_lib}
+    #  DESTINATION ${VPP_LIBRARY_DIR}/vat2_plugins
+    #  #COMPONENT ${ARG_COMPONENT}
+    #  )
+  endforeach()
+endmacro()
index 2aae895..c165b59 100644 (file)
@@ -15,7 +15,7 @@ macro(add_vpp_plugin name)
   cmake_parse_arguments(PLUGIN
     ""
     "LINK_FLAGS;COMPONENT;DEV_COMPONENT"
-    "SOURCES;API_FILES;MULTIARCH_SOURCES;LINK_LIBRARIES;INSTALL_HEADERS;API_TEST_SOURCES"
+    "SOURCES;API_FILES;MULTIARCH_SOURCES;LINK_LIBRARIES;INSTALL_HEADERS;API_TEST_SOURCES;"
     ${ARGN}
   )
   set(plugin_name ${name}_plugin)
@@ -99,6 +99,10 @@ macro(add_vpp_plugin name)
       COMPONENT ${PLUGIN_COMPONENT}
     )
   endif()
+  if (PLUGIN_API_FILES)
+    add_vpp_test_library(${name}_test_plugin ${PLUGIN_API_FILES})
+  endif()
+
   install(
     TARGETS ${plugin_name}
     DESTINATION ${VPP_LIBRARY_DIR}/vpp_plugins
index 78cc818..c43dd23 100644 (file)
@@ -30,7 +30,4 @@ add_vpp_plugin(acl
 
   API_TEST_SOURCES
   acl_test.c
-
-  INSTALL_HEADERS
-  manual_fns.h
 )
index 25c231d..a4706c3 100644 (file)
@@ -102,7 +102,7 @@ define acl_plugin_get_conn_table_max_entries_reply
     @r - Rules for this access-list
 */
 
-manual_print manual_endian define acl_add_replace
+ define acl_add_replace
 {
   u32 client_index;
   u32 context;
@@ -132,7 +132,7 @@ define acl_add_replace_reply
     @param acl_index - ACL index to delete
 */
 
-autoreply manual_print define acl_del
+autoreply define acl_del
 {
   u32 client_index;
   u32 context;
@@ -151,7 +151,7 @@ autoreply manual_print define acl_del
     @param acl_index - index of ACL for the operation
 */
 
-autoreply manual_print define acl_interface_add_del
+autoreply define acl_interface_add_del
 {
   u32 client_index;
   u32 context;
@@ -175,7 +175,7 @@ autoreply manual_print define acl_interface_add_del
     @param acls - vector of ACL indices
 */
 
-autoreply manual_print define acl_interface_set_acl_list
+autoreply define acl_interface_set_acl_list
 {
   u32 client_index;
   u32 context;
@@ -213,7 +213,7 @@ define acl_dump
     @param r - Array of rules within this ACL
 */
 
-manual_endian manual_print define acl_details
+define acl_details
 {
   u32 context;
   u32 acl_index;
@@ -261,7 +261,7 @@ define acl_interface_list_details
     @param r - vector of MACIP ACL rules
 */
 
-manual_endian manual_print define macip_acl_add
+define macip_acl_add
 {
   u32 client_index;
   u32 context;
@@ -293,7 +293,7 @@ define macip_acl_add_reply
     @param r - vector of MACIP ACL rules
 */
 
-manual_endian manual_print define macip_acl_add_replace
+define macip_acl_add_replace
 {
   u32 client_index;
   u32 context;
@@ -323,7 +323,7 @@ define macip_acl_add_replace_reply
     @param acl_index - MACIP ACL index to delete
 */
 
-autoreply manual_print define macip_acl_del
+autoreply define macip_acl_del
 {
   u32 client_index;
   u32 context;
@@ -339,7 +339,7 @@ autoreply manual_print define macip_acl_del
     @param acl_index - MACIP ACL index
 */
 
-autoreply manual_print define macip_acl_interface_add_del
+autoreply  define macip_acl_interface_add_del
 {
   u32 client_index;
   u32 context;
@@ -372,7 +372,7 @@ define macip_acl_dump
     @param r - rules comprising this MACIP ACL
 */
 
-manual_endian manual_print define macip_acl_details
+  define macip_acl_details
 {
   u32 context;
   u32 acl_index;
@@ -442,7 +442,7 @@ define macip_acl_interface_list_details
     @param whitelist - vector of whitelisted ethertypes
 */
 
-autoreply manual_print define acl_interface_set_etype_whitelist
+autoreply  define acl_interface_set_etype_whitelist
 {
   u32 client_index;
   u32 context;
index b4770a7..b18e851 100644 (file)
@@ -25,6 +25,9 @@
 #include <vpp/app/version.h>
 
 #include <vnet/ethernet/ethernet_types_api.h>
+#include <vnet/ip/format.h>
+#include <vnet/ethernet/ethernet.h>
+#include <vnet/ip/ip_types_api.h>
 
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
@@ -34,7 +37,6 @@
 #include <acl/acl.api_types.h>
 
 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
-#include "manual_fns.h"
 
 #include "fa_node.h"
 #include "public_inlines.h"
@@ -132,6 +134,26 @@ print_cli_and_reset (vlib_main_t * vm, u8 * out0)
 
 typedef void (*acl_vector_print_func_t) (vlib_main_t * vm, u8 * out0);
 
+static inline u8 *
+format_acl_action (u8 * s, u8 action)
+{
+  switch (action)
+    {
+    case 0:
+      s = format (s, "deny");
+      break;
+    case 1:
+      s = format (s, "permit");
+      break;
+    case 2:
+      s = format (s, "permit+reflect");
+      break;
+    default:
+      s = format (s, "action %d", action);
+    }
+  return (s);
+}
+
 static void
 acl_print_acl_x (acl_vector_print_func_t vpr, vlib_main_t * vm,
                 acl_main_t * am, int acl_index)
@@ -629,16 +651,16 @@ acl_interface_set_inout_acl_list (acl_main_t * am, u32 sw_if_index,
 
 
   u32 **pinout_lc_index_by_sw_if_index =
-    is_input ? &am->
-    input_lc_index_by_sw_if_index : &am->output_lc_index_by_sw_if_index;
+    is_input ? &am->input_lc_index_by_sw_if_index : &am->
+    output_lc_index_by_sw_if_index;
 
   u32 ***pinout_acl_vec_by_sw_if_index =
-    is_input ? &am->
-    input_acl_vec_by_sw_if_index : &am->output_acl_vec_by_sw_if_index;
+    is_input ? &am->input_acl_vec_by_sw_if_index : &am->
+    output_acl_vec_by_sw_if_index;
 
   u32 ***pinout_sw_if_index_vec_by_acl =
-    is_input ? &am->
-    input_sw_if_index_vec_by_acl : &am->output_sw_if_index_vec_by_acl;
+    is_input ? &am->input_sw_if_index_vec_by_acl : &am->
+    output_sw_if_index_vec_by_acl;
 
   vec_validate ((*pinout_acl_vec_by_sw_if_index), sw_if_index);
 
@@ -713,7 +735,9 @@ acl_interface_set_inout_acl_list (acl_main_t * am, u32 sw_if_index,
     {
       if (~0 != (*pinout_lc_index_by_sw_if_index)[sw_if_index])
        {
-         acl_plugin.put_lookup_context_index ((*pinout_lc_index_by_sw_if_index)[sw_if_index]);
+         acl_plugin.
+           put_lookup_context_index ((*pinout_lc_index_by_sw_if_index)
+                                     [sw_if_index]);
          (*pinout_lc_index_by_sw_if_index)[sw_if_index] = ~0;
        }
     }
@@ -750,8 +774,8 @@ acl_interface_add_del_inout_acl (u32 sw_if_index, u8 is_add, u8 is_input,
     : VNET_API_ERROR_ACL_IN_USE_OUTBOUND;
 
   u32 ***pinout_acl_vec_by_sw_if_index =
-    is_input ? &am->
-    input_acl_vec_by_sw_if_index : &am->output_acl_vec_by_sw_if_index;
+    is_input ? &am->input_acl_vec_by_sw_if_index : &am->
+    output_acl_vec_by_sw_if_index;
   int rv = 0;
   if (is_add)
     {
@@ -1435,9 +1459,9 @@ macip_create_classify_tables (acl_main_t * am, u32 macip_acl_index)
 
                  vnet_classify_add_del_session (cm, tag_table,
                                                 mask,
-                                                a->
-                                                rules[i].is_permit ? ~0 : 0,
-                                                i, 0, action, metadata, 1);
+                                                a->rules[i].
+                                                is_permit ? ~0 : 0, i, 0,
+                                                action, metadata, 1);
                }
            }
        }
@@ -2280,7 +2304,8 @@ static void
        if (~0 != am->macip_acl_by_sw_if_index[sw_if_index])
          {
            send_macip_acl_interface_list_details (am, reg, sw_if_index,
-                                                  am->macip_acl_by_sw_if_index
+                                                  am->
+                                                  macip_acl_by_sw_if_index
                                                   [sw_if_index],
                                                   mp->context);
          }
index c139b32..79058cd 100644 (file)
@@ -37,7 +37,6 @@ uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
 #include <acl/acl.api_enum.h>
 #include <acl/acl.api_types.h>
 #define vl_print(handle, ...)
-#include <acl/manual_fns.h>
 #undef vl_print
 #define vl_endianfun            /* define message structures */
 #include <acl/acl.api.h>
diff --git a/src/plugins/acl/manual_fns.h b/src/plugins/acl/manual_fns.h
deleted file mode 100644 (file)
index f2585a9..0000000
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (c) 2016 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef included_manual_fns_h
-#define included_manual_fns_h
-
-#include <vnet/ip/format.h>
-#include <vnet/ethernet/ethernet.h>
-#include <vnet/ip/ip_types_api.h>
-
-#define vl_endianfun            /* define message structures */
-#include <acl/acl_types.api.h>
-#undef vl_endianfun
-
-/* Macro to finish up custom dump fns */
-#define PRINT_S \
-    vec_add1 (s, 0);                            \
-    vl_print (handle, (char *)s);               \
-    vec_free (s);
-
-static inline void
-vl_api_acl_rule_t_array_endian(vl_api_acl_rule_t *rules, u32 count)
-{
-  u32 i;
-  for(i=0; i<count; i++) {
-    vl_api_acl_rule_t_endian (&rules[i]);
-  }
-}
-
-static inline void
-vl_api_macip_acl_rule_t_array_endian(vl_api_macip_acl_rule_t *rules, u32 count)
-{
-  u32 i;
-  for(i=0; i<count; i++) {
-    vl_api_macip_acl_rule_t_endian (&rules[i]);
-  }
-}
-
-static inline void
-vl_api_acl_details_t_endian (vl_api_acl_details_t * a)
-{
-  a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id);
-  a->context = clib_net_to_host_u32 (a->context);
-  a->acl_index = clib_net_to_host_u32 (a->acl_index);
-  /* a->tag[0..63] = a->tag[0..63] (no-op) */
-  a->count = clib_net_to_host_u32 (a->count);
-  vl_api_acl_rule_t_array_endian (a->r, a->count);
-}
-
-static inline void
-vl_api_macip_acl_details_t_endian (vl_api_macip_acl_details_t * a)
-{
-  a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id);
-  a->context = clib_net_to_host_u32 (a->context);
-  a->acl_index = clib_net_to_host_u32 (a->acl_index);
-  /* a->tag[0..63] = a->tag[0..63] (no-op) */
-  a->count = clib_net_to_host_u32 (a->count);
-  vl_api_macip_acl_rule_t_array_endian (a->r, a->count);
-}
-
-
-static inline void
-vl_api_acl_add_replace_t_endian (vl_api_acl_add_replace_t * a)
-{
-  a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id);
-  a->client_index = clib_net_to_host_u32 (a->client_index);
-  a->context = clib_net_to_host_u32 (a->context);
-  a->acl_index = clib_net_to_host_u32 (a->acl_index);
-  /* a->tag[0..63] = a->tag[0..63] (no-op) */
-  a->count = clib_net_to_host_u32 (a->count);
-  vl_api_acl_rule_t_array_endian (a->r, a->count);
-}
-
-static inline void
-vl_api_macip_acl_add_t_endian (vl_api_macip_acl_add_t * a)
-{
-  a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id);
-  a->client_index = clib_net_to_host_u32 (a->client_index);
-  a->context = clib_net_to_host_u32 (a->context);
-  /* a->tag[0..63] = a->tag[0..63] (no-op) */
-  a->count = clib_net_to_host_u32 (a->count);
-  vl_api_macip_acl_rule_t_array_endian (a->r, a->count);
-}
-
-static inline void
-vl_api_macip_acl_add_replace_t_endian (vl_api_macip_acl_add_replace_t * a)
-{
-  a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id);
-  a->client_index = clib_net_to_host_u32 (a->client_index);
-  a->context = clib_net_to_host_u32 (a->context);
-  a->acl_index = clib_net_to_host_u32 (a->acl_index);
-  /* a->tag[0..63] = a->tag[0..63] (no-op) */
-  a->count = clib_net_to_host_u32 (a->count);
-  vl_api_macip_acl_rule_t_array_endian (a->r, a->count);
-}
-
-static inline u8 *
-format_acl_action(u8 *s, u8 action)
-{
-  switch(action) {
-    case 0:
-      s = format (s, "deny");
-      break;
-    case 1:
-      s = format (s, "permit");
-      break;
-    case 2:
-      s = format (s, "permit+reflect");
-      break;
-    default:
-      s = format (s, "action %d", action);
-  }
-  return(s);
-}
-
-static inline void *
-vl_api_acl_rule_t_print (vl_api_acl_rule_t * a, void *handle)
-{
-  u8 *s;
-  ip_prefix_t src, dst;
-
-  ip_prefix_decode2 (&a->src_prefix, &src);
-  ip_prefix_decode2 (&a->dst_prefix, &dst);
-
-  s = format (0, "  %s ", a->src_prefix.address.af ? "ipv6" : "ipv4");
-  s = format_acl_action (s, a->is_permit);
-  s = format (s, " \\\n");
-
-  s = format (s, "    src %U dst %U \\\n",
-              format_ip_prefix, &src,
-              format_ip_prefix, &dst);
-  s = format (s, "    proto %d \\\n", a->proto);
-  s = format (s, "    sport %d-%d dport %d-%d \\\n",
-             clib_net_to_host_u16 (a->srcport_or_icmptype_first),
-             clib_net_to_host_u16 (a->srcport_or_icmptype_last),
-             clib_net_to_host_u16 (a->dstport_or_icmpcode_first),
-             clib_net_to_host_u16 (a->dstport_or_icmpcode_last));
-
-  s = format (s, "    tcpflags %u mask %u, \\",
-             a->tcp_flags_value, a->tcp_flags_mask);
-  PRINT_S;
-  return handle;
-}
-
-static inline void *
-vl_api_macip_acl_rule_t_print (vl_api_macip_acl_rule_t * a, void *handle)
-{
-  u8 *s;
-  ip_prefix_t src;
-
-  ip_prefix_decode2 (&a->src_prefix, &src);
-
-  s = format (0, "  %s %s \\\n", a->src_prefix.address.af ? "ipv6" : "ipv4",
-              a->is_permit ? "permit" : "deny");
-
-  s = format (s, "    src mac %U mask %U \\\n",
-             format_ethernet_address, a->src_mac,
-             format_ethernet_address, a->src_mac_mask);
-
-  s = format (s, "    src ip %U, \\",
-               format_ip_prefix, &src);
-
-  PRINT_S;
-  return handle;
-}
-
-static inline void *
-vl_api_acl_add_replace_t_print (vl_api_acl_add_replace_t * a, void *handle)
-{
-  u8 *s = 0;
-  int i;
-  u32 acl_index = clib_net_to_host_u32 (a->acl_index);
-  u32 count = clib_net_to_host_u32 (a->count);
-  if (count > 0x100000)
-    {
-      s = format (s, "WARN: acl_add_replace count endianness wrong? Fixup to avoid long loop.\n");
-      count = a->count;
-    }
-
-  s = format (s, "SCRIPT: acl_add_replace %d count %d ",
-             acl_index, count);
-
-  if (a->tag[0])
-    s = format (s, "tag %s ", a->tag);
-
-  s = format(s, "\\\n");
-  PRINT_S;
-
-  for (i = 0; i < count; i++)
-    vl_api_acl_rule_t_print (&a->r[i], handle);
-
-  s = format(s, "\n");
-  PRINT_S;
-  return handle;
-}
-
-static inline void *
-vl_api_acl_del_t_print (vl_api_macip_acl_del_t * a, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: acl_del %d ",
-              clib_host_to_net_u32 (a->acl_index));
-
-  PRINT_S;
-  return handle;
-}
-
-
-static inline void *
-vl_api_acl_details_t_print (vl_api_acl_details_t * a, void *handle)
-{
-  u8 *s = 0;
-  int i;
-  u32 acl_index = clib_net_to_host_u32 (a->acl_index);
-  u32 count = clib_net_to_host_u32 (a->count);
-  if (count > 0x100000)
-    {
-      s = format (s, "WARN: acl_details count endianness wrong? Fixup to avoid long loop.\n");
-      count = a->count;
-    }
-
-  s = format (s, "acl_details index %d count %d ",
-             acl_index, count);
-
-  if (a->tag[0])
-    s = format (s, "tag %s ", a->tag);
-
-  s = format(s, "\n");
-  PRINT_S;
-
-  for (i = 0; i < count; i++)
-    vl_api_acl_rule_t_print (&a->r[i], handle);
-
-  return handle;
-}
-
-static inline void *
-vl_api_macip_acl_details_t_print (vl_api_macip_acl_details_t * a,
-                                 void *handle)
-{
-  u8 *s = 0;
-  int i;
-  u32 acl_index = clib_net_to_host_u32 (a->acl_index);
-  u32 count = clib_net_to_host_u32 (a->count);
-  if (count > 0x100000)
-    {
-      s = format (s, "WARN: macip_acl_details count endianness wrong? Fixup to avoid long loop.\n");
-      count = a->count;
-    }
-
-  s = format (s, "macip_acl_details index %d count %d ",
-             acl_index, count);
-
-  if (a->tag[0])
-    s = format (s, "tag %s ", a->tag);
-
-  s = format(s, "\n");
-  PRINT_S;
-
-  for (i = 0; i < count; i++)
-    vl_api_macip_acl_rule_t_print (&a->r[i], handle);
-
-  return handle;
-}
-
-static inline void *
-vl_api_macip_acl_add_t_print (vl_api_macip_acl_add_t * a, void *handle)
-{
-  u8 *s = 0;
-  int i;
-  u32 count = clib_net_to_host_u32 (a->count);
-  if (count > 0x100000)
-    {
-      s = format (s, "WARN: macip_acl_add count endianness wrong? Fixup to avoid long loop.\n");
-      count = a->count;
-    }
-
-  s = format (s, "SCRIPT: macip_acl_add ");
-  if (a->tag[0])
-    s = format (s, "tag %s ", a->tag);
-
-  s = format (s, "count %d \\\n", count);
-
-  PRINT_S;
-
-  for (i = 0; i < count; i++)
-    vl_api_macip_acl_rule_t_print (&a->r[i], handle);
-
-  s = format (0, "\n");
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_macip_acl_add_replace_t_print (vl_api_macip_acl_add_replace_t * a, void *handle)
-{
-  u8 *s = 0;
-  int i;
-  u32 acl_index = clib_net_to_host_u32 (a->acl_index);
-  u32 count = clib_net_to_host_u32 (a->count);
-  if (count > 0x100000)
-    {
-      s = format (s, "WARN: macip_acl_add_replace count endianness wrong? Fixup to avoid long loop.\n");
-      count = a->count;
-    }
-
-  s = format (s, "SCRIPT: macip_acl_add_replace %d count %d ",
-        acl_index, count);
-  if (a->tag[0])
-    s = format (s, "tag %s ", a->tag);
-
-  s = format (s, "count %d \\\n", count);
-
-  PRINT_S;
-
-  for (i = 0; i < count; i++)
-    vl_api_macip_acl_rule_t_print (&a->r[i], handle);
-
-  s = format (0, "\n");
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_acl_interface_set_acl_list_t_print (vl_api_acl_interface_set_acl_list_t
-                                          * a, void *handle)
-{
-  u8 *s;
-  int i;
-
-  s = format
-    (0, "SCRIPT: acl_interface_set_acl_list sw_if_index %d count %d\n",
-     clib_net_to_host_u32 (a->sw_if_index), (u32) a->count);
-
-  s = format (s, "    input ");
-
-  for (i = 0; i < a->count; i++)
-    {
-      if (i == a->n_input)
-        s = format (s, "output ");
-      s = format (s, "%d ", clib_net_to_host_u32 (a->acls[i]));
-    }
-
-  PRINT_S;
-  return handle;
-}
-
-static inline void *
-vl_api_acl_interface_set_etype_whitelist_t_print (vl_api_acl_interface_set_etype_whitelist_t
-                                          * a, void *handle)
-{
-  u8 *s;
-  int i;
-
-  s = format
-    (0, "SCRIPT: acl_interface_set_etype_whitelist sw_if_index %d count %d\n",
-     clib_net_to_host_u32 (a->sw_if_index), (u32) a->count);
-
-  s = format (s, "    input ");
-
-  for (i = 0; i < a->count; i++)
-    {
-      if (i == a->n_input)
-        s = format (s, "output ");
-      s = format (s, "%x ", clib_net_to_host_u16 (a->whitelist[i]));
-    }
-
-  PRINT_S;
-  return handle;
-}
-
-static inline void *
-vl_api_acl_interface_add_del_t_print (vl_api_acl_interface_add_del_t * a,
-                                     void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: acl_interface_add_del sw_if_index %d acl %d ",
-             clib_net_to_host_u32 (a->sw_if_index),
-             clib_net_to_host_u32 (a->acl_index));
-  s = format (s, "%s %s",
-             a->is_input ? "input" : "output", a->is_add ? "add" : "del");
-
-  PRINT_S;
-  return handle;
-}
-
-static inline void *vl_api_macip_acl_interface_add_del_t_print
-  (vl_api_macip_acl_interface_add_del_t * a, void *handle)
-{
-  u8 *s;
-
-  s = format
-    (0,
-     "SCRIPT: macip_acl_interface_add_del sw_if_index %d acl_index %d ",
-     clib_net_to_host_u32 (a->sw_if_index),
-     clib_net_to_host_u32 (a->acl_index));
-  s = format (s, "%s", a->is_add ? "add" : "del");
-
-  PRINT_S;
-  return handle;
-}
-
-
-static inline void *
-vl_api_macip_acl_del_t_print (vl_api_macip_acl_del_t * a, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: macip_acl_del %d ",
-             clib_host_to_net_u32 (a->acl_index));
-
-  PRINT_S;
-  return handle;
-}
-
-
-#endif /* included_manual_fns_h */
index 866ed8a..17ab524 100644 (file)
@@ -21,7 +21,7 @@ option version = "1.0.0";
     @param context - sender context, to match reply w/ request
     @param is_enable - 1 = enable, 0 = disable
 */
-autoreply manual_print define dns_enable_disable {
+autoreply define dns_enable_disable {
     u32 client_index;
     u32 context;
     u8 enable;
@@ -36,7 +36,7 @@ autoreply manual_print define dns_enable_disable {
     @param is_add - add = 1, delete = 0
     @param server_address - server ip address
 */
-autoreply manual_print define dns_name_server_add_del {
+autoreply define dns_name_server_add_del {
     u32 client_index;
     u32 context;
     u8 is_ip6;
@@ -51,7 +51,7 @@ autoreply manual_print define dns_name_server_add_del {
     @param context - sender context, to match reply w/ request
     @param name - the name to resolve
 */
-manual_print define dns_resolve_name {
+define dns_resolve_name {
     u32 client_index;
     u32 context;
     u8 name[256];
@@ -84,7 +84,7 @@ define dns_resolve_name_reply {
     @param is_ip6 - set if the reverse-DNS request is an ip6 address
     @param address - the address to map to a name
 */
-manual_print define dns_resolve_ip {
+define dns_resolve_ip {
     u32 client_index;
     u32 context;
     u8 is_ip6;
index 045f4d6..de1862d 100644 (file)
@@ -2992,59 +2992,6 @@ found_src_address:
     }
 }
 
-static void *vl_api_dns_enable_disable_t_print
-  (vl_api_dns_enable_disable_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: dns_enable_disable ");
-  s = format (s, "%s ", mp->enable ? "enable" : "disable");
-
-  FINISH;
-}
-
-static void *vl_api_dns_name_server_add_del_t_print
-  (vl_api_dns_name_server_add_del_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: dns_name_server_add_del ");
-  if (mp->is_ip6)
-    s = format (s, "%U ", format_ip6_address,
-               (ip6_address_t *) mp->server_address);
-  else
-    s = format (s, "%U ", format_ip4_address,
-               (ip4_address_t *) mp->server_address);
-
-  if (mp->is_add == 0)
-    s = format (s, "del ");
-
-  FINISH;
-}
-
-static void *vl_api_dns_resolve_name_t_print
-  (vl_api_dns_resolve_name_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: dns_resolve_name ");
-  s = format (s, "%s ", mp->name);
-  FINISH;
-}
-
-static void *vl_api_dns_resolve_ip_t_print
-  (vl_api_dns_resolve_ip_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: dns_resolve_ip ");
-  if (mp->is_ip6)
-    s = format (s, "%U ", format_ip6_address, mp->address);
-  else
-    s = format (s, "%U ", format_ip4_address, mp->address);
-  FINISH;
-}
-
 #include <dns/dns.api.c>
 static clib_error_t *
 dns_init (vlib_main_t * vm)
index 8e8b90f..55dd51d 100644 (file)
@@ -30,7 +30,7 @@ enum flowprobe_record_flags : u8
     @param which - flags indicating forwarding path
     @param sw_if_index - index of the interface
 */
-autoreply manual_print define flowprobe_tx_interface_add_del
+autoreply define flowprobe_tx_interface_add_del
 {
   /* Client identifier, set from api_main.my_client_index */
   u32 client_index;
index 9bc12c8..3df877a 100644 (file)
@@ -656,24 +656,6 @@ out:
   REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY);
 }
 
-/**
- * @brief API message custom-dump function
- * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
- * @param handle void * print function handle
- * @returns u8 * output string
- */
-static void *vl_api_flowprobe_tx_interface_add_del_t_print
-  (vl_api_flowprobe_tx_interface_add_del_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: flowprobe_tx_interface_add_del ");
-  s = format (s, "sw_if_index %d is_add %d which %d ",
-             clib_host_to_net_u32 (mp->sw_if_index),
-             (int) mp->is_add, (int) mp->which);
-  FINISH;
-}
-
 #define vec_neg_search(v,E)         \
 ({              \
   word _v(i) = 0;         \
index d9034fb..95f664f 100644 (file)
@@ -51,5 +51,4 @@ add_vpp_plugin(gbp
 
   INSTALL_HEADERS
   gbp.h
-  gbp_api_print.h
 )
index f7643c7..2f46634 100644 (file)
@@ -39,14 +39,14 @@ typedef gbp_bridge_domain
   vl_api_interface_index_t bm_flood_sw_if_index;
 };
 
-manual_print autoreply define gbp_bridge_domain_add
+ autoreply define gbp_bridge_domain_add
 {
   option status="in_progress";
   u32 client_index;
   u32 context;
   vl_api_gbp_bridge_domain_t bd;
 };
-manual_print autoreply define gbp_bridge_domain_del
+ autoreply define gbp_bridge_domain_del
 {
   option status="in_progress";
   u32 client_index;
@@ -78,14 +78,14 @@ typedef gbp_route_domain
   vl_api_gbp_scope_t scope;
 };
 
-manual_print autoreply define gbp_route_domain_add
+ autoreply define gbp_route_domain_add
 {
   option status="in_progress";
   u32 client_index;
   u32 context;
   vl_api_gbp_route_domain_t rd;
 };
-manual_print autoreply define gbp_route_domain_del
+ autoreply define gbp_route_domain_del
 {
   option status="in_progress";
   u32 client_index;
@@ -136,7 +136,7 @@ typedef gbp_endpoint
   vl_api_address_t ips[n_ips];
 };
 
-manual_print define gbp_endpoint_add
+ define gbp_endpoint_add
 {
   option status="in_progress";
   u32 client_index;
@@ -152,7 +152,7 @@ define gbp_endpoint_add_reply
   u32 handle;
 };
 
-manual_print autoreply define gbp_endpoint_del
+ autoreply define gbp_endpoint_del
 {
   option status="in_progress";
   u32 client_index;
@@ -191,14 +191,14 @@ typedef gbp_endpoint_group
   vl_api_gbp_endpoint_retention_t retention;
 };
 
-manual_print autoreply define gbp_endpoint_group_add
+ autoreply define gbp_endpoint_group_add
 {
   option status="in_progress";
   u32 client_index;
   u32 context;
   vl_api_gbp_endpoint_group_t epg;
 };
-manual_print autoreply define gbp_endpoint_group_del
+ autoreply define gbp_endpoint_group_del
 {
   option status="in_progress";
   u32 client_index;
@@ -227,7 +227,7 @@ typedef gbp_recirc
   bool is_ext;
 };
 
-manual_print autoreply define gbp_recirc_add_del
+ autoreply define gbp_recirc_add_del
 {
   option status="in_progress";
   u32 client_index;
@@ -268,7 +268,7 @@ typedef gbp_subnet
   vl_api_prefix_t prefix;
 };
 
-manual_print autoreply define gbp_subnet_add_del
+ autoreply define gbp_subnet_add_del
 {
   option status="in_progress";
   u32 client_index;
@@ -338,7 +338,7 @@ typedef gbp_contract
   vl_api_gbp_rule_t rules[n_rules];
 };
 
-manual_print define gbp_contract_add_del
+ define gbp_contract_add_del
 {
   option status="in_progress";
   u32 client_index;
@@ -388,7 +388,7 @@ typedef gbp_vxlan_tunnel
   vl_api_ip4_address_t src;
 };
 
-manual_print define gbp_vxlan_tunnel_add
+ define gbp_vxlan_tunnel_add
 {
   option status="in_progress";
   u32 client_index;
@@ -404,7 +404,7 @@ define gbp_vxlan_tunnel_add_reply
   vl_api_interface_index_t sw_if_index;
 };
 
-manual_print autoreply define gbp_vxlan_tunnel_del
+ autoreply define gbp_vxlan_tunnel_del
 {
   option status="in_progress";
   u32 client_index;
@@ -440,7 +440,7 @@ typedef gbp_ext_itf
   vl_api_gbp_ext_itf_flags_t flags;
 };
 
-manual_print autoreply define gbp_ext_itf_add_del
+ autoreply define gbp_ext_itf_add_del
 {
   option status="in_progress";
   u32 client_index;
index aea03d8..ab89172 100644 (file)
@@ -42,7 +42,6 @@
 #include <vnet/format_fns.h>
 #include <vlibapi/api_helper_macros.h>
 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
-#include "gbp_api_print.h"
 
 gbp_main_t gbp_main;
 
diff --git a/src/plugins/gbp/gbp_api_print.h b/src/plugins/gbp/gbp_api_print.h
deleted file mode 100644 (file)
index 39c25b6..0000000
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (c) 2019 Cisco and/or its affiliates.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __GBP_API_PRINT_H__
-#define __GBP_API_PRINT_H__
-
-#include <vpp/api/types.h>
-
-/* Macro to finish up custom dump fns */
-#define PRINT_S \
-    vec_add1 (s, 0);                            \
-    vl_print (handle, (char *)s);               \
-    vec_free (s);
-
-static inline void *
-vl_api_gbp_bridge_domain_add_t_print (vl_api_gbp_bridge_domain_add_t * a,
-                                     void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_bridge_domain_add ");
-  s = format (s, "bd_id %d ", ntohl (a->bd.bd_id));
-  s = format (s, "rd_id %d ", ntohl (a->bd.rd_id));
-  s = format (s, "flags %d ", ntohl (a->bd.flags));
-  s = format (s, "uu-fwd %d ", ntohl (a->bd.uu_fwd_sw_if_index));
-  s = format (s, "bvi %d ", ntohl (a->bd.bvi_sw_if_index));
-  s = format (s, "bm-flood %d", ntohl (a->bd.bm_flood_sw_if_index));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_bridge_domain_del_t_print (vl_api_gbp_bridge_domain_del_t * a,
-                                     void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_bridge_domain_del ");
-  s = format (s, "bd_id %d ", ntohl (a->bd_id));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_route_domain_add_t_print (vl_api_gbp_route_domain_add_t * a,
-                                    void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_route_domain_add ");
-  s = format (s, "rd_id %d ", ntohl (a->rd.rd_id));
-  s = format (s, "ip4_table_id %d ", ntohl (a->rd.ip4_table_id));
-  s = format (s, "ip6_table_id %d ", ntohl (a->rd.ip6_table_id));
-  s = format (s, "ip4_uu_sw_if_index %d ", ntohl (a->rd.ip4_uu_sw_if_index));
-  s = format (s, "ip6_uu_sw_if_index %d", ntohl (a->rd.ip6_uu_sw_if_index));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_route_domain_del_t_print (vl_api_gbp_route_domain_del_t * a,
-                                    void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_route_domain_del ");
-  s = format (s, "rd_id %d", ntohl (a->rd_id));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_endpoint_add_t_print (vl_api_gbp_endpoint_add_t * a, void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_endpoint_add ");
-  s = format (s, "sw_if_index %d ", ntohl (a->endpoint.sw_if_index));
-  s = format (s, "sclass %d ", ntohs (a->endpoint.sclass));
-  s = format (s, "flags %x ", ntohl (a->endpoint.flags));
-  s = format (s, "mac %U ", format_vl_api_mac_address, a->endpoint.mac);
-  s =
-    format (s, "\n\ttun\n\t\t src %U", format_vl_api_address,
-           &a->endpoint.tun.src);
-  s =
-    format (s, "\n\t\t dst %U ", format_vl_api_address, &a->endpoint.tun.dst);
-
-  if (a->endpoint.n_ips)
-    s = format (s, "\n\t ips");
-  for (int i = 0; i < a->endpoint.n_ips; i++)
-    s = format (s, "\n\t\t %U", format_vl_api_address, &a->endpoint.ips[i]);
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_endpoint_del_t_print (vl_api_gbp_endpoint_del_t * a, void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_endpoint_del ");
-  s = format (s, "handle %d", ntohl (a->handle));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_endpoint_group_add_t_print (vl_api_gbp_endpoint_group_add_t * a,
-                                      void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_endpoint_group_add ");
-  s = format (s, "vnid %d ", ntohl (a->epg.vnid));
-  s = format (s, "sclass %d ", ntohs (a->epg.sclass));
-  s = format (s, "bd_id %d ", ntohl (a->epg.bd_id));
-  s = format (s, "rd_id %d ", ntohl (a->epg.rd_id));
-  s = format (s, "uplink_sw_if_index %d ", ntohl (a->epg.uplink_sw_if_index));
-  s =
-    format (s, "remote_ep_timeout %d",
-           ntohl (a->epg.retention.remote_ep_timeout));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_endpoint_group_del_t_print (vl_api_gbp_endpoint_group_del_t * a,
-                                      void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_endpoint_group_del ");
-  s = format (s, "sclass %d ", ntohs (a->sclass));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_recirc_add_del_t_print (vl_api_gbp_recirc_add_del_t * a,
-                                  void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_recirc_add_del ");
-
-  if (a->is_add)
-    s = format (s, "add ");
-  else
-    s = format (s, "del ");
-  s = format (s, "sw_if_index %d ", ntohl (a->recirc.sw_if_index));
-  s = format (s, "sclass %d ", ntohs (a->recirc.sclass));
-  s = format (s, "is_ext %d ", a->recirc.is_ext);
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_subnet_add_del_t_print (vl_api_gbp_subnet_add_del_t * a,
-                                  void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_subnet_add_del ");
-  if (a->is_add)
-    s = format (s, "add ");
-  else
-    s = format (s, "del ");
-  s = format (s, "rd_id %d ", ntohl (a->subnet.rd_id));
-  s = format (s, "sw_if_index %d ", ntohl (a->subnet.sw_if_index));
-  s = format (s, "sclass %d ", ntohs (a->subnet.sclass));
-  s = format (s, "type %d ", ntohl (a->subnet.type));
-  s =
-    format (s, "prefix %U/%d", format_vl_api_address,
-           &a->subnet.prefix.address, a->subnet.prefix.len);
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_contract_add_del_t_print (vl_api_gbp_contract_add_del_t * a,
-                                    void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_contract_add_del ");
-  if (a->is_add)
-    s = format (s, "add ");
-  else
-    s = format (s, "del ");
-  s = format (s, "scope %d ", ntohl (a->contract.scope));
-  s = format (s, "sclass %d ", ntohs (a->contract.sclass));
-  s = format (s, "dclass %d ", ntohs (a->contract.dclass));
-  s = format (s, "acl_index %d \n", ntohl (a->contract.acl_index));
-  for (int i = 0; i < a->contract.n_rules; i++)
-    {
-      s = format (s, "\t action %d\n", ntohl (a->contract.rules[i].action));
-      s =
-       format (s, "\t hash_mode %d",
-               ntohl (a->contract.rules[i].nh_set.hash_mode));
-      for (int j = 0; j < a->contract.rules[i].nh_set.n_nhs; j++)
-       {
-         s =
-           format (s, "\n\t \t nhs ip %U ", format_vl_api_address,
-                   &a->contract.rules[i].nh_set.nhs[j].ip);
-         s =
-           format (s, "nhs mac %U ", format_vl_api_mac_address,
-                   a->contract.rules[i].nh_set.nhs[j].mac);
-         s =
-           format (s, "nhs bd_id %d ",
-                   ntohl (a->contract.rules[i].nh_set.nhs[j].bd_id));
-         s =
-           format (s, "nhs rd_id %d",
-                   ntohl (a->contract.rules[i].nh_set.nhs[j].rd_id));
-       }
-      s = format (s, "\n");
-    }
-
-  if (a->contract.n_ether_types)
-    s = format (s, "\tethertypes");
-  for (int i = 0; i < a->contract.n_ether_types; i++)
-    {
-      s = format (s, " %d ", ntohs (a->contract.allowed_ethertypes[i]));
-    }
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_vxlan_tunnel_add_t_print (vl_api_gbp_vxlan_tunnel_add_t * a,
-                                    void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_vxlan_tunnel_add ");
-
-  s = format (s, "vni %d ", ntohl (a->tunnel.vni));
-  s = format (s, "mode %d ", ntohl (a->tunnel.mode));
-  s = format (s, "bd_rd_id %d ", ntohl (a->tunnel.bd_rd_id));
-  s = format (s, "src %U ", format_vl_api_ip4_address, a->tunnel.src);
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_vxlan_tunnel_del_t_print (vl_api_gbp_vxlan_tunnel_del_t * a,
-                                    void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_vxlan_tunnel_del ");
-  s = format (s, "vni %d ", ntohl (a->vni));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-static inline void *
-vl_api_gbp_ext_itf_add_del_t_print (vl_api_gbp_ext_itf_add_del_t * a,
-                                   void *handle)
-{
-  u8 *s = 0;
-
-  s = format (s, "SCRIPT: gbp_ext_itf_add_del ");
-  if (a->is_add)
-    s = format (s, "add ");
-  else
-    s = format (s, "del ");
-
-  s = format (s, "sw_if_index %d ", ntohl (a->ext_itf.sw_if_index));
-  s = format (s, "bd_id %d ", ntohl (a->ext_itf.bd_id));
-  s = format (s, "rd_id %d ", ntohl (a->ext_itf.rd_id));
-  s = format (s, "flags %x ", ntohl (a->ext_itf.flags));
-
-  s = format (s, "\n");
-
-  PRINT_S;
-
-  return handle;
-}
-
-/*
- * fd.io coding-style-patch-verification: ON
- *
- * Local Variables:
- * eval: (c-set-style "gnu")
- * End:
- */
-
-#endif /* __GBP_API_PRINT_H__ */
index 253cf5b..176da24 100644 (file)
@@ -65,19 +65,6 @@ vl_api_lb_conf_t_handler
  REPLY_MACRO (VL_API_LB_CONF_REPLY);
 }
 
-static void *vl_api_lb_conf_t_print
-(vl_api_lb_conf_t *mp, void * handle)
-{
-  u8 * s;
-  s = format (0, "SCRIPT: lb_conf ");
-  s = format (s, "%U ", format_ip4_address, (ip4_address_t *)&mp->ip4_src_address);
-  s = format (s, "%U ", format_ip6_address, (ip6_address_t *)&mp->ip6_src_address);
-  s = format (s, "%u ", mp->sticky_buckets_per_core);
-  s = format (s, "%u ", mp->flow_timeout);
-  FINISH;
-}
-
-
 static void
 vl_api_lb_add_del_vip_t_handler
 (vl_api_lb_add_del_vip_t * mp)
@@ -142,38 +129,6 @@ vl_api_lb_add_del_vip_t_handler
  REPLY_MACRO (VL_API_LB_ADD_DEL_VIP_REPLY);
 }
 
-static void *vl_api_lb_add_del_vip_t_print
-(vl_api_lb_add_del_vip_t *mp, void * handle)
-{
-  u8 * s;
-  s = format (0, "SCRIPT: lb_add_del_vip ");
-  s = format (s, "%U", format_vl_api_prefix,
-       &mp->pfx);
-
-  s = format (s, "%s ", (mp->encap == LB_API_ENCAP_TYPE_GRE4)? "gre4"
-              : (mp->encap == LB_API_ENCAP_TYPE_GRE6)? "gre6"
-              : (mp->encap == LB_API_ENCAP_TYPE_NAT4)? "nat4"
-              : (mp->encap == LB_API_ENCAP_TYPE_NAT6)? "nat6"
-              : "l3dsr");
-
-  if (mp->encap==LB_API_ENCAP_TYPE_L3DSR)
-    {
-      s = format (s, "dscp %u ", mp->dscp);
-    }
-
-  if ((mp->encap==LB_API_ENCAP_TYPE_NAT4)
-      || (mp->encap==LB_API_ENCAP_TYPE_NAT6))
-    {
-      s = format (s, "type %u ", mp->type);
-      s = format (s, "port %u ", mp->port);
-      s = format (s, "target_port %u ", mp->target_port);
-    }
-
-  s = format (s, "%u ", mp->new_flows_table_length);
-  s = format (s, "%s ", mp->is_del?"del":"add");
-  FINISH;
-}
-
 static void
 vl_api_lb_add_del_as_t_handler
 (vl_api_lb_add_del_as_t * mp)
@@ -206,25 +161,6 @@ done:
  REPLY_MACRO (VL_API_LB_ADD_DEL_AS_REPLY);
 }
 
-static void *vl_api_lb_add_del_as_t_print
-(vl_api_lb_add_del_as_t *mp, void * handle)
-{
-  u8 * s;
-  ip46_address_t address;
-  s = format (0, "SCRIPT: lb_add_del_as ");
-  s = format (s, "%U ", format_vl_api_prefix,
-       &mp->pfx);
-  s = format(s, "%u ", mp->protocol);
-  if (ip_address_decode (&mp->as_address, &address) == IP46_TYPE_IP6)
-  s = format (s, "%U ", format_ip6_address,
-               (ip6_address_t *) & address.ip6);
-  else
-  s = format (s, "%U ", format_ip4_address,
-               (ip6_address_t *) & address.ip4);
-  s = format (s, "%s ", mp->is_del?"del":"add");
-  FINISH;
-}
-
 static void
 vl_api_lb_vip_dump_t_handler
 (vl_api_lb_vip_dump_t * mp)
@@ -407,19 +343,6 @@ static void vl_api_lb_add_del_intf_nat6_t_handler
   REPLY_MACRO (VL_API_LB_ADD_DEL_INTF_NAT6_REPLY);
 }
 
-static void *vl_api_lb_flush_vip_t_print
-(vl_api_lb_flush_vip_t *mp, void * handle)
-{
-  u8 * s;
-  s = format (0, "SCRIPT: lb_add_del_vip ");
-  s = format (s, "%U/%d", format_vl_api_address,
-       &mp->pfx.address, mp->pfx.len);
-  s = format (s, "protocol %u ", mp->protocol);
-  s = format (s, "port %u ", mp->port);
-
-  FINISH;
-}
-
 #include <lb/lb.api.c>
 static clib_error_t * lb_api_init (vlib_main_t * vm)
 {
index 564fe23..4bf30e7 100644 (file)
@@ -12,7 +12,7 @@ import "vnet/interface_types.api";
     @param flow_timeout - Time in seconds after which, if no packet is received
            for a given flow, the flow is removed from the established flow table.
 */
-autoreply manual_print define lb_conf
+autoreply  define lb_conf
 {
   u32 client_index;
   u32 context;
@@ -38,7 +38,7 @@ autoreply manual_print define lb_conf
            for this VIP (must be power of 2).
     @param is_del - The VIP should be removed.
 */
-autoreply manual_print define lb_add_del_vip {
+autoreply  define lb_add_del_vip {
   u32 client_index;
   u32 context;
   vl_api_address_with_prefix_t pfx;
@@ -64,7 +64,7 @@ autoreply manual_print define lb_add_del_vip {
     @param is_del - The AS should be removed.
     @param is_flush - The sessions related to this AS should be flushed.
 */
-autoreply manual_print define lb_add_del_as {
+autoreply  define lb_add_del_as {
   u32 client_index;
   u32 context;
   vl_api_address_with_prefix_t pfx;
@@ -83,7 +83,7 @@ autoreply manual_print define lb_add_del_as {
     @param protocol - tcp or udp.
     @param port - destination port.
 */
-autoreply manual_print define lb_flush_vip {
+autoreply  define lb_flush_vip {
   u32 client_index;
   u32 context;
   vl_api_address_with_prefix_t pfx;
index 94d2458..e65174e 100644 (file)
@@ -153,7 +153,10 @@ vl_api_map_domains_get_t_handler (vl_api_map_domains_get_t * mp)
   i32 rv = 0;
 
   if (pool_elts (mm->domains) == 0)
-    return;
+    {
+      REPLY_MACRO (VL_API_MAP_DOMAINS_GET_REPLY);
+      return;
+    }
 
   /* *INDENT-OFF* */
   REPLY_AND_DETAILS_MACRO (VL_API_MAP_DOMAINS_GET_REPLY, mm->domains,
index 4bbd254..fd06c10 100644 (file)
@@ -1047,7 +1047,7 @@ typedef nat44_lb_addr_port {
     @param locals - local network nodes
     @param tag - opaque string tag
 */
-autoreply manual_endian define nat44_add_del_lb_static_mapping {
+autoreply define nat44_add_del_lb_static_mapping {
   u32 client_index;
   u32 context;
   bool is_add;
@@ -1105,7 +1105,7 @@ define nat44_lb_static_mapping_dump {
     @param locals - local network nodes
     @param tag - opaque string tag
 */
-manual_endian define nat44_lb_static_mapping_details {
+define nat44_lb_static_mapping_details {
   u32 context;
   vl_api_ip4_address_t external_addr;
   u16 external_port;
index 6e28285..37c3dba 100644 (file)
@@ -18,9 +18,6 @@
  * @brief NAT44 plugin API implementation
  */
 
-#define vl_api_nat44_lb_static_mapping_details_t_endian vl_noop_handler
-#define vl_api_nat44_add_del_lb_static_mapping_t_endian vl_noop_handler
-
 #include <vnet/ip/ip_types_api.h>
 #include <vlibmemory/api.h>
 
index 613d180..fad6299 100644 (file)
@@ -30,7 +30,7 @@ import "vnet/ip/ip_types.api";
     @param sw_if_index - Interface index
     @param is_add - 1 if add, 0 if delete
 */
-autoreply manual_print define stn_add_del_rule {
+autoreply define stn_add_del_rule {
   u32 client_index;
   u32 context;
   vl_api_address_t ip_address;
index a22bbff..818c4a6 100644 (file)
     vec_free (s);                               \
     return handle;
 
-/**
- * @brief API message custom-dump function
- * @param mp vl_api_stn_add_del_rule_t * mp the api message
- * @param handle void * print function handle
- * @returns u8 * output string
- */
-static void *vl_api_stn_add_del_rule_t_print
-  (vl_api_stn_add_del_rule_t * mp, void *handle)
-{
-  u8 *s;
-
-  s = format (0, "SCRIPT: stn_add_del_rule ");
-  s = format (s, "address %U ", format_ip46_address, mp->ip_address);
-  s = format (s, "sw_if_index %d is_add %d", mp->sw_if_index, mp->is_add);
-
-  FINISH;
-}
-
 static void
 vl_api_stn_add_del_rule_t_handler (vl_api_stn_add_del_rule_t * mp)
 {
index b828706..da00823 100755 (executable)
@@ -1,7 +1,5 @@
 #!/usr/bin/env python3
 
-import ply.lex as lex
-import ply.yacc as yacc
 import sys
 import argparse
 import keyword
@@ -9,6 +7,8 @@ import logging
 import binascii
 import os
 from subprocess import Popen, PIPE
+import ply.lex as lex
+import ply.yacc as yacc
 
 assert sys.version_info >= (3, 5), \
     "Not supported Python version: {}".format(sys.version)
@@ -147,6 +147,14 @@ class VPPAPILexer(object):
     t_ignore = ' \t'
 
 
+def vla_mark_length_field(block):
+    if isinstance(block[-1], Array):
+        lengthfield = block[-1].lengthfield
+        for b in block:
+            if b.fieldname == lengthfield:
+                b.is_lengthfield = True
+
+
 def vla_is_last_check(name, block):
     vla = False
     for i, b in enumerate(block):
@@ -175,7 +183,8 @@ def vla_is_last_check(name, block):
 
 
 class Service():
-    def __init__(self, caller, reply, events=None, stream_message=None, stream=False):
+    def __init__(self, caller, reply, events=None, stream_message=None,
+                 stream=False):
         self.caller = caller
         self.reply = reply
         self.stream = stream
@@ -186,6 +195,7 @@ class Service():
 class Typedef():
     def __init__(self, name, flags, block):
         self.name = name
+        self.type = 'Typedef'
         self.flags = flags
         self.block = block
         self.crc = str(block).encode()
@@ -199,6 +209,7 @@ class Typedef():
         global_type_add(name, self)
 
         self.vla = vla_is_last_check(name, block)
+        vla_mark_length_field(self.block)
 
     def __repr__(self):
         return self.name + str(self.flags) + str(self.block)
@@ -207,6 +218,7 @@ class Typedef():
 class Using():
     def __init__(self, name, flags, alias):
         self.name = name
+        self.type = 'Using'
         self.vla = False
         self.block = []
         self.manual_print = True
@@ -226,6 +238,8 @@ class Using():
         else:
             a = {'type': alias.fieldtype}
         self.alias = a
+        self.using = alias
+
         #
         # Should have been:
         #  self.crc = str(alias).encode()
@@ -264,6 +278,7 @@ class Union():
 class Define():
     def __init__(self, name, flags, block):
         self.name = name
+        self.type = 'Define'
         self.flags = flags
         self.block = block
         self.dont_trace = False
@@ -294,6 +309,8 @@ class Define():
         block = [x for x in block if x not in remove]
         self.block = block
         self.vla = vla_is_last_check(name, block)
+        vla_mark_length_field(self.block)
+
         self.crc = str(block).encode()
 
     def __repr__(self):
@@ -305,6 +322,8 @@ class Enum():
         self.name = name
         self.enumtype = enumtype
         self.vla = False
+        self.type = 'Enum'
+        self.manual_print = False
 
         count = 0
         block2 = []
@@ -324,7 +343,8 @@ class Enum():
             except KeyError:
                 block3.append([b['id'], count])
                 if bc_set:
-                    raise ValueError("Backward compatible enum must be last {!r} {!r}"
+                    raise ValueError("Backward compatible enum must "
+                                     "be last {!r} {!r}"
                                      .format(name, b['id']))
         self.block = block2
         self.crc = str(block3).encode()
@@ -335,7 +355,7 @@ class Enum():
 
 
 class Import():
-
+    _initialized = False
     def __new__(cls, *args, **kwargs):
         if args[0] not in seen_imports:
             instance = super().__new__(cls)
@@ -347,18 +367,17 @@ class Import():
     def __init__(self, filename, revision):
         if self._initialized:
             return
-        else:
-            self.filename = filename
-            # Deal with imports
-            parser = VPPAPI(filename=filename, revision=revision)
-            dirlist = dirlist_get()
-            f = filename
-            for dir in dirlist:
-                f = os.path.join(dir, filename)
-                if os.path.exists(f):
-                    break
-            self.result = parser.parse_filename(f, None)
-            self._initialized = True
+        self.filename = filename
+        # Deal with imports
+        parser = VPPAPI(filename=filename, revision=revision)
+        dirlist = dirlist_get()
+        f = filename
+        for dir in dirlist:
+            f = os.path.join(dir, filename)
+            if os.path.exists(f):
+                break
+        self.result = parser.parse_filename(f, None)
+        self._initialized = True
 
     def __repr__(self):
         return self.filename
@@ -402,6 +421,7 @@ class Field():
     def __init__(self, fieldtype, name, limit=None):
         self.type = 'Field'
         self.fieldtype = fieldtype
+        self.is_lengthfield = False
 
         if self.fieldtype == 'string':
             raise ValueError("The string type {!r} is an "
@@ -475,8 +495,8 @@ class VPPAPIParser(object):
 
     def _coord(self, lineno, column=None):
         return Coord(
-                file=self.filename,
-                line=lineno, column=column)
+            file=self.filename,
+            line=lineno, column=column)
 
     def _token_coord(self, p, token_idx):
         """ Returns the coordinates for the YaccProduction object 'p' indexed
@@ -842,7 +862,7 @@ class VPPAPIParser(object):
             self._parse_error('At end of input', self.filename)
 
 
-class VPPAPI(object):
+class VPPAPI():
 
     def __init__(self, debug=False, filename='', logger=None, revision=None):
         self.lexer = lex.lex(module=VPPAPILexer(filename), debug=debug)
@@ -868,11 +888,11 @@ class VPPAPI(object):
             try:
                 data, errs = proc.communicate()
                 if proc.returncode != 0:
-                    print('File not found: {}:{}'.format(self.revision,
-                      filename), file=sys.stderr)
+                    print('File not found: {}:{}'
+                          .format(self.revision, filename), file=sys.stderr)
                     sys.exit(2)
                 return self.parse_string(data, debug=debug)
-            except Exception as e:
+            except Exception:
                 sys.exit(3)
         else:
             try:
@@ -916,14 +936,11 @@ class VPPAPI(object):
                 for o2 in o:
                     if isinstance(o2, Service):
                         s['Service'].append(o2)
-            elif (isinstance(o, Enum) or
-                  isinstance(o, Typedef) or
-                  isinstance(o, Using) or
-                  isinstance(o, Union)):
+            elif isinstance(o, (Enum, Typedef, Union, Using)):
                 s['types'].append(o)
-            elif (isinstance(o, Counter)):
+            elif isinstance(o, Counter):
                 s['Counters'].append(o)
-            elif (isinstance(o, Paths)):
+            elif isinstance(o, Paths):
                 s['Paths'].append(o)
             else:
                 if tname not in s:
@@ -986,9 +1003,8 @@ class VPPAPI(object):
                 if d[:-8]+'_get' in msgs:
                     if d[:-8]+'_get' in svcs:
                         continue
-                    else:
-                        raise ValueError('{} should be in a stream service'
-                                         .format(d[:-8]+'_get'))
+                    raise ValueError('{} should be in a stream service'
+                                     .format(d[:-8]+'_get'))
                 if d[:-8]+'_dump' in msgs:
                     continue
                 raise ValueError('{} missing dump or get message'
@@ -1006,14 +1022,10 @@ class VPPAPI(object):
         return s
 
     def process_imports(self, objs, in_import, result):
-        imported_objs = []
         for o in objs:
             # Only allow the following object types from imported file
-            if in_import and not (isinstance(o, Enum) or
-                                  isinstance(o, Union) or
-                                  isinstance(o, Typedef) or
-                                  isinstance(o, Import) or
-                                  isinstance(o, Using)):
+            if in_import and not isinstance(o, (Enum, Import, Typedef,
+                                                Union, Using)):
                 continue
             if isinstance(o, Import):
                 result.append(o)
@@ -1067,284 +1079,284 @@ def foldup_blocks(block, crc):
 # a different result and fail the comparison.
 
 fixup_crc_dict = {
-        "abf_policy_add_del": { 0xc6131197: 0xee66f93e },
-        "abf_policy_details": { 0xb7487fa4: 0x6769e504 },
-        "acl_add_replace": { 0xee5c2f18: 0x1cabdeab },
-        "acl_details": { 0x95babae0: 0x7a97f21c },
-        "macip_acl_add": { 0xce6fbad0: 0xd648fd0a },
-        "macip_acl_add_replace": { 0x2a461dd4: 0xe34402a7 },
-        "macip_acl_details": { 0x27135b59: 0x57c7482f },
-        "dhcp_proxy_config": { 0x4058a689: 0x6767230e },
-        "dhcp_client_config": { 0x1af013ea: 0x959b80a3 },
-        "dhcp_compl_event": { 0x554a44e5: 0xe908fd1d },
-        "dhcp_client_details": { 0x3c5cd28a: 0xacd82f5a },
-        "dhcp_proxy_details": { 0xdcbaf540: 0xce16f044 },
-        "dhcp6_send_client_message": { 0xf8222476: 0xf6f14ef0 },
-        "dhcp6_pd_send_client_message": { 0x3739fd8d: 0x64badb8 },
-        "dhcp6_reply_event": { 0x85b7b17e: 0x9f3af9e5 },
-        "dhcp6_pd_reply_event": { 0x5e878029: 0xcb3e462b },
-        "ip6_add_del_address_using_prefix": { 0x3982f30a: 0x9b3d11e0 },
-        "gbp_bridge_domain_add": { 0x918e8c01: 0x8454bfdf },
-        "gbp_bridge_domain_details": { 0x51d51be9: 0x2acd15f9 },
-        "gbp_route_domain_add": { 0x204c79e1: 0x2d0afe38 },
-        "gbp_route_domain_details": { 0xa78bfbca: 0x8ab11375 },
-        "gbp_endpoint_add": { 0x7b3af7de: 0x9ce16d5a },
-        "gbp_endpoint_details": { 0x8dd8fbd3: 0x8aecb60 },
-        "gbp_endpoint_group_add": { 0x301ddf15: 0x8e0f4054 },
-        "gbp_endpoint_group_details": { 0xab71d723: 0x8f38292c },
-        "gbp_subnet_add_del": { 0xa8803c80: 0x888aca35 },
-        "gbp_subnet_details": { 0xcbc5ca18: 0x4ed84156 },
-        "gbp_contract_add_del": { 0xaa8d652d: 0x553e275b },
-        "gbp_contract_details": { 0x65dec325: 0x2a18db6e },
-        "gbp_ext_itf_add_del": { 0x7606d0e1: 0x12ed5700 },
-        "gbp_ext_itf_details": { 0x519c3d3c: 0x408a45c0 },
-        "gtpu_add_del_tunnel": { 0xca983a2b: 0x9a9c0426 },
-        "gtpu_tunnel_update_tteid": { 0x79f33816: 0x8a2db108 },
-        "gtpu_tunnel_details": { 0x27f434ae: 0x4535cf95 },
-        "igmp_listen": { 0x19a49f1e: 0x3f93a51a },
-        "igmp_details": { 0x38f09929: 0x52f12a89 },
-        "igmp_event": { 0x85fe93ec: 0xd7696eaf },
-        "igmp_group_prefix_set": { 0x5b14a5ce: 0xd4f20ac5 },
-        "igmp_group_prefix_details": { 0x259ccd81: 0xc3b3c526 },
-        "ikev2_set_responder": { 0xb9aa4d4e: 0xf0d3dc80 },
-        "vxlan_gpe_ioam_export_enable_disable": { 0xd4c76d3a: 0xe4d4ebfa },
-        "ioam_export_ip6_enable_disable": { 0xd4c76d3a: 0xe4d4ebfa },
-        "vxlan_gpe_ioam_vni_enable": { 0xfbb5fb1: 0x997161fb },
-        "vxlan_gpe_ioam_vni_disable": { 0xfbb5fb1: 0x997161fb },
-        "vxlan_gpe_ioam_transit_enable": { 0x3d3ec657: 0x553f5b7b },
-        "vxlan_gpe_ioam_transit_disable": { 0x3d3ec657: 0x553f5b7b },
-        "udp_ping_add_del": { 0xfa2628fc: 0xc692b188 },
-        "l3xc_update": { 0xe96aabdf: 0x787b1d3 },
-        "l3xc_details": { 0xbc5bf852: 0xd4f69627 },
-        "sw_interface_lacp_details": { 0xd9a83d2f: 0x745ae0ba },
-        "lb_conf": { 0x56cd3261: 0x22ddb739 },
-        "lb_add_del_vip": { 0x6fa569c7: 0xd15b7ddc },
-        "lb_add_del_as": { 0x35d72500: 0x78628987 },
-        "lb_vip_dump": { 0x56110cb7: 0xc7bcb124 },
-        "lb_vip_details": { 0x1329ec9b: 0x8f39bed },
-        "lb_as_details": { 0x8d24c29e: 0x9c39f60e },
-        "mactime_add_del_range": { 0xcb56e877: 0x101858ef },
-        "mactime_details": { 0xda25b13a: 0x44921c06 },
-        "map_add_domain": { 0x249f195c: 0x7a5a18c9 },
-        "map_domain_details": { 0x796edb50: 0xfc1859dd },
-        "map_param_add_del_pre_resolve": { 0xdae5af03: 0x17008c66 },
-        "map_param_get_reply": { 0x26272c90: 0x28092156 },
-        "memif_details": { 0xda34feb9: 0xd0382c4c },
-        "dslite_add_del_pool_addr_range": { 0xde2a5b02: 0xc448457a },
-        "dslite_set_aftr_addr": { 0x78b50fdf: 0x1e955f8d },
-        "dslite_get_aftr_addr_reply": { 0x8e23608e: 0x38e30db1 },
-        "dslite_set_b4_addr": { 0x78b50fdf: 0x1e955f8d },
-        "dslite_get_b4_addr_reply": { 0x8e23608e: 0x38e30db1 },
-        "nat44_add_del_address_range": { 0x6f2b8055: 0xd4c7568c },
-        "nat44_address_details": { 0xd1beac1: 0x45410ac4 },
-        "nat44_add_del_static_mapping": { 0x5ae5f03e: 0xe165e83b },
-        "nat44_static_mapping_details": { 0x6cb40b2: 0x1a433ef7 },
-        "nat44_add_del_identity_mapping": { 0x2faaa22: 0x8e12743f },
-        "nat44_identity_mapping_details": { 0x2a52a030: 0x36d21351 },
-        "nat44_add_del_interface_addr": { 0x4aed50c0: 0xfc835325 },
-        "nat44_interface_addr_details": { 0xe4aca9ca: 0x3e687514 },
-        "nat44_user_session_details": { 0x2cf6e16d: 0x1965fd69 },
-        "nat44_add_del_lb_static_mapping": { 0x4f68ee9d: 0x53b24611 },
-        "nat44_lb_static_mapping_add_del_local": { 0x7ca47547: 0x2910a151 },
-        "nat44_lb_static_mapping_details": { 0xed5ce876: 0x2267b9e8 },
-        "nat44_del_session": { 0x15a5bf8c: 0x4c49c387 },
-        "nat_det_add_del_map": { 0x1150a190: 0x112fde05 },
-        "nat_det_map_details": { 0xad91dc83: 0x88000ee1 },
-        "nat_det_close_session_out": { 0xf6b259d1: 0xc1b6cbfb },
-        "nat_det_close_session_in": { 0x3c68e073: 0xa10ef64 },
-        "nat64_add_del_pool_addr_range": { 0xa3b944e3: 0x21234ef3 },
-        "nat64_add_del_static_bib": { 0x1c404de5: 0x90fae58a },
-        "nat64_bib_details": { 0x43bc3ddf: 0x62c8541d },
-        "nat64_st_details": { 0xdd3361ed: 0xc770d620 },
-        "nat66_add_del_static_mapping": { 0x3ed88f71: 0xfb64e50b },
-        "nat66_static_mapping_details": { 0xdf39654b: 0x5c568448 },
-        "nsh_add_del_map": { 0xa0f42b0: 0x898d857d },
-        "nsh_map_details": { 0x2fefcf49: 0xb34ac8a1 },
-        "nsim_cross_connect_enable_disable": { 0x9c3ead86: 0x16f70bdf },
-        "pppoe_add_del_session": { 0xf6fd759e: 0x46ace853 },
-        "pppoe_session_details": { 0x4b8e8a4a: 0x332bc742 },
-        "stn_add_del_rule": { 0x224c6edd: 0x53f751e6 },
-        "stn_rules_details": { 0xa51935a6: 0xb0f6606c },
-        "svs_route_add_del": { 0xe49bc63c: 0xd39e31fc },
-        "svs_details": { 0x6282cd55: 0xb8523d64 },
-        "vmxnet3_details": { 0x6a1a5498: 0x829ba055 },
-        "vrrp_vr_add_del": { 0xc5cf15aa: 0x6dc4b881 },
-        "vrrp_vr_details": { 0x46edcebd: 0x412fa71 },
-        "vrrp_vr_set_peers": { 0x20bec71f: 0xbaa2e52b },
-        "vrrp_vr_peer_details": { 0x3d99c108: 0xabd9145e },
-        "vrrp_vr_track_if_add_del": { 0xd67df299: 0x337f4ba4 },
-        "vrrp_vr_track_if_details": { 0x73c36f81: 0x99bcca9c },
-        "proxy_arp_add_del": { 0x1823c3e7: 0x85486cbd },
-        "proxy_arp_details": { 0x5b948673: 0x9228c150 },
-        "bfd_udp_get_echo_source_reply": { 0xe3d736a1: 0x1e00cfce },
-        "bfd_udp_add": { 0x939cd26a: 0x7a6d1185 },
-        "bfd_udp_mod": { 0x913df085: 0x783a3ff6 },
-        "bfd_udp_del": { 0xdcb13a89: 0x8096514d },
-        "bfd_udp_session_details": { 0x9fb2f2d: 0x60653c02 },
-        "bfd_udp_session_set_flags": { 0x4b4bdfd: 0xcf313851 },
-        "bfd_udp_auth_activate": { 0x21fd1bdb: 0x493ee0ec },
-        "bfd_udp_auth_deactivate": { 0x9a05e2e0: 0x99978c32 },
-        "bier_route_add_del": { 0xfd02f3ea: 0xf29edca0 },
-        "bier_route_details": { 0x4008caee: 0x39ee6a56 },
-        "bier_disp_entry_add_del": { 0x9eb80cb4: 0x648323eb },
-        "bier_disp_entry_details": { 0x84c218f1: 0xe5b039a9 },
-        "bond_create": { 0xf1dbd4ff: 0x48883c7e },
-        "bond_enslave": { 0xe7d14948: 0x76ecfa7 },
-        "sw_interface_bond_details": { 0xbb7c929b: 0xf5ef2106 },
-        "pipe_create_reply": { 0xb7ce310c: 0xd4c2c2b3 },
-        "pipe_details": { 0xc52b799d: 0x43ac107a },
-        "tap_create_v2": { 0x2d0d6570: 0x445835fd },
-        "sw_interface_tap_v2_details": { 0x1e2b2a47: 0xe53c16de },
-        "sw_interface_vhost_user_details": { 0xcee1e53: 0x98530df1 },
-        "virtio_pci_create": { 0x1944f8db: 0xa9f1370c },
-        "sw_interface_virtio_pci_details": { 0x6ca9c167: 0x16187f3a },
-        "p2p_ethernet_add": { 0x36a1a6dc: 0xeeb8e717 },
-        "p2p_ethernet_del": { 0x62f81c8c: 0xb62c386 },
-        "geneve_add_del_tunnel": { 0x99445831: 0x976693b5 },
-        "geneve_tunnel_details": { 0x6b16eb24: 0xe27e2748 },
-        "gre_tunnel_add_del": { 0xa27d7f17: 0x6efc9c22 },
-        "gre_tunnel_details": { 0x24435433: 0x3bfbf1 },
-        "sw_interface_set_flags": { 0xf5aec1b8: 0x6a2b491a },
-        "sw_interface_event": { 0x2d3d95a7: 0xf709f78d },
-        "sw_interface_details": { 0x6c221fc7: 0x17b69fa2 },
-        "sw_interface_add_del_address": { 0x5463d73b: 0x5803d5c4 },
-        "sw_interface_set_unnumbered": { 0x154a6439: 0x938ef33b },
-        "sw_interface_set_mac_address": { 0xc536e7eb: 0x6aca746a },
-        "sw_interface_set_rx_mode": { 0xb04d1cfe: 0x780f5cee },
-        "sw_interface_rx_placement_details": { 0x9e44a7ce: 0xf6d7d024 },
-        "create_subif": { 0x790ca755: 0xcb371063 },
-        "ip_neighbor_add_del": { 0x607c257: 0x105518b6 },
-        "ip_neighbor_dump": { 0xd817a484: 0xcd831298 },
-        "ip_neighbor_details": { 0xe29d79f0: 0x870e80b9 },
-        "want_ip_neighbor_events": { 0x73e70a86: 0x1a312870 },
-        "ip_neighbor_event": { 0xbdb092b2: 0x83933131 },
-        "ip_route_add_del": { 0xb8ecfe0d: 0xc1ff832d },
-        "ip_route_details": { 0xbda8f315: 0xd1ffaae1 },
-        "ip_route_lookup": { 0x710d6471: 0xe2986185 },
-        "ip_route_lookup_reply": { 0x5d8febcb: 0xae99de8e },
-        "ip_mroute_add_del": { 0x85d762f3: 0xf6627d17 },
-        "ip_mroute_details": { 0x99341a45: 0xc1cb4b44 },
-        "ip_address_details": { 0xee29b797: 0xb1199745 },
-        "ip_unnumbered_details": { 0xcc59bd42: 0xaa12a483 },
-        "mfib_signal_details": { 0x6f4a4cfb: 0x64398a9a },
-        "ip_punt_redirect": { 0x6580f635: 0xa9a5592c },
-        "ip_punt_redirect_details": { 0x2cef63e7: 0x3924f5d3 },
-        "ip_container_proxy_add_del": { 0x7df1dff1: 0x91189f40 },
-        "ip_container_proxy_details": { 0xa8085523: 0xee460e8 },
-        "ip_source_and_port_range_check_add_del": { 0x92a067e3: 0x8bfc76f2 },
-        "sw_interface_ip6_set_link_local_address": { 0x1c10f15f: 0x2931d9fa },
-        "ip_reassembly_enable_disable": { 0xeb77968d: 0x885c85a6 },
-        "set_punt": { 0xaa83d523: 0x83799618 },
-        "punt_socket_register": { 0x95268cbf: 0xc8cd10fa },
-        "punt_socket_details": { 0xde575080: 0x1de0ce75 },
-        "punt_socket_deregister": { 0x98fc9102: 0x98a444f4 },
-        "sw_interface_ip6nd_ra_prefix": { 0x82cc1b28: 0xe098785f },
-        "ip6nd_proxy_add_del": { 0xc2e4a686: 0x3fdf6659 },
-        "ip6nd_proxy_details": { 0x30b9ff4a: 0xd35be8ff },
-        "ip6_ra_event": { 0x364c1c5: 0x47e8cfbe },
-        "set_ipfix_exporter": { 0x5530c8a0: 0x69284e07 },
-        "ipfix_exporter_details": { 0xdedbfe4: 0x11e07413 },
-        "ipip_add_tunnel": { 0x2ac399f5: 0xa9decfcd },
-        "ipip_6rd_add_tunnel": { 0xb9ec1863: 0x56e93cc0 },
-        "ipip_tunnel_details": { 0xd31cb34e: 0x53236d75 },
-        "ipsec_spd_entry_add_del": { 0x338b7411: 0x9f384b8d },
-        "ipsec_spd_details": { 0x5813d7a2: 0xf2222790 },
-        "ipsec_sad_entry_add_del": { 0xab64b5c6: 0xb8def364 },
-        "ipsec_tunnel_protect_update": { 0x30d5f133: 0x143f155d },
-        "ipsec_tunnel_protect_del": { 0xcd239930: 0xddd2ba36 },
-        "ipsec_tunnel_protect_details": { 0x21663a50: 0xac6c823b },
-        "ipsec_tunnel_if_add_del": { 0x20e353fa: 0x2b135e68 },
-        "ipsec_sa_details": { 0x345d14a7: 0xb30c7f41 },
-        "l2_xconnect_details": { 0x472b6b67: 0xc8aa6b37 },
-        "l2_fib_table_details": { 0xa44ef6b8: 0xe8d2fc72 },
-        "l2fib_add_del": { 0xeddda487: 0xf29d796c },
-        "l2_macs_event": { 0x44b8fd64: 0x2eadfc8b },
-        "bridge_domain_details": { 0xfa506fd: 0x979f549d },
-        "l2_interface_pbb_tag_rewrite": { 0x38e802a8: 0x612efa5a },
-        "l2_patch_add_del": { 0xa1f6a6f3: 0x522f3445 },
-        "sw_interface_set_l2_xconnect": { 0x4fa28a85: 0x1aaa2dbb },
-        "sw_interface_set_l2_bridge": { 0xd0678b13: 0x2e483cd0 },
-        "bd_ip_mac_add_del": { 0x257c869: 0x5f2b84e2 },
-        "bd_ip_mac_details": { 0x545af86a: 0xa52f8044 },
-        "l2_arp_term_event": { 0x6963e07a: 0x85ff71ea },
-        "l2tpv3_create_tunnel": { 0x15bed0c2: 0x596892cb },
-        "sw_if_l2tpv3_tunnel_details": { 0x50b88993: 0x1dab5c7e },
-        "lisp_add_del_local_eid": { 0x4e5a83a2: 0x21f573bd },
-        "lisp_add_del_map_server": { 0xce19e32d: 0x6598ea7c },
-        "lisp_add_del_map_resolver": { 0xce19e32d: 0x6598ea7c },
-        "lisp_use_petr": { 0xd87dbad9: 0x9e141831 },
-        "show_lisp_use_petr_reply": { 0x22b9a4b0: 0xdcad8a81 },
-        "lisp_add_del_remote_mapping": { 0x6d5c789e: 0xfae8ed77 },
-        "lisp_add_del_adjacency": { 0x2ce0e6f6: 0xcf5edb61 },
-        "lisp_locator_details": { 0x2c620ffe: 0xc0c4c2a7 },
-        "lisp_eid_table_details": { 0x1c29f792: 0x4bc32e3a },
-        "lisp_eid_table_dump": { 0x629468b5: 0xb959b73b },
-        "lisp_adjacencies_get_reply": { 0x807257bf: 0x3f97bcdd },
-        "lisp_map_resolver_details": { 0x3e78fc57: 0x82a09deb },
-        "lisp_map_server_details": { 0x3e78fc57: 0x82a09deb },
-        "one_add_del_local_eid": { 0x4e5a83a2: 0x21f573bd },
-        "one_add_del_map_server": { 0xce19e32d: 0x6598ea7c },
-        "one_add_del_map_resolver": { 0xce19e32d: 0x6598ea7c },
-        "one_use_petr": { 0xd87dbad9: 0x9e141831 },
-        "show_one_use_petr_reply": { 0x84a03528: 0x10e744a6 },
-        "one_add_del_remote_mapping": { 0x6d5c789e: 0xfae8ed77 },
-        "one_add_del_l2_arp_entry": { 0x1aa5e8b3: 0x33209078 },
-        "one_l2_arp_entries_get_reply": { 0xb0dd200f: 0xb0a47bbe },
-        "one_add_del_ndp_entry": { 0xf8a287c: 0xd1629a2f },
-        "one_ndp_entries_get_reply": { 0x70719b1a: 0xbd34161 },
-        "one_add_del_adjacency": { 0x9e830312: 0xe48e7afe },
-        "one_locator_details": { 0x2c620ffe: 0xc0c4c2a7 },
-        "one_eid_table_details": { 0x1c29f792: 0x4bc32e3a },
-        "one_eid_table_dump": { 0xbd190269: 0x95151038 },
-        "one_adjacencies_get_reply": { 0x85bab89: 0xa8ed89a5 },
-        "one_map_resolver_details": { 0x3e78fc57: 0x82a09deb },
-        "one_map_server_details": { 0x3e78fc57: 0x82a09deb },
-        "one_stats_details": { 0x2eb74678: 0xff6ef238 },
-        "gpe_add_del_fwd_entry": { 0xf0847644: 0xde6df50f },
-        "gpe_fwd_entries_get_reply": { 0xc4844876: 0xf9f53f1b },
-        "gpe_fwd_entry_path_details": { 0x483df51a: 0xee80b19a },
-        "gpe_add_del_native_fwd_rpath": { 0x43fc8b54: 0x812da2f2 },
-        "gpe_native_fwd_rpaths_get_reply": { 0x7a1ca5a2: 0x79d54eb9 },
-        "sw_interface_set_lldp": { 0x57afbcd4: 0xd646ae0f },
-        "mpls_ip_bind_unbind": { 0xc7533b32: 0x48249a27 },
-        "mpls_tunnel_add_del": { 0x44350ac1: 0xe57ce61d },
-        "mpls_tunnel_details": { 0x57118ae3: 0xf3c0928e },
-        "mpls_route_add_del": { 0x8e1d1e07: 0x343cff54 },
-        "mpls_route_details": { 0x9b5043dc: 0xd0ac384c },
-        "policer_add_del": { 0x2b31dd38: 0xcb948f6e },
-        "policer_details": { 0x72d0e248: 0xa43f781a },
-        "qos_store_enable_disable": { 0xf3abcc8b: 0x3507235e },
-        "qos_store_details": { 0x3ee0aad7: 0x38a6d48 },
-        "qos_record_enable_disable": { 0x2f1a4a38: 0x25b33f88 },
-        "qos_record_details": { 0xa425d4d3: 0x4956ccdd },
-        "session_rule_add_del": { 0xe4895422: 0xe31f9443 },
-        "session_rules_details": { 0x28d71830: 0x304b91f0 },
-        "sw_interface_span_enable_disable": { 0x23ddd96b: 0xacc8fea1 },
-        "sw_interface_span_details": { 0x8a20e79f: 0x55643fc },
-        "sr_mpls_steering_add_del": { 0x64acff63: 0x7d1b0a0b },
-        "sr_mpls_policy_assign_endpoint_color": { 0xe7eb978: 0x5e1c5c13 },
-        "sr_localsid_add_del": { 0x5a36c324: 0x26fa3309 },
-        "sr_policy_add": { 0x44ac92e8: 0xec79ee6a },
-        "sr_policy_mod": { 0xb97bb56e: 0xe531a102 },
-        "sr_steering_add_del": { 0xe46b0a0f: 0x3711dace },
-        "sr_localsids_details": { 0x2e9221b9: 0x6a6c0265 },
-        "sr_policies_details": { 0xdb6ff2a1: 0x7ec2d93 },
-        "sr_steering_pol_details": { 0xd41258c9: 0x1c1ee786 },
-        "syslog_set_sender": { 0xb8011d0b: 0xbb641285 },
-        "syslog_get_sender_reply": { 0x424cfa4e: 0xd3da60ac },
-        "tcp_configure_src_addresses": { 0x67eede0d: 0x4b02b946 },
-        "teib_entry_add_del": { 0x8016cfd2: 0x5aa0a538 },
-        "teib_details": { 0x981ee1a1: 0xe3b6a503 },
-        "udp_encap_add": { 0xf74a60b1: 0x61d5fc48 },
-        "udp_encap_details": { 0x8cfb9c76: 0x87c82821 },
-        "vxlan_gbp_tunnel_add_del": { 0x6c743427: 0x8c819166 },
-        "vxlan_gbp_tunnel_details": { 0x66e94a89: 0x1da24016 },
-        "vxlan_gpe_add_del_tunnel": { 0xa645b2b0: 0x7c6da6ae },
-        "vxlan_gpe_tunnel_details": { 0x968fc8b: 0x57712346 },
-        "vxlan_add_del_tunnel": { 0xc09dc80: 0xa35dc8f5 },
-        "vxlan_tunnel_details": { 0xc3916cb1: 0xe782f70f },
-        "vxlan_offload_rx": { 0x9cc95087: 0x89a1564b },
-        "log_details": { 0x3d61cc0: 0x255827a1 },
+        "abf_policy_add_del": {0xc6131197: 0xee66f93e},
+        "abf_policy_details": {0xb7487fa4: 0x6769e504},
+        "acl_add_replace": {0xee5c2f18: 0x1cabdeab},
+        "acl_details": {0x95babae0: 0x7a97f21c},
+        "macip_acl_add": {0xce6fbad0: 0xd648fd0a},
+        "macip_acl_add_replace": {0x2a461dd4: 0xe34402a7},
+        "macip_acl_details": {0x27135b59: 0x57c7482f},
+        "dhcp_proxy_config": {0x4058a689: 0x6767230e},
+        "dhcp_client_config": {0x1af013ea: 0x959b80a3},
+        "dhcp_compl_event": {0x554a44e5: 0xe908fd1d},
+        "dhcp_client_details": {0x3c5cd28a: 0xacd82f5a},
+        "dhcp_proxy_details": {0xdcbaf540: 0xce16f044},
+        "dhcp6_send_client_message": {0xf8222476: 0xf6f14ef0},
+        "dhcp6_pd_send_client_message": {0x3739fd8d: 0x64badb8},
+        "dhcp6_reply_event": {0x85b7b17e: 0x9f3af9e5},
+        "dhcp6_pd_reply_event": {0x5e878029: 0xcb3e462b},
+        "ip6_add_del_address_using_prefix": {0x3982f30a: 0x9b3d11e0},
+        "gbp_bridge_domain_add": {0x918e8c01: 0x8454bfdf},
+        "gbp_bridge_domain_details": {0x51d51be9: 0x2acd15f9},
+        "gbp_route_domain_add": {0x204c79e1: 0x2d0afe38},
+        "gbp_route_domain_details": {0xa78bfbca: 0x8ab11375},
+        "gbp_endpoint_add": {0x7b3af7de: 0x9ce16d5a},
+        "gbp_endpoint_details": {0x8dd8fbd3: 0x8aecb60},
+        "gbp_endpoint_group_add": {0x301ddf15: 0x8e0f4054},
+        "gbp_endpoint_group_details": {0xab71d723: 0x8f38292c},
+        "gbp_subnet_add_del": {0xa8803c80: 0x888aca35},
+        "gbp_subnet_details": {0xcbc5ca18: 0x4ed84156},
+        "gbp_contract_add_del": {0xaa8d652d: 0x553e275b},
+        "gbp_contract_details": {0x65dec325: 0x2a18db6e},
+        "gbp_ext_itf_add_del": {0x7606d0e1: 0x12ed5700},
+        "gbp_ext_itf_details": {0x519c3d3c: 0x408a45c0},
+        "gtpu_add_del_tunnel": {0xca983a2b: 0x9a9c0426},
+        "gtpu_tunnel_update_tteid": {0x79f33816: 0x8a2db108},
+        "gtpu_tunnel_details": {0x27f434ae: 0x4535cf95},
+        "igmp_listen": {0x19a49f1e: 0x3f93a51a},
+        "igmp_details": {0x38f09929: 0x52f12a89},
+        "igmp_event": {0x85fe93ec: 0xd7696eaf},
+        "igmp_group_prefix_set": {0x5b14a5ce: 0xd4f20ac5},
+        "igmp_group_prefix_details": {0x259ccd81: 0xc3b3c526},
+        "ikev2_set_responder": {0xb9aa4d4e: 0xf0d3dc80},
+        "vxlan_gpe_ioam_export_enable_disable": {0xd4c76d3a: 0xe4d4ebfa},
+        "ioam_export_ip6_enable_disable": {0xd4c76d3a: 0xe4d4ebfa},
+        "vxlan_gpe_ioam_vni_enable": {0xfbb5fb1: 0x997161fb},
+        "vxlan_gpe_ioam_vni_disable": {0xfbb5fb1: 0x997161fb},
+        "vxlan_gpe_ioam_transit_enable": {0x3d3ec657: 0x553f5b7b},
+        "vxlan_gpe_ioam_transit_disable": {0x3d3ec657: 0x553f5b7b},
+        "udp_ping_add_del": {0xfa2628fc: 0xc692b188},
+        "l3xc_update": {0xe96aabdf: 0x787b1d3},
+        "l3xc_details": {0xbc5bf852: 0xd4f69627},
+        "sw_interface_lacp_details": {0xd9a83d2f: 0x745ae0ba},
+        "lb_conf": {0x56cd3261: 0x22ddb739},
+        "lb_add_del_vip": {0x6fa569c7: 0xd15b7ddc},
+        "lb_add_del_as": {0x35d72500: 0x78628987},
+        "lb_vip_dump": {0x56110cb7: 0xc7bcb124},
+        "lb_vip_details": {0x1329ec9b: 0x8f39bed},
+        "lb_as_details": {0x8d24c29e: 0x9c39f60e},
+        "mactime_add_del_range": {0xcb56e877: 0x101858ef},
+        "mactime_details": {0xda25b13a: 0x44921c06},
+        "map_add_domain": {0x249f195c: 0x7a5a18c9},
+        "map_domain_details": {0x796edb50: 0xfc1859dd},
+        "map_param_add_del_pre_resolve": {0xdae5af03: 0x17008c66},
+        "map_param_get_reply": {0x26272c90: 0x28092156},
+        "memif_details": {0xda34feb9: 0xd0382c4c},
+        "dslite_add_del_pool_addr_range": {0xde2a5b02: 0xc448457a},
+        "dslite_set_aftr_addr": {0x78b50fdf: 0x1e955f8d},
+        "dslite_get_aftr_addr_reply": {0x8e23608e: 0x38e30db1},
+        "dslite_set_b4_addr": {0x78b50fdf: 0x1e955f8d},
+        "dslite_get_b4_addr_reply": {0x8e23608e: 0x38e30db1},
+        "nat44_add_del_address_range": {0x6f2b8055: 0xd4c7568c},
+        "nat44_address_details": {0xd1beac1: 0x45410ac4},
+        "nat44_add_del_static_mapping": {0x5ae5f03e: 0xe165e83b},
+        "nat44_static_mapping_details": {0x6cb40b2: 0x1a433ef7},
+        "nat44_add_del_identity_mapping": {0x2faaa22: 0x8e12743f},
+        "nat44_identity_mapping_details": {0x2a52a030: 0x36d21351},
+        "nat44_add_del_interface_addr": {0x4aed50c0: 0xfc835325},
+        "nat44_interface_addr_details": {0xe4aca9ca: 0x3e687514},
+        "nat44_user_session_details": {0x2cf6e16d: 0x1965fd69},
+        "nat44_add_del_lb_static_mapping": {0x4f68ee9d: 0x53b24611},
+        "nat44_lb_static_mapping_add_del_local": {0x7ca47547: 0x2910a151},
+        "nat44_lb_static_mapping_details": {0xed5ce876: 0x2267b9e8},
+        "nat44_del_session": {0x15a5bf8c: 0x4c49c387},
+        "nat_det_add_del_map": {0x1150a190: 0x112fde05},
+        "nat_det_map_details": {0xad91dc83: 0x88000ee1},
+        "nat_det_close_session_out": {0xf6b259d1: 0xc1b6cbfb},
+        "nat_det_close_session_in": {0x3c68e073: 0xa10ef64},
+        "nat64_add_del_pool_addr_range": {0xa3b944e3: 0x21234ef3},
+        "nat64_add_del_static_bib": {0x1c404de5: 0x90fae58a},
+        "nat64_bib_details": {0x43bc3ddf: 0x62c8541d},
+        "nat64_st_details": {0xdd3361ed: 0xc770d620},
+        "nat66_add_del_static_mapping": {0x3ed88f71: 0xfb64e50b},
+        "nat66_static_mapping_details": {0xdf39654b: 0x5c568448},
+        "nsh_add_del_map": {0xa0f42b0: 0x898d857d},
+        "nsh_map_details": {0x2fefcf49: 0xb34ac8a1},
+        "nsim_cross_connect_enable_disable": {0x9c3ead86: 0x16f70bdf},
+        "pppoe_add_del_session": {0xf6fd759e: 0x46ace853},
+        "pppoe_session_details": {0x4b8e8a4a: 0x332bc742},
+        "stn_add_del_rule": {0x224c6edd: 0x53f751e6},
+        "stn_rules_details": {0xa51935a6: 0xb0f6606c},
+        "svs_route_add_del": {0xe49bc63c: 0xd39e31fc},
+        "svs_details": {0x6282cd55: 0xb8523d64},
+        "vmxnet3_details": {0x6a1a5498: 0x829ba055},
+        "vrrp_vr_add_del": {0xc5cf15aa: 0x6dc4b881},
+        "vrrp_vr_details": {0x46edcebd: 0x412fa71},
+        "vrrp_vr_set_peers": {0x20bec71f: 0xbaa2e52b},
+        "vrrp_vr_peer_details": {0x3d99c108: 0xabd9145e},
+        "vrrp_vr_track_if_add_del": {0xd67df299: 0x337f4ba4},
+        "vrrp_vr_track_if_details": {0x73c36f81: 0x99bcca9c},
+        "proxy_arp_add_del": {0x1823c3e7: 0x85486cbd},
+        "proxy_arp_details": {0x5b948673: 0x9228c150},
+        "bfd_udp_get_echo_source_reply": {0xe3d736a1: 0x1e00cfce},
+        "bfd_udp_add": {0x939cd26a: 0x7a6d1185},
+        "bfd_udp_mod": {0x913df085: 0x783a3ff6},
+        "bfd_udp_del": {0xdcb13a89: 0x8096514d},
+        "bfd_udp_session_details": {0x9fb2f2d: 0x60653c02},
+        "bfd_udp_session_set_flags": {0x4b4bdfd: 0xcf313851},
+        "bfd_udp_auth_activate": {0x21fd1bdb: 0x493ee0ec},
+        "bfd_udp_auth_deactivate": {0x9a05e2e0: 0x99978c32},
+        "bier_route_add_del": {0xfd02f3ea: 0xf29edca0},
+        "bier_route_details": {0x4008caee: 0x39ee6a56},
+        "bier_disp_entry_add_del": {0x9eb80cb4: 0x648323eb},
+        "bier_disp_entry_details": {0x84c218f1: 0xe5b039a9},
+        "bond_create": {0xf1dbd4ff: 0x48883c7e},
+        "bond_enslave": {0xe7d14948: 0x76ecfa7},
+        "sw_interface_bond_details": {0xbb7c929b: 0xf5ef2106},
+        "pipe_create_reply": {0xb7ce310c: 0xd4c2c2b3},
+        "pipe_details": {0xc52b799d: 0x43ac107a},
+        "tap_create_v2": {0x2d0d6570: 0x445835fd},
+        "sw_interface_tap_v2_details": {0x1e2b2a47: 0xe53c16de},
+        "sw_interface_vhost_user_details": {0xcee1e53: 0x98530df1},
+        "virtio_pci_create": {0x1944f8db: 0xa9f1370c},
+        "sw_interface_virtio_pci_details": {0x6ca9c167: 0x16187f3a},
+        "p2p_ethernet_add": {0x36a1a6dc: 0xeeb8e717},
+        "p2p_ethernet_del": {0x62f81c8c: 0xb62c386},
+        "geneve_add_del_tunnel": {0x99445831: 0x976693b5},
+        "geneve_tunnel_details": {0x6b16eb24: 0xe27e2748},
+        "gre_tunnel_add_del": {0xa27d7f17: 0x6efc9c22},
+        "gre_tunnel_details": {0x24435433: 0x3bfbf1},
+        "sw_interface_set_flags": {0xf5aec1b8: 0x6a2b491a},
+        "sw_interface_event": {0x2d3d95a7: 0xf709f78d},
+        "sw_interface_details": {0x6c221fc7: 0x17b69fa2},
+        "sw_interface_add_del_address": {0x5463d73b: 0x5803d5c4},
+        "sw_interface_set_unnumbered": {0x154a6439: 0x938ef33b},
+        "sw_interface_set_mac_address": {0xc536e7eb: 0x6aca746a},
+        "sw_interface_set_rx_mode": {0xb04d1cfe: 0x780f5cee},
+        "sw_interface_rx_placement_details": {0x9e44a7ce: 0xf6d7d024},
+        "create_subif": {0x790ca755: 0xcb371063},
+        "ip_neighbor_add_del": {0x607c257: 0x105518b6},
+        "ip_neighbor_dump": {0xd817a484: 0xcd831298},
+        "ip_neighbor_details": {0xe29d79f0: 0x870e80b9},
+        "want_ip_neighbor_events": {0x73e70a86: 0x1a312870},
+        "ip_neighbor_event": {0xbdb092b2: 0x83933131},
+        "ip_route_add_del": {0xb8ecfe0d: 0xc1ff832d},
+        "ip_route_details": {0xbda8f315: 0xd1ffaae1},
+        "ip_route_lookup": {0x710d6471: 0xe2986185},
+        "ip_route_lookup_reply": {0x5d8febcb: 0xae99de8e},
+        "ip_mroute_add_del": {0x85d762f3: 0xf6627d17},
+        "ip_mroute_details": {0x99341a45: 0xc1cb4b44},
+        "ip_address_details": {0xee29b797: 0xb1199745},
+        "ip_unnumbered_details": {0xcc59bd42: 0xaa12a483},
+        "mfib_signal_details": {0x6f4a4cfb: 0x64398a9a},
+        "ip_punt_redirect": {0x6580f635: 0xa9a5592c},
+        "ip_punt_redirect_details": {0x2cef63e7: 0x3924f5d3},
+        "ip_container_proxy_add_del": {0x7df1dff1: 0x91189f40},
+        "ip_container_proxy_details": {0xa8085523: 0xee460e8},
+        "ip_source_and_port_range_check_add_del": {0x92a067e3: 0x8bfc76f2},
+        "sw_interface_ip6_set_link_local_address": {0x1c10f15f: 0x2931d9fa},
+        "ip_reassembly_enable_disable": {0xeb77968d: 0x885c85a6},
+        "set_punt": {0xaa83d523: 0x83799618},
+        "punt_socket_register": {0x95268cbf: 0xc8cd10fa},
+        "punt_socket_details": {0xde575080: 0x1de0ce75},
+        "punt_socket_deregister": {0x98fc9102: 0x98a444f4},
+        "sw_interface_ip6nd_ra_prefix": {0x82cc1b28: 0xe098785f},
+        "ip6nd_proxy_add_del": {0xc2e4a686: 0x3fdf6659},
+        "ip6nd_proxy_details": {0x30b9ff4a: 0xd35be8ff},
+        "ip6_ra_event": {0x364c1c5: 0x47e8cfbe},
+        "set_ipfix_exporter": {0x5530c8a0: 0x69284e07},
+        "ipfix_exporter_details": {0xdedbfe4: 0x11e07413},
+        "ipip_add_tunnel": {0x2ac399f5: 0xa9decfcd},
+        "ipip_6rd_add_tunnel": {0xb9ec1863: 0x56e93cc0},
+        "ipip_tunnel_details": {0xd31cb34e: 0x53236d75},
+        "ipsec_spd_entry_add_del": {0x338b7411: 0x9f384b8d},
+        "ipsec_spd_details": {0x5813d7a2: 0xf2222790},
+        "ipsec_sad_entry_add_del": {0xab64b5c6: 0xb8def364},
+        "ipsec_tunnel_protect_update": {0x30d5f133: 0x143f155d},
+        "ipsec_tunnel_protect_del": {0xcd239930: 0xddd2ba36},
+        "ipsec_tunnel_protect_details": {0x21663a50: 0xac6c823b},
+        "ipsec_tunnel_if_add_del": {0x20e353fa: 0x2b135e68},
+        "ipsec_sa_details": {0x345d14a7: 0xb30c7f41},
+        "l2_xconnect_details": {0x472b6b67: 0xc8aa6b37},
+        "l2_fib_table_details": {0xa44ef6b8: 0xe8d2fc72},
+        "l2fib_add_del": {0xeddda487: 0xf29d796c},
+        "l2_macs_event": {0x44b8fd64: 0x2eadfc8b},
+        "bridge_domain_details": {0xfa506fd: 0x979f549d},
+        "l2_interface_pbb_tag_rewrite": {0x38e802a8: 0x612efa5a},
+        "l2_patch_add_del": {0xa1f6a6f3: 0x522f3445},
+        "sw_interface_set_l2_xconnect": {0x4fa28a85: 0x1aaa2dbb},
+        "sw_interface_set_l2_bridge": {0xd0678b13: 0x2e483cd0},
+        "bd_ip_mac_add_del": {0x257c869: 0x5f2b84e2},
+        "bd_ip_mac_details": {0x545af86a: 0xa52f8044},
+        "l2_arp_term_event": {0x6963e07a: 0x85ff71ea},
+        "l2tpv3_create_tunnel": {0x15bed0c2: 0x596892cb},
+        "sw_if_l2tpv3_tunnel_details": {0x50b88993: 0x1dab5c7e},
+        "lisp_add_del_local_eid": {0x4e5a83a2: 0x21f573bd},
+        "lisp_add_del_map_server": {0xce19e32d: 0x6598ea7c},
+        "lisp_add_del_map_resolver": {0xce19e32d: 0x6598ea7c},
+        "lisp_use_petr": {0xd87dbad9: 0x9e141831},
+        "show_lisp_use_petr_reply": {0x22b9a4b0: 0xdcad8a81},
+        "lisp_add_del_remote_mapping": {0x6d5c789e: 0xfae8ed77},
+        "lisp_add_del_adjacency": {0x2ce0e6f6: 0xcf5edb61},
+        "lisp_locator_details": {0x2c620ffe: 0xc0c4c2a7},
+        "lisp_eid_table_details": {0x1c29f792: 0x4bc32e3a},
+        "lisp_eid_table_dump": {0x629468b5: 0xb959b73b},
+        "lisp_adjacencies_get_reply": {0x807257bf: 0x3f97bcdd},
+        "lisp_map_resolver_details": {0x3e78fc57: 0x82a09deb},
+        "lisp_map_server_details": {0x3e78fc57: 0x82a09deb},
+        "one_add_del_local_eid": {0x4e5a83a2: 0x21f573bd},
+        "one_add_del_map_server": {0xce19e32d: 0x6598ea7c},
+        "one_add_del_map_resolver": {0xce19e32d: 0x6598ea7c},
+        "one_use_petr": {0xd87dbad9: 0x9e141831},
+        "show_one_use_petr_reply": {0x84a03528: 0x10e744a6},
+        "one_add_del_remote_mapping": {0x6d5c789e: 0xfae8ed77},
+        "one_add_del_l2_arp_entry": {0x1aa5e8b3: 0x33209078},
+        "one_l2_arp_entries_get_reply": {0xb0dd200f: 0xb0a47bbe},
+        "one_add_del_ndp_entry": {0xf8a287c: 0xd1629a2f},
+        "one_ndp_entries_get_reply": {0x70719b1a: 0xbd34161},
+        "one_add_del_adjacency": {0x9e830312: 0xe48e7afe},
+        "one_locator_details": {0x2c620ffe: 0xc0c4c2a7},
+        "one_eid_table_details": {0x1c29f792: 0x4bc32e3a},
+        "one_eid_table_dump": {0xbd190269: 0x95151038},
+        "one_adjacencies_get_reply": {0x85bab89: 0xa8ed89a5},
+        "one_map_resolver_details": {0x3e78fc57: 0x82a09deb},
+        "one_map_server_details": {0x3e78fc57: 0x82a09deb},
+        "one_stats_details": {0x2eb74678: 0xff6ef238},
+        "gpe_add_del_fwd_entry": {0xf0847644: 0xde6df50f},
+        "gpe_fwd_entries_get_reply": {0xc4844876: 0xf9f53f1b},
+        "gpe_fwd_entry_path_details": {0x483df51a: 0xee80b19a},
+        "gpe_add_del_native_fwd_rpath": {0x43fc8b54: 0x812da2f2},
+        "gpe_native_fwd_rpaths_get_reply": {0x7a1ca5a2: 0x79d54eb9},
+        "sw_interface_set_lldp": {0x57afbcd4: 0xd646ae0f},
+        "mpls_ip_bind_unbind": {0xc7533b32: 0x48249a27},
+        "mpls_tunnel_add_del": {0x44350ac1: 0xe57ce61d},
+        "mpls_tunnel_details": {0x57118ae3: 0xf3c0928e},
+        "mpls_route_add_del": {0x8e1d1e07: 0x343cff54},
+        "mpls_route_details": {0x9b5043dc: 0xd0ac384c},
+        "policer_add_del": {0x2b31dd38: 0xcb948f6e},
+        "policer_details": {0x72d0e248: 0xa43f781a},
+        "qos_store_enable_disable": {0xf3abcc8b: 0x3507235e},
+        "qos_store_details": {0x3ee0aad7: 0x38a6d48},
+        "qos_record_enable_disable": {0x2f1a4a38: 0x25b33f88},
+        "qos_record_details": {0xa425d4d3: 0x4956ccdd},
+        "session_rule_add_del": {0xe4895422: 0xe31f9443},
+        "session_rules_details": {0x28d71830: 0x304b91f0},
+        "sw_interface_span_enable_disable": {0x23ddd96b: 0xacc8fea1},
+        "sw_interface_span_details": {0x8a20e79f: 0x55643fc},
+        "sr_mpls_steering_add_del": {0x64acff63: 0x7d1b0a0b},
+        "sr_mpls_policy_assign_endpoint_color": {0xe7eb978: 0x5e1c5c13},
+        "sr_localsid_add_del": {0x5a36c324: 0x26fa3309},
+        "sr_policy_add": {0x44ac92e8: 0xec79ee6a},
+        "sr_policy_mod": {0xb97bb56e: 0xe531a102},
+        "sr_steering_add_del": {0xe46b0a0f: 0x3711dace},
+        "sr_localsids_details": {0x2e9221b9: 0x6a6c0265},
+        "sr_policies_details": {0xdb6ff2a1: 0x7ec2d93},
+        "sr_steering_pol_details": {0xd41258c9: 0x1c1ee786},
+        "syslog_set_sender": {0xb8011d0b: 0xbb641285},
+        "syslog_get_sender_reply": {0x424cfa4e: 0xd3da60ac},
+        "tcp_configure_src_addresses": {0x67eede0d: 0x4b02b946},
+        "teib_entry_add_del": {0x8016cfd2: 0x5aa0a538},
+        "teib_details": {0x981ee1a1: 0xe3b6a503},
+        "udp_encap_add": {0xf74a60b1: 0x61d5fc48},
+        "udp_encap_details": {0x8cfb9c76: 0x87c82821},
+        "vxlan_gbp_tunnel_add_del": {0x6c743427: 0x8c819166},
+        "vxlan_gbp_tunnel_details": {0x66e94a89: 0x1da24016},
+        "vxlan_gpe_add_del_tunnel": {0xa645b2b0: 0x7c6da6ae},
+        "vxlan_gpe_tunnel_details": {0x968fc8b: 0x57712346},
+        "vxlan_add_del_tunnel": {0xc09dc80: 0xa35dc8f5},
+        "vxlan_tunnel_details": {0xc3916cb1: 0xe782f70f},
+        "vxlan_offload_rx": {0x9cc95087: 0x89a1564b},
+        "log_details": {0x3d61cc0: 0x255827a1},
 }
 
 
@@ -1358,6 +1370,7 @@ def foldup_crcs(s):
             if f.crc in fixup_crc_dict.get(f.name):
                 f.crc = fixup_crc_dict.get(f.name).get(f.crc)
 
+
 #
 # Main
 #
@@ -1365,13 +1378,13 @@ def main():
     if sys.version_info < (3, 5,):
         log.exception('vppapigen requires a supported version of python. '
                       'Please use version 3.5 or greater. '
-                      'Using {}'.format(sys.version))
+                      'Using %s', sys.version)
         return 1
 
     cliparser = argparse.ArgumentParser(description='VPP API generator')
-    cliparser.add_argument('--pluginpath', default=""),
-    cliparser.add_argument('--includedir', action='append'),
-    cliparser.add_argument('--outputdir', action='store'),
+    cliparser.add_argument('--pluginpath', default="")
+    cliparser.add_argument('--includedir', action='append')
+    cliparser.add_argument('--outputdir', action='store')
     cliparser.add_argument('--input')
     cliparser.add_argument('--output', nargs='?',
                            type=argparse.FileType('w', encoding='UTF-8'),
@@ -1431,8 +1444,8 @@ def main():
         plugin = SourceFileLoader(args.output_module,
                                   module_path).load_module()
     except Exception as err:
-        log.exception('Error importing output plugin: {}, {}'
-                      .format(module_path, err))
+        log.exception('Error importing output plugin: %s, %s',
+                      module_path, err)
         return 1
 
     parser = VPPAPI(debug=args.debug, filename=filename, logger=log,
@@ -1461,6 +1474,8 @@ def main():
         s = parser.process(result)
     else:
         s = parser.process(parsed_objects)
+        imports = parser.process_imports(parsed_objects, False, result)
+        s['imported'] = parser.process(imports)
 
     # Add msg_id field
     s['Define'] = add_msg_id(s['Define'])
@@ -1482,8 +1497,7 @@ def main():
     if result:
         print(result, file=args.output)
     else:
-        log.exception('Running plugin failed: {} {}'
-                      .format(filename, result))
+        log.exception('Running plugin failed: %s %s', filename, result)
         return 1
     return 0
 
index a879074..4369dd8 100644 (file)
@@ -1,4 +1,28 @@
-# C generation
+#
+# Copyright (c) 2020 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
+# binary API format
+#
+
+'''
+This module creates C code for core VPP, VPP plugins and client side VAT and
+VAT2 tests.
+'''
+
 import datetime
 import os
 import time
@@ -8,10 +32,588 @@ import shutil
 
 process_imports = False
 
-datestring = datetime.datetime.utcfromtimestamp(
+###############################################################################
+class ToJSON():
+    '''Class to generate functions converting from VPP binary API to JSON.'''
+    _dispatch = {}
+    noprint_fields = {'_vl_msg_id': None,
+                      'client_index': None,
+                      'context': None}
+    is_number = {'u8': None,
+                 'i8': None,
+                 'u16': None,
+                 'i16': None,
+                 'u32': None,
+                 'i32': None,
+                 'u64': None,
+                 'i64': None,
+                 'f64': None,
+                 }
+
+    def __init__(self, module, types, defines, imported_types, stream):
+        self.stream = stream
+        self.module = module
+        self.defines = defines
+        self.types = types
+        self.types_hash = {'vl_api_'+d.name+'_t':
+                           d for d in types + imported_types}
+        self.defines_hash = {d.name: d for d in defines}
+
+    def header(self):
+        '''Output the top boilerplate.'''
+        write = self.stream.write
+        write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
+        write('#define included_{}_api_tojson_h\n'.format(self.module))
+        write('#include <vppinfra/cJSON.h>\n\n')
+        write('#include <vat2/jsonconvert.h>\n\n')
+
+    def footer(self):
+        '''Output the bottom boilerplate.'''
+        write = self.stream.write
+        write('#endif\n')
+
+    def get_json_func(self, t):
+        '''Given the type, returns the function to use to create a
+        cJSON object'''
+        vt_type = None
+        try:
+            vt = self.types_hash[t]
+            if vt.type == 'Using' and 'length' not in vt.alias:
+                vt_type = vt.alias['type']
+        except KeyError:
+            vt = t
+
+        if t in self.is_number or vt_type in self.is_number:
+            return 'cJSON_AddNumberToObject', '', False
+        if t == 'bool':
+            return 'cJSON_AddBoolToObject', '', False
+
+        # Lookup type name check if it's enum
+        if vt.type == 'Enum':
+            return '{t}_tojson'.format(t=t), '', True
+        return '{t}_tojson'.format(t=t), '&', True
+
+    def get_json_array_func(self, t):
+        '''Given a type returns the function to create a cJSON object
+        for arrays.'''
+        if t in self.is_number:
+            return 'cJSON_CreateNumber', ''
+        if t == 'bool':
+            return 'cJSON_CreateBool', ''
+        return '{t}_tojson'.format(t=t), '&'
+
+    def print_string(self, o):
+        '''Create cJSON object from vl_api_string_t'''
+        write = self.stream.write
+        if o.modern_vla:
+            write('    vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
+                  .format(n=o.fieldname))
+        else:
+
+            write('    cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
+                  .format(n=o.fieldname))
+
+    def print_field(self, o):
+        '''Called for every field in a typedef or define.'''
+        write = self.stream.write
+        if o.fieldname in self.noprint_fields:
+            return
+
+        f, p, newobj = self.get_json_func(o.fieldtype)
+
+        if newobj:
+            write('    cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
+                  .format(f=f, p=p, n=o.fieldname))
+        else:
+            write('    {f}(o, "{n}", {p}a->{n});\n'
+                  .format(f=f, p=p, n=o.fieldname))
+
+    _dispatch['Field'] = print_field
+
+    def print_array(self, o):
+        '''Converts a VPP API array to cJSON array.'''
+        write = self.stream.write
+
+        forloop = '''\
+    {{
+        int i;
+        cJSON *array = cJSON_AddArrayToObject(o, "{n}");
+        for (i = 0; i < {lfield}; i++) {{
+            cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
+        }}
+    }}
+'''
+
+        if o.fieldtype == 'string':
+            self.print_string(o)
+            return
+
+        lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
+        if o.fieldtype == 'u8':
+            write('    {\n')
+            # What is length field doing here?
+            write('    u8 *s = format(0, "0x%U", format_hex_bytes, '
+                  '&a->{n}, {lfield});\n'
+                  .format(n=o.fieldname, lfield=lfield))
+            write('    cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
+                  .format(n=o.fieldname))
+            write('    vec_free(s);\n')
+            write('    }\n')
+            return
+
+        f, p = self.get_json_array_func(o.fieldtype)
+        write(forloop.format(lfield=lfield,
+                             t=o.fieldtype,
+                             n=o.fieldname,
+                             f=f,
+                             p=p))
+
+    _dispatch['Array'] = print_array
+
+    def print_enum(self, o):
+        '''Create cJSON object (string) for VPP API enum'''
+        write = self.stream.write
+        write('static inline cJSON *vl_api_{name}_t_tojson '
+              '(vl_api_{name}_t a) {{\n'.format(name=o.name))
+
+        write("    switch(a) {\n")
+        for b in o.block:
+            write("    case %s:\n" % b[1])
+            write('        return cJSON_CreateString("{}");\n'.format(b[0]))
+        write('    default: return cJSON_CreateString("Invalid ENUM");\n')
+        write('    }\n')
+        write('    return 0;\n')
+        write('}\n')
+
+    _dispatch['Enum'] = print_enum
+
+    def print_typedef(self, o):
+        '''Create cJSON (dictionary) object from VPP API typedef'''
+        write = self.stream.write
+        write('static inline cJSON *vl_api_{name}_t_tojson '
+              '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
+        write('    cJSON *o = cJSON_CreateObject();\n')
+
+        for t in o.block:
+            self._dispatch[t.type](self, t)
+
+        write('    return o;\n')
+        write('}\n')
+
+    def print_define(self, o):
+        '''Create cJSON (dictionary) object from VPP API define'''
+        write = self.stream.write
+        write('static inline cJSON *vl_api_{name}_t_tojson '
+              '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
+        write('    cJSON *o = cJSON_CreateObject();\n')
+        write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'
+              .format(o.name))
+
+        for t in o.block:
+            self._dispatch[t.type](self, t)
+
+        write('    return o;\n')
+        write('}\n')
+
+    def print_using(self, o):
+        '''Create cJSON (dictionary) object from VPP API aliased type'''
+        if o.manual_print:
+            return
+
+        write = self.stream.write
+        write('static inline cJSON *vl_api_{name}_t_tojson '
+              '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
+
+        write('    u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
+              .format(o.name))
+        write('    cJSON *o = cJSON_CreateString((char *)s);\n')
+        write('    vec_free(s);\n')
+        write('    return o;\n')
+        write('}\n')
+
+    _dispatch['Typedef'] = print_typedef
+    _dispatch['Define'] = print_define
+    _dispatch['Using'] = print_using
+    _dispatch['Union'] = print_typedef
+
+    def generate_function(self, t):
+        '''Main entry point'''
+        write = self.stream.write
+        if t.manual_print:
+            write('/* Manual print {} */\n'.format(t.name))
+            return
+        self._dispatch[t.type](self, t)
+
+    def generate_types(self):
+        '''Main entry point'''
+        for t in self.types:
+            self.generate_function(t)
+
+    def generate_defines(self):
+        '''Main entry point'''
+        for t in self.defines:
+            self.generate_function(t)
+
+
+class FromJSON():
+    '''
+    Parse JSON objects into VPP API binary message structures.
+    '''
+    _dispatch = {}
+    noprint_fields = {'_vl_msg_id': None,
+                      'client_index': None,
+                      'context': None}
+    is_number = {'u8': None,
+                 'i8': None,
+                 'u16': None,
+                 'i16': None,
+                 'u32': None,
+                 'i32': None,
+                 'u64': None,
+                 'i64': None,
+                 'f64': None,
+                 }
+
+    def __init__(self, module, types, defines, imported_types, stream):
+        self.stream = stream
+        self.module = module
+        self.defines = defines
+        self.types = types
+        self.types_hash = {'vl_api_'+d.name+'_t':
+                           d for d in types + imported_types}
+        self.defines_hash = {d.name: d for d in defines}
+
+    def header(self):
+        '''Output the top boilerplate.'''
+        write = self.stream.write
+        write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
+        write('#define included_{}_api_fromjson_h\n'.format(self.module))
+        write('#include <vppinfra/cJSON.h>\n\n')
+        write('#include <vat2/jsonconvert.h>\n\n')
+
+    def is_base_type(self, t):
+        '''Check if a type is one of the VPP API base types'''
+        if t in self.is_number:
+            return True
+        if t == 'bool':
+            return True
+        return False
+
+    def footer(self):
+        '''Output the bottom boilerplate.'''
+        write = self.stream.write
+        write('#endif\n')
+
+    def print_string(self, o, toplevel=False):
+        '''Convert JSON string to vl_api_string_t'''
+        write = self.stream.write
+
+        msgvar = "a" if toplevel else "mp"
+        msgsize = "l" if toplevel else "*len"
+
+        if o.modern_vla:
+            write('    char *p = cJSON_GetStringValue(item);\n')
+            write('    size_t plen = strlen(p);\n')
+            write('    {msgvar} = realloc({msgvar}, {msgsize} + plen);\n'
+                  .format(msgvar=msgvar, msgsize=msgsize))
+            write('    vl_api_c_string_to_api_string(p, (void *){msgvar} + '
+                  '{msgsize} - sizeof(vl_api_string_t));\n'
+                  .format(msgvar=msgvar, msgsize=msgsize))
+            write('    {msgsize} += plen;\n'.format(msgsize=msgsize))
+        else:
+            write('    strncpy_s((char *)a->{n}, sizeof(a->{n}), '
+                  'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
+                  .format(n=o.fieldname))
+
+    def print_field(self, o, toplevel=False):
+        '''Called for every field in a typedef or define.'''
+        write = self.stream.write
+        write('    // start field {}\n'.format(o.fieldname))
+        if o.fieldname in self.noprint_fields:
+            return
+        is_bt = self.is_base_type(o.fieldtype)
+        t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
+
+        msgvar = "a" if toplevel else "mp"
+        msgsize = "&l" if toplevel else "len"
+
+        if is_bt:
+            write('    vl_api_{t}_fromjson(item, &a->{n});\n'
+                  .format(t=o.fieldtype, n=o.fieldname))
+        else:
+            write('    {msgvar} = {t}_fromjson({msgvar}, '
+                  '{msgsize}, item, &a->{n});\n'
+                  .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
+            write('    if (!{msgvar}) return 0;\n'.format(msgvar=msgvar))
+
+        write('    // end field {}\n'.format(o.fieldname))
+
+    _dispatch['Field'] = print_field
+
+    def print_array(self, o, toplevel=False):
+        '''Convert JSON array to VPP API array'''
+        write = self.stream.write
+
+        forloop = '''\
+    {{
+        int i;
+        cJSON *array = cJSON_GetObjectItem(o, "{n}");
+        int size = cJSON_GetArraySize(array);
+        if (size != {lfield}) return 0;
+        for (i = 0; i < size; i++) {{
+            cJSON *e = cJSON_GetArrayItem(array, i);
+            {call}
+        }}
+    }}
+'''
+        forloop_vla = '''\
+    {{
+        int i;
+        cJSON *array = cJSON_GetObjectItem(o, "{n}");
+        int size = cJSON_GetArraySize(array);
+        {lfield} = size;
+        {msgvar} = realloc({msgvar}, {msgsize} + sizeof({t}) * size);
+        {t} *d = (void *){msgvar} + {msgsize};
+        {msgsize} += sizeof({t}) * size;
+        for (i = 0; i < size; i++) {{
+            cJSON *e = cJSON_GetArrayItem(array, i);
+            {call}
+        }}
+    }}
+'''
+        t = o.fieldtype
+        if o.fieldtype == 'string':
+            self.print_string(o, toplevel)
+            return
+
+        lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
+        msgvar = "a" if toplevel else "mp"
+        msgsize = "l" if toplevel else "*len"
+
+        if o.fieldtype == 'u8':
+            if o.lengthfield:
+                write('    s = u8string_fromjson(o, "{}");\n'
+                      .format(o.fieldname))
+                write('    if (!s) return 0;\n')
+                write('    {} = vec_len(s);\n'.format(lfield))
+
+                write('    {msgvar} = realloc({msgvar}, {msgsize} + '
+                      'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
+                write('    memcpy((void *){msgvar} + {msgsize}, s, '
+                      'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
+                write('    {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
+
+                write('    vec_free(s);\n')
+            else:
+                write('    u8string_fromjson2(o, "{n}", a->{n});\n'
+                      .format(n=o.fieldname))
+            return
+
+        is_bt = self.is_base_type(o.fieldtype)
+
+        if o.lengthfield:
+            if is_bt:
+                call = ('vl_api_{t}_fromjson(e, &d[i]);'
+                        .format(t=o.fieldtype))
+            else:
+                call = ('{t}_fromjson({msgvar}, len, e, &d[i]); '
+                        .format(t=o.fieldtype, msgvar=msgvar))
+            write(forloop_vla.format(lfield=lfield,
+                                     t=o.fieldtype,
+                                     n=o.fieldname,
+                                     call=call,
+                                     msgvar=msgvar,
+                                     msgsize=msgsize))
+        else:
+            if is_bt:
+                call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
+                        .format(t=t, n=o.fieldname))
+            else:
+                call = ('a = {}_fromjson({}, len, e, &a->{}[i]);'
+                        .format(t, msgvar, o.fieldname))
+            write(forloop.format(lfield=lfield,
+                                 t=t,
+                                 n=o.fieldname,
+                                 call=call,
+                                 msgvar=msgvar,
+                                 msgsize=msgsize))
+
+    _dispatch['Array'] = print_array
+
+    def print_enum(self, o):
+        '''Convert to JSON enum(string) to VPP API enum (int)'''
+        write = self.stream.write
+        write('static inline void *vl_api_{n}_t_fromjson '
+              '(void *mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
+              .format(n=o.name))
+        write('    char *p = cJSON_GetStringValue(o);\n')
+        for b in o.block:
+            write('    if (strcmp(p, "{}") == 0) {{*a = {}; return mp;}}\n'
+                  .format(b[0], b[1]))
+        write('   return 0;\n')
+        write('}\n')
+
+    _dispatch['Enum'] = print_enum
+
+    def print_typedef(self, o):
+        '''Convert from JSON object to VPP API binary representation'''
+        write = self.stream.write
+
+        write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
+              'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
+              .format(name=o.name))
+        write('    cJSON *item __attribute__ ((unused));\n')
+        write('    u8 *s __attribute__ ((unused));\n')
+        for t in o.block:
+            if t.type == 'Field' and t.is_lengthfield:
+                continue
+            write('    item = cJSON_GetObjectItem(o, "{}");\n'
+                  .format(t.fieldname))
+            write('    if (!item) return 0;\n')
+
+            self._dispatch[t.type](self, t)
+
+        write('    return mp;\n')
+        write('}\n')
+
+    def print_union(self, o):
+        '''Convert JSON object to VPP API binary union'''
+        write = self.stream.write
+
+        write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
+              'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
+              .format(name=o.name))
+        write('    cJSON *item __attribute__ ((unused));\n')
+        write('    u8 *s __attribute__ ((unused));\n')
+        for t in o.block:
+            if t.type == 'Field' and t.is_lengthfield:
+                continue
+            write('    item = cJSON_GetObjectItem(o, "{}");\n'
+                  .format(t.fieldname))
+            write('    if (item) {\n')
+            self._dispatch[t.type](self, t)
+            write('    };\n')
+        write('    return mp;\n')
+        write('}\n')
+
+    def print_define(self, o):
+        '''Convert JSON object to VPP API message'''
+        write = self.stream.write
+        write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
+              '(cJSON *o, int *len) {{\n'.format(name=o.name))
+        write('    cJSON *item __attribute__ ((unused));\n')
+        write('    u8 *s __attribute__ ((unused));\n')
+        write('    int l = sizeof(vl_api_{}_t);\n'.format(o.name))
+        write('    vl_api_{}_t *a = malloc(l);\n'.format(o.name))
+
+        for t in o.block:
+            if t.fieldname in self.noprint_fields:
+                continue
+            if t.type == 'Field' and t.is_lengthfield:
+                continue
+            write('    // processing {}: {} {}\n'
+                  .format(o.name, t.fieldtype, t.fieldname))
+
+            write('    item = cJSON_GetObjectItem(o, "{}");\n'
+                  .format(t.fieldname))
+            write('    if (!item) return 0;\n')
+            self._dispatch[t.type](self, t, toplevel=True)
+            write('\n')
+
+        write('\n')
+        write('    *len = l;\n')
+        write('    return a;\n')
+        write('}\n')
+
+    def print_using(self, o):
+        '''Convert JSON field to VPP type alias'''
+        write = self.stream.write
+
+        if o.manual_print:
+            return
+
+        t = o.using
+        write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
+              'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
+              .format(name=o.name))
+        if 'length' in o.alias:
+            if t.fieldtype != 'u8':
+                raise ValueError("Error in processing type {} for {}"
+                                 .format(t.fieldtype, o.name))
+            write('    vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
+                  .format(o.alias['length']))
+        else:
+            write('    vl_api_{t}_fromjson(o, ({t} *)a);\n'
+                  .format(t=t.fieldtype))
+
+        write('    return mp;\n')
+        write('}\n')
+
+    _dispatch['Typedef'] = print_typedef
+    _dispatch['Define'] = print_define
+    _dispatch['Using'] = print_using
+    _dispatch['Union'] = print_union
+
+    def generate_function(self, t):
+        '''Main entry point'''
+        write = self.stream.write
+        if t.manual_print:
+            write('/* Manual print {} */\n'.format(t.name))
+            return
+        self._dispatch[t.type](self, t)
+
+    def generate_types(self):
+        '''Main entry point'''
+        for t in self.types:
+            self.generate_function(t)
+
+    def generate_defines(self):
+        '''Main entry point'''
+        for t in self.defines:
+            self.generate_function(t)
+
+
+def generate_tojson(s, modulename, stream):
+    '''Generate all functions to convert from API to JSON'''
+    write = stream.write
+
+    write('/* Imported API files */\n')
+    for i in s['Import']:
+        f = i.filename.replace('plugins/', '')
+        write('#include <{}_tojson.h>\n'.format(f))
+
+    pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
+                stream)
+    pp.header()
+    pp.generate_types()
+    pp.generate_defines()
+    pp.footer()
+    return ''
+
+
+def generate_fromjson(s, modulename, stream):
+    '''Generate all functions to convert from JSON to API'''
+    write = stream.write
+    write('/* Imported API files */\n')
+    for i in s['Import']:
+        f = i.filename.replace('plugins/', '')
+        write('#include <{}_fromjson.h>\n'.format(f))
+
+    pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
+                  stream)
+    pp.header()
+    pp.generate_types()
+    pp.generate_defines()
+    pp.footer()
+
+    return ''
+
+###############################################################################
+
+
+DATESTRING = datetime.datetime.utcfromtimestamp(
     int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
-input_filename = 'inputfil'
-top_boilerplate = '''\
+TOP_BOILERPLATE = '''\
 /*
  * VLIB API definitions {datestring}
  * Input file: {input_filename}
@@ -32,7 +634,7 @@ top_boilerplate = '''\
 #define VL_API_PACKED(x) x __attribute__ ((packed))
 '''
 
-bottom_boilerplate = '''\
+BOTTOM_BOILERPLATE = '''\
 /****** API CRC (whole file) *****/
 
 #ifdef vl_api_version
@@ -43,6 +645,7 @@ vl_api_version({input_filename}, {file_crc:#08x})
 
 
 def msg_ids(s):
+    '''Generate macro to map API message id to handler'''
     output = '''\
 
 /****** Message ID / handler enum ******/
@@ -59,6 +662,7 @@ def msg_ids(s):
 
 
 def msg_names(s):
+    '''Generate calls to name mapping macro'''
     output = '''\
 
 /****** Message names ******/
@@ -75,6 +679,7 @@ def msg_names(s):
 
 
 def msg_name_crc_list(s, suffix):
+    '''Generate list of names to CRC mappings'''
     output = '''\
 
 /****** Message name, crc list ******/
@@ -92,6 +697,7 @@ def msg_name_crc_list(s, suffix):
 
 
 def api2c(fieldtype):
+    '''Map between API type names and internal VPP type names'''
     mappingtable = {'string': 'vl_api_string_t', }
     if fieldtype in mappingtable:
         return mappingtable[fieldtype]
@@ -99,7 +705,7 @@ def api2c(fieldtype):
 
 
 def typedefs(filename):
-
+    '''Include in the main files to the types file'''
     output = '''\
 
 /****** Typedefs ******/
@@ -111,7 +717,7 @@ def typedefs(filename):
     return output
 
 
-format_strings = {'u8': '%u',
+FORMAT_STRINGS = {'u8': '%u',
                   'bool': '%u',
                   'i8': '%d',
                   'u16': '%u',
@@ -122,18 +728,20 @@ format_strings = {'u8': '%u',
                   'i64': '%lld',
                   'f64': '%.2f'}
 
-noprint_fields = {'_vl_msg_id': None,
-                  'client_index': None,
-                  'context': None}
-
 
 class Printfun():
+    '''Functions for pretty printing VPP API messages'''
     _dispatch = {}
+    noprint_fields = {'_vl_msg_id': None,
+                      'client_index': None,
+                      'context': None}
 
     def __init__(self, stream):
         self.stream = stream
 
-    def print_string(self, o, stream):
+    @staticmethod
+    def print_string(o, stream):
+        '''Pretty print a vl_api_string_t'''
         write = stream.write
         if o.modern_vla:
             write('    if (vl_api_string_len(&a->{f}) > 0) {{\n'
@@ -151,11 +759,12 @@ class Printfun():
                   .format(f=o.fieldname))
 
     def print_field(self, o, stream):
+        '''Pretty print API field'''
         write = stream.write
-        if o.fieldname in noprint_fields:
+        if o.fieldname in self.noprint_fields:
             return
-        if o.fieldtype in format_strings:
-            f = format_strings[o.fieldtype]
+        if o.fieldtype in FORMAT_STRINGS:
+            f = FORMAT_STRINGS[o.fieldtype]
             write('    s = format(s, "\\n%U{n}: {f}", '
                   'format_white_space, indent, a->{n});\n'
                   .format(n=o.fieldname, f=f))
@@ -168,6 +777,7 @@ class Printfun():
     _dispatch['Field'] = print_field
 
     def print_array(self, o, stream):
+        '''Pretty print API array'''
         write = stream.write
 
         forloop = '''\
@@ -185,7 +795,8 @@ class Printfun():
 '''
 
         if o.fieldtype == 'string':
-            return self.print_string(o, stream)
+            self.print_string(o, stream)
+            return
 
         if o.fieldtype == 'u8':
             if o.lengthfield:
@@ -199,29 +810,33 @@ class Printfun():
             return
 
         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
-        if o.fieldtype in format_strings:
+        if o.fieldtype in FORMAT_STRINGS:
             write(forloop_format.format(lfield=lfield,
-                                        t=format_strings[o.fieldtype],
+                                        t=FORMAT_STRINGS[o.fieldtype],
                                         n=o.fieldname))
         else:
             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
 
     _dispatch['Array'] = print_array
 
-    def print_alias(self, k, v, stream):
+    @staticmethod
+    def print_alias(k, v, stream):
+        '''Pretty print type alias'''
         write = stream.write
         if ('length' in v.alias and v.alias['length'] and
                 v.alias['type'] == 'u8'):
             write('    return format(s, "%U", format_hex_bytes, a, {});\n'
                   .format(v.alias['length']))
-        elif v.alias['type'] in format_strings:
+        elif v.alias['type'] in FORMAT_STRINGS:
             write('    return format(s, "{}", *a);\n'
-                  .format(format_strings[v.alias['type']]))
+                  .format(FORMAT_STRINGS[v.alias['type']]))
         else:
             write('    return format(s, "{} (print not implemented)");\n'
                   .format(k))
 
-    def print_enum(self, o, stream):
+    @staticmethod
+    def print_enum(o, stream):
+        '''Pretty print API enum'''
         write = stream.write
         write("    switch(*a) {\n")
         for b in o:
@@ -232,6 +847,7 @@ class Printfun():
     _dispatch['Enum'] = print_enum
 
     def print_obj(self, o, stream):
+        '''Entry point'''
         write = stream.write
 
         if o.type in self._dispatch:
@@ -242,6 +858,7 @@ class Printfun():
 
 
 def printfun(objs, stream, modulename):
+    '''Main entry point for pretty print function generation'''
     write = stream.write
 
     h = '''\
@@ -294,6 +911,7 @@ static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
 
 
 def printfun_types(objs, stream, modulename):
+    '''Pretty print API types'''
     write = stream.write
     pp = Printfun(stream)
 
@@ -345,7 +963,8 @@ static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
     write("\n#endif /* vl_printfun_types */\n")
 
 
-def imports(imports):
+def generate_imports(imports):
+    '''Add #include matching the API import statements'''
     output = '/* Imported API files */\n'
     output += '#ifndef vl_api_version\n'
 
@@ -356,7 +975,7 @@ def imports(imports):
     return output
 
 
-endian_strings = {
+ENDIAN_STRINGS = {
     'u16': 'clib_net_to_host_u16',
     'u32': 'clib_net_to_host_u32',
     'u64': 'clib_net_to_host_u64',
@@ -368,6 +987,7 @@ endian_strings = {
 
 
 def endianfun_array(o):
+    '''Generate endian functions for arrays'''
     forloop = '''\
     for (i = 0; i < {length}; i++) {{
         a->{name}[i] = {format}(a->{name}[i]);
@@ -385,10 +1005,10 @@ 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.fieldtype in endian_strings:
+        if o.fieldtype in ENDIAN_STRINGS:
             output += (forloop
                        .format(length=lfield,
-                               format=endian_strings[o.fieldtype],
+                               format=ENDIAN_STRINGS[o.fieldtype],
                                name=o.fieldname))
         else:
             output += (forloop_format
@@ -396,23 +1016,26 @@ def endianfun_array(o):
                                name=o.fieldname))
     return output
 
-no_endian_conversion = {'client_index': None}
+
+NO_ENDIAN_CONVERSION = {'client_index': None}
+
 
 def endianfun_obj(o):
+    '''Generate endian conversion function for type'''
     output = ''
     if o.type == 'Array':
         return endianfun_array(o)
-    elif o.type != 'Field':
+    if o.type != 'Field':
         output += ('    s = format(s, "\\n{} {} {} (print not implemented");\n'
                    .format(o.type, o.fieldtype, o.fieldname))
         return output
-    if o.fieldname in no_endian_conversion:
+    if o.fieldname in NO_ENDIAN_CONVERSION:
         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
         return output
-    if o.fieldtype in endian_strings:
+    if o.fieldtype in ENDIAN_STRINGS:
         output += ('    a->{name} = {format}(a->{name});\n'
                    .format(name=o.fieldname,
-                           format=endian_strings[o.fieldtype]))
+                           format=ENDIAN_STRINGS[o.fieldtype]))
     elif o.fieldtype.startswith('vl_api_'):
         output += ('    {type}_endian(&a->{name});\n'
                    .format(type=o.fieldtype, name=o.fieldname))
@@ -423,6 +1046,7 @@ def endianfun_obj(o):
 
 
 def endianfun(objs, modulename):
+    '''Main entry point for endian function generation'''
     output = '''\
 
 /****** Endian swap functions *****/\n\
@@ -449,9 +1073,9 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
     for t in objs:
         if t.__class__.__name__ == 'Enum':
             output += signature.format(name=t.name)
-            if t.enumtype in endian_strings:
+            if t.enumtype in ENDIAN_STRINGS:
                 output += ('    *a = {}(*a);\n'
-                           .format(endian_strings[t.enumtype]))
+                           .format(ENDIAN_STRINGS[t.enumtype]))
             else:
                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
                            .format(name=t.name))
@@ -469,9 +1093,9 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
                     t.alias['type'] == 'u8'):
                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
                            .format(name=t.name))
-            elif t.alias['type'] in format_strings:
+            elif t.alias['type'] in FORMAT_STRINGS:
                 output += ('    *a = {}(*a);\n'
-                           .format(endian_strings[t.alias['type']]))
+                           .format(ENDIAN_STRINGS[t.alias['type']]))
             else:
                 output += '    /* Not Implemented yet {} */'.format(t.name)
             output += '}\n\n'
@@ -490,6 +1114,7 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
 
 
 def version_tuple(s, module):
+    '''Generate semantic version string'''
     output = '''\
 /****** Version tuple *****/
 
@@ -508,9 +1133,10 @@ def version_tuple(s, module):
 
 
 def generate_include_enum(s, module, stream):
+    '''Generate <name>.api_enum.h'''
     write = stream.write
 
-    if len(s['Define']):
+    if 'Define' in s:
         write('typedef enum {\n')
         for t in s['Define']:
             write('   VL_API_{},\n'.format(t.name.upper()))
@@ -518,7 +1144,8 @@ def generate_include_enum(s, module, stream):
         write('}} vl_api_{}_enum_t;\n'.format(module))
 
 
-def generate_include_counters(s, module, stream):
+def generate_include_counters(s, stream):
+    '''Include file for the counter data model types.'''
     write = stream.write
 
     for counters in s:
@@ -530,14 +1157,11 @@ def generate_include_counters(s, module, stream):
         write('   {}_N_ERROR\n'.format(csetname.upper()))
         write('}} vl_counter_{}_enum_t;\n'.format(csetname))
 
-        # write('extern char *{}_error_strings[];\n'.format(csetname))
-        # write('extern char *{}_description_strings[];\n'.format(csetname))
         write('extern vl_counter_t {}_error_counters[];\n'.format(csetname))
 
-#
-# Generate separate API _types file.
-#
+
 def generate_include_types(s, module, stream):
+    '''Generate separate API _types file.'''
     write = stream.write
 
     write('#ifndef included_{module}_api_types_h\n'.format(module=module))
@@ -546,11 +1170,14 @@ def generate_include_types(s, module, stream):
     if 'version' in s['Option']:
         v = s['Option']['version']
         (major, minor, patch) = v.split('.')
-        write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'.format(m=module.upper(), v=major))
-        write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'.format(m=module.upper(), v=minor))
-        write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'.format(m=module.upper(), v=patch))
-
-    if len(s['Import']):
+        write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
+              .format(m=module.upper(), v=major))
+        write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
+              .format(m=module.upper(), v=minor))
+        write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
+              .format(m=module.upper(), v=patch))
+
+    if 'Import' in s:
         write('/* Imported API files */\n')
         for i in s['Import']:
             filename = i.filename.replace('plugins/', '')
@@ -560,7 +1187,8 @@ def generate_include_types(s, module, stream):
         tname = o.__class__.__name__
         if tname == 'Using':
             if 'length' in o.alias:
-                write('typedef %s vl_api_%s_t[%s];\n' % (o.alias['type'], o.name, o.alias['length']))
+                write('typedef %s vl_api_%s_t[%s];\n' %
+                      (o.alias['type'], o.name, o.alias['length']))
             else:
                 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
         elif tname == 'Enum':
@@ -580,20 +1208,21 @@ def generate_include_types(s, module, stream):
                       % (size1, size2, err_str))
         else:
             if tname == 'Union':
-                write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
+                write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
+                      % o.name)
             else:
                 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
-                           % o.name)
+                      % o.name)
             for b in o.block:
                 if b.type == 'Option':
                     continue
                 if b.type == 'Field':
-                      write("    %s %s;\n" % (api2c(b.fieldtype),
-                                              b.fieldname))
+                    write("    %s %s;\n" % (api2c(b.fieldtype),
+                                            b.fieldname))
                 elif b.type == 'Array':
                     if b.lengthfield:
-                      write("    %s %s[0];\n" % (api2c(b.fieldtype),
-                                                 b.fieldname))
+                        write("    %s %s[0];\n" % (api2c(b.fieldtype),
+                                                   b.fieldname))
                     else:
                         # Fixed length strings decay to nul terminated u8
                         if b.fieldtype == 'string':
@@ -623,6 +1252,7 @@ def generate_include_types(s, module, stream):
 
 def generate_c_boilerplate(services, defines, counters, file_crc,
                            module, stream):
+    '''VPP side plugin.'''
     write = stream.write
     define_hash = {d.name: d for d in defines}
 
@@ -644,34 +1274,36 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
     write('setup_message_id_table (void) {\n')
     write('   api_main_t *am = my_api_main;\n')
     write('   vl_msg_api_msg_config_t c;\n')
-    write('   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", VL_MSG_{m}_LAST);\n'
+    write('   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()))
 
-
     for d in defines:
         write('   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
               '                                VL_API_{ID} + msg_id_base);\n'
               .format(n=d.name, ID=d.name.upper(), crc=d.crc))
     for s in services:
         d = define_hash[s.caller]
-        write('   c = (vl_msg_api_msg_config_t) {{.id = VL_API_{ID} + msg_id_base,\n'
-              '                                  .name = "{n}",\n'
-              '                                  .handler = vl_api_{n}_t_handler,\n'
-              '                                  .cleanup = vl_noop_handler,\n'
-              '                                  .endian = vl_api_{n}_t_endian,\n'
-              '                                  .print = vl_api_{n}_t_print,\n'
-              '                                  .is_autoendian = 0}};\n'
+        write('   c = (vl_msg_api_msg_config_t) '
+              ' {{.id = VL_API_{ID} + msg_id_base,\n'
+              '   .name = "{n}",\n'
+              '   .handler = vl_api_{n}_t_handler,\n'
+              '   .cleanup = vl_noop_handler,\n'
+              '   .endian = vl_api_{n}_t_endian,\n'
+              '   .print = vl_api_{n}_t_print,\n'
+              '   .is_autoendian = 0}};\n'
               .format(n=s.caller, ID=s.caller.upper()))
         write('   vl_msg_api_config (&c);\n')
         try:
             d = define_hash[s.reply]
-            write('   c = (vl_msg_api_msg_config_t) {{.id = VL_API_{ID} + msg_id_base,\n'
-                  '                                  .name = "{n}",\n'
-                  '                                  .handler = 0,\n'
-                  '                                  .cleanup = vl_noop_handler,\n'
-                  '                                  .endian = vl_api_{n}_t_endian,\n'
-                  '                                  .print = vl_api_{n}_t_print,\n'
-                  '                                  .is_autoendian = 0}};\n'
+            write('   c = (vl_msg_api_msg_config_t) '
+                  '{{.id = VL_API_{ID} + msg_id_base,\n'
+                  '  .name = "{n}",\n'
+                  '  .handler = 0,\n'
+                  '  .cleanup = vl_noop_handler,\n'
+                  '  .endian = vl_api_{n}_t_endian,\n'
+                  '  .print = vl_api_{n}_t_print,\n'
+                  '  .is_autoendian = 0}};\n'
                   .format(n=s.reply, ID=s.reply.upper()))
             write('   vl_msg_api_config (&c);\n')
         except KeyError:
@@ -686,16 +1318,6 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
 
     for cnt in counters:
         csetname = cnt.name
-        '''
-        write('char *{}_error_strings[] = {{\n'.format(csetname))
-        for c in cnt.block:
-            write('   "{}",\n'.format(c['name']))
-        write('};\n')
-        write('char *{}_description_strings[] = {{\n'.format(csetname))
-        for c in cnt.block:
-            write('   "{}",\n'.format(c['description']))
-        write('};\n')
-        '''
         write('vl_counter_t {}_error_counters[] = {{\n'.format(csetname))
         for c in cnt.block:
             write('  {\n')
@@ -705,14 +1327,16 @@ def generate_c_boilerplate(services, defines, counters, file_crc,
             write('  },\n')
         write('};\n')
 
-def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
+
+def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
+                                stream):
+    '''Generate code for legacy style VAT. To be deleted.'''
     write = stream.write
 
-    define_hash = {d.name:d for d in defines}
-    replies = {}
+    define_hash = {d.name: d for d in defines}
 
     hdr = '''\
-#define vl_endianfun           /* define message structures */
+#define vl_endianfun            /* define message structures */
 #include "{module}.api.h"
 #undef vl_endianfun
 
@@ -728,19 +1352,23 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, str
     for s in services:
         try:
             d = define_hash[s.reply]
-        except:
+        except KeyError:
             continue
         if d.manual_print:
-            write('/* Manual definition requested for: vl_api_{n}_t_handler() */\n'
+            write('/*\n'
+                  ' * Manual definition requested for: \n'
+                  ' * vl_api_{n}_t_handler()\n'
+                  ' */\n'
                   .format(n=s.reply))
             continue
         if not define_hash[s.caller].autoreply:
-            write('/* Only autoreply is supported (vl_api_{n}_t_handler()) */\n'
+            write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
                   .format(n=s.reply))
             continue
         write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
         write('static void\n')
-        write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=s.reply))
+        write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
+              .format(n=s.reply))
         write('   vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
         write('   i32 retval = ntohl(mp->retval);\n')
         write('   if (vam->async_mode) {\n')
@@ -764,23 +1392,31 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, str
     write('static void\n')
     write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
     for s in services:
-        write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, "{n}",\n'
-              '                           vl_api_{n}_t_handler, vl_noop_handler,\n'
-              '                           vl_api_{n}_t_endian, vl_api_{n}_t_print,\n'
+        write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
+              '                           "{n}",\n'
+              '                           vl_api_{n}_t_handler, '
+              '                           vl_noop_handler,\n'
+              '                           vl_api_{n}_t_endian, '
+              '                           vl_api_{n}_t_print,\n'
               '                           sizeof(vl_api_{n}_t), 1);\n'
               .format(n=s.reply, ID=s.reply.upper()))
-        write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(n=s.caller))
+        write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
+              .format(n=s.caller))
         try:
             write('   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
-                  .format(n=s.caller, help=define_hash[s.caller].options['vat_help']))
-        except:
+                  .format(n=s.caller,
+                          help=define_hash[s.caller].options['vat_help']))
+        except KeyError:
             pass
 
         # Events
         for e in s.events:
-            write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, "{n}",\n'
-                  '                           vl_api_{n}_t_handler, vl_noop_handler,\n'
-                  '                           vl_api_{n}_t_endian, vl_api_{n}_t_print,\n'
+            write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
+                  '                          "{n}",\n'
+                  '                           vl_api_{n}_t_handler, '
+                  '                           vl_noop_handler,\n'
+                  '                           vl_api_{n}_t_endian, '
+                  '                           vl_api_{n}_t_print,\n'
                   '                           sizeof(vl_api_{n}_t), 1);\n'
                   .format(n=e, ID=e.upper()))
 
@@ -788,14 +1424,17 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, str
     if plugin:
         write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
     else:
-        write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'.format(module))
+        write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'
+              .format(module))
     write('{\n')
     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
     write('   mainp->vat_main = vam;\n')
-    write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id ("{n}_{crc:08x}");\n'
+    write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
+          '                       ("{n}_{crc:08x}");\n'
           .format(n=module, crc=file_crc))
     write('   if (mainp->msg_id_base == (u16) ~0)\n')
-    write('      return clib_error_return (0, "{} plugin not loaded...");\n'.format(module))
+    write('      return clib_error_return (0, "{} plugin not loaded...");\n'
+          .format(module))
     write('   setup_message_id_table (vam, mainp->msg_id_base);\n')
     write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
     write('    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
@@ -803,30 +1442,255 @@ def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, str
     write('   return 0;\n')
     write('}\n')
 
+
+def apifunc(func):
+    '''Check if a method is generated already.'''
+    def _f(module, d, processed, *args):
+        if d.name in processed:
+            return None
+        processed[d.name] = True
+        return func(module, d, *args)
+    return _f
+
+
+def c_test_api_service(s, dump, stream):
+    '''Generate JSON code for a service.'''
+    write = stream.write
+
+    req_reply_template = '''\
+static cJSON *
+api_{n} (cJSON *o)
+{{
+  vl_api_{n}_t *mp;
+  int len;
+  if (!o) return 0;
+  mp = vl_api_{n}_t_fromjson(o, &len);
+  if (!mp) {{
+    fprintf(stderr, "Failed converting JSON to API\\n");
+    return 0;
+  }}
+
+  mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
+  vl_api_{n}_t_endian(mp);
+  vac_write((char *)mp, len);
+  free(mp);
+
+  /* Read reply */
+  char *p;
+  int l;
+  vac_read(&p, &l, 5); // XXX: Fix timeout
+    // XXX Will fail in case of event received. Do loop
+  if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
+    fprintf(stderr, "Mismatched reply\\n");
+    return 0;
+  }}
+  vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
+  vl_api_{r}_t_endian(rmp);
+  return vl_api_{r}_t_tojson(rmp);
+}}
+
+'''
+    dump_details_template = '''\
+static cJSON *
+api_{n} (cJSON *o)
+{{
+  u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
+  int len;
+  if (!o) return 0;
+  vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
+  if (!mp) {{
+      fprintf(stderr, "Failed converting JSON to API\\n");
+      return 0;
+  }}
+  mp->_vl_msg_id = msg_id;
+  vl_api_{n}_t_endian(mp);
+  vac_write((char *)mp, len);
+  free(mp);
+
+  vat2_control_ping(123); // FIX CONTEXT
+  cJSON *reply = cJSON_CreateArray();
+
+  u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
+  u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
+
+  while (1) {{
+    /* Read reply */
+    char *p;
+    int l;
+    vac_read(&p, &l, 5); // XXX: Fix timeout
+
+    /* Message can be one of [_details, control_ping_reply
+     * or unrelated event]
+     */
+    u16 reply_msg_id = ntohs(*((u16 *)p));
+    if (reply_msg_id == ping_reply_msg_id) {{
+        break;
+    }}
+
+    if (reply_msg_id == details_msg_id) {{
+        vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
+        vl_api_{r}_t_endian(rmp);
+        cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
+    }}
+  }}
+  return reply;
+}}
+
+'''
+    gets_details_reply_template = '''\
+static cJSON *
+api_{n} (cJSON *o)
+{{
+    u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
+  int len = 0;
+  if (!o) return 0;
+  vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
+  if (!mp) {{
+    fprintf(stderr, "Failed converting JSON to API\\n");
+    return 0;
+  }}
+  mp->_vl_msg_id = msg_id;
+
+  vl_api_{n}_t_endian(mp);
+  vac_write((char *)mp, len);
+  free(mp);
+
+  cJSON *reply = cJSON_CreateArray();
+
+  u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
+  u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
+
+  while (1) {{
+    /* Read reply */
+    char *p;
+    int l;
+    vac_read(&p, &l, 5); // XXX: Fix timeout
+
+    /* Message can be one of [_details, control_ping_reply
+     * or unrelated event]
+     */
+    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);
+        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);
+        cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
+    }}
+  }}
+  return reply;
+}}
+
+'''
+
+    if dump:
+        if s.stream_message:
+            write(gets_details_reply_template
+                  .format(n=s.caller, r=s.reply, N=s.caller.upper(),
+                          R=s.reply.upper(), d=s.stream_message,
+                          D=s.stream_message.upper()))
+        else:
+            write(dump_details_template.format(n=s.caller, r=s.reply,
+                                               N=s.caller.upper(),
+                                               R=s.reply.upper()))
+    else:
+        write(req_reply_template.format(n=s.caller, r=s.reply,
+                                        N=s.caller.upper(),
+                                        R=s.reply.upper()))
+
+
+def generate_c_test2_boilerplate(services, defines, module, stream):
+    '''Generate code for VAT2 plugin.'''
+    write = stream.write
+
+    define_hash = {d.name: d for d in defines}
+    # replies = {}
+
+    hdr = '''\
+#include <vlibapi/api.h>
+#include <vlibmemory/api.h>
+#include <vppinfra/error.h>
+#include <vnet/ip/ip_format_fns.h>
+#include <vnet/ethernet/ethernet_format_fns.h>
+
+#define vl_typedefs             /* define message structures */
+#include <vpp/api/vpe_all_api_h.h>
+#undef vl_typedefs
+
+#include "{module}.api_enum.h"
+#include "{module}.api_types.h"
+
+#define vl_endianfun           /* define message structures */
+#include "{module}.api.h"
+#undef vl_endianfun
+
+#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
+#define vl_printfun
+#include "{module}.api.h"
+#undef vl_printfun
+
+#include "{module}.api_tojson.h"
+#include "{module}.api_fromjson.h"
+#include <vpp-api/client/vppapiclient.h>
+
+#include <vat2/vat2_helpers.h>
+
+'''
+
+    write(hdr.format(module=module))
+
+    for s in services:
+        if s.reply not in define_hash:
+            continue
+        c_test_api_service(s, s.stream, stream)
+
+    write('void vat2_register_function(char *, cJSON * (*)(cJSON *));\n')
+    # write('__attribute__((constructor))')
+    write('clib_error_t *\n')
+    write('vat2_register_plugin (void) {\n')
+    for s in services:
+        write('   vat2_register_function("{n}", api_{n});\n'
+              .format(n=s.caller))
+    write('   return 0;\n')
+    write('}\n')
+
+
 #
 # Plugin entry point
 #
-def run(args, input_filename, s):
+def run(args, apifilename, s):
+    '''Main plugin entry point.'''
     stream = StringIO()
 
     if not args.outputdir:
         sys.stderr.write('Missing --outputdir argument')
         return None
 
-    basename = os.path.basename(input_filename)
-    filename, file_extension = os.path.splitext(basename)
+    basename = os.path.basename(apifilename)
+    filename, _ = os.path.splitext(basename)
     modulename = filename.replace('.', '_')
     filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
     filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
     filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
     filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
+    filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
+                                     '_test2.c'))
+    filename_c_tojson = (os.path.join(args.outputdir +
+                                      '/' + basename + '_tojson.h'))
+    filename_c_fromjson = (os.path.join(args.outputdir + '/' +
+                                        basename + '_fromjson.h'))
 
     # Generate separate types file
     st = StringIO()
     generate_include_types(s, modulename, st)
-    with open (filename_types, 'w') as fd:
-        st.seek (0)
-        shutil.copyfileobj (st, fd)
+    with open(filename_types, 'w') as fd:
+        st.seek(0)
+        shutil.copyfileobj(st, fd)
     st.close()
 
     # Generate separate enum file
@@ -834,35 +1698,59 @@ def run(args, input_filename, s):
     st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
     st.write('#define included_{}_api_enum_h\n'.format(modulename))
     generate_include_enum(s, modulename, st)
-    generate_include_counters(s['Counters'], modulename, st)
+    generate_include_counters(s['Counters'], st)
     st.write('#endif\n')
-    with open (filename_enum, 'w') as fd:
-        st.seek (0)
-        shutil.copyfileobj (st, fd)
+    with open(filename_enum, 'w') as fd:
+        st.seek(0)
+        shutil.copyfileobj(st, fd)
     st.close()
 
     # Generate separate C file
     st = StringIO()
     generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
                            s['file_crc'], modulename, st)
-    with open (filename_c, 'w') as fd:
-        st.seek (0)
+    with open(filename_c, 'w') as fd:
+        st.seek(0)
         shutil.copyfileobj(st, fd)
     st.close()
 
     # Generate separate C test file
     st = StringIO()
-    plugin = True if 'plugin' in input_filename else False
-    generate_c_test_boilerplate(s['Service'], s['Define'], s['file_crc'],
+    plugin = bool('plugin' in apifilename)
+    generate_c_test_boilerplate(s['Service'], s['Define'],
+                                s['file_crc'],
                                 modulename, plugin, st)
-    with open (filename_c_test, 'w') as fd:
-        st.seek (0)
+    with open(filename_c_test, 'w') as fd:
+        st.seek(0)
+        shutil.copyfileobj(st, fd)
+    st.close()
+
+    # Fully autogenerated VATv2 C test file
+    st = StringIO()
+    generate_c_test2_boilerplate(s['Service'], s['Define'],
+                                 modulename, st)
+    with open(filename_c_test2, 'w') as fd:
+        st.seek(0)
+        shutil.copyfileobj(st, fd)
+    st.close()                  #
+
+    # Generate separate JSON file
+    st = StringIO()
+    generate_tojson(s, modulename, st)
+    with open(filename_c_tojson, 'w') as fd:
+        st.seek(0)
+        shutil.copyfileobj(st, fd)
+    st.close()
+    st = StringIO()
+    generate_fromjson(s, modulename, st)
+    with open(filename_c_fromjson, 'w') as fd:
+        st.seek(0)
         shutil.copyfileobj(st, fd)
     st.close()
 
-    output = top_boilerplate.format(datestring=datestring,
+    output = TOP_BOILERPLATE.format(datestring=DATESTRING,
                                     input_filename=basename)
-    output += imports(s['Import'])
+    output += generate_imports(s['Import'])
     output += msg_ids(s)
     output += msg_names(s)
     output += msg_name_crc_list(s, filename)
@@ -873,7 +1761,7 @@ def run(args, input_filename, s):
     stream.close()
     output += endianfun(s['types'] + s['Define'], modulename)
     output += version_tuple(s, basename)
-    output += bottom_boilerplate.format(input_filename=basename,
+    output += BOTTOM_BOILERPLATE.format(input_filename=basename,
                                         file_crc=s['file_crc'])
 
     return output
diff --git a/src/vat2/CMakeLists.txt b/src/vat2/CMakeLists.txt
new file mode 100644 (file)
index 0000000..690267c
--- /dev/null
@@ -0,0 +1,43 @@
+# Copyright (c) 2020 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+##############################################################################
+# vat2
+##############################################################################
+add_vpp_executable(vat2 ENABLE_EXPORTS NO_INSTALL
+  SOURCES
+  main.c
+  plugin.c
+  jsonconvert.c
+
+  DEPENDS api_headers
+
+  LINK_LIBRARIES
+  vlibmemoryclient
+  svm
+  vppinfra
+  vppapiclient
+  Threads::Threads
+  rt m dl crypto
+)
+
+##############################################################################
+# vat2 headers
+##############################################################################
+install(
+  FILES
+  jsonconvert.h
+  vat2_helpers.h
+  DESTINATION include/vat2
+  COMPONENT vpp-dev
+)
diff --git a/src/vat2/jsonconvert.c b/src/vat2/jsonconvert.c
new file mode 100644 (file)
index 0000000..3aeaeed
--- /dev/null
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vppinfra/cJSON.h>
+#include <vnet/ethernet/mac_address.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip_format_fns.h>
+#include <vpp/api/types.h>
+#include "jsonconvert.h"
+
+#define _(T)                                    \
+int vl_api_ ##T## _fromjson(cJSON *o, T *d)     \
+{                                               \
+    if (!cJSON_IsNumber(o)) return -1;          \
+    memcpy(d, &o->valueint, sizeof(T));         \
+    return 0;                                   \
+}
+  foreach_vat2_fromjson
+#undef _
+
+int vl_api_bool_fromjson(cJSON *o, bool *d)
+{
+    if (!cJSON_IsBool(o)) return -1;
+    *d = o->valueint ? true : false;
+    return 0;
+}
+
+int vl_api_u8_string_fromjson(cJSON *o, u8 *s, int len)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "0x%U", unformat_hex_string, s);
+    return 0;
+}
+
+u8 *
+u8string_fromjson(cJSON *o, char *fieldname)
+{
+    u8 *s = 0;
+    unformat_input_t input;
+    cJSON *item = cJSON_GetObjectItem(o, fieldname);
+    if (!item) {
+        printf("Illegal JSON, no such fieldname %s\n", fieldname);
+        return 0;
+    }
+
+    char *p = cJSON_GetStringValue(item);
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "0x%U", unformat_hex_string, &s);
+    return s;
+}
+
+int
+u8string_fromjson2(cJSON *o, char *fieldname, u8 *data)
+{
+    u8 *s = u8string_fromjson(o, fieldname);
+    if (!s) return 0;
+    memcpy(data, s, vec_len(s));
+    vec_free(s);
+    return 0;
+}
+
+/* Parse an IP4 address %d.%d.%d.%d. */
+uword
+unformat_ip4_address (unformat_input_t * input, va_list * args)
+{
+  u8 *result = va_arg (*args, u8 *);
+  unsigned a[4];
+
+  if (!unformat (input, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]))
+    return 0;
+
+  if (a[0] >= 256 || a[1] >= 256 || a[2] >= 256 || a[3] >= 256)
+    return 0;
+
+  result[0] = a[0];
+  result[1] = a[1];
+  result[2] = a[2];
+  result[3] = a[3];
+
+  return 1;
+}
+
+/* Parse an IP6 address. */
+uword
+unformat_ip6_address (unformat_input_t * input, va_list * args)
+{
+  ip6_address_t *result = va_arg (*args, ip6_address_t *);
+  u16 hex_quads[8];
+  uword hex_quad, n_hex_quads, hex_digit, n_hex_digits;
+  uword c, n_colon, double_colon_index;
+
+  n_hex_quads = hex_quad = n_hex_digits = n_colon = 0;
+  double_colon_index = ARRAY_LEN (hex_quads);
+  while ((c = unformat_get_input (input)) != UNFORMAT_END_OF_INPUT)
+    {
+      hex_digit = 16;
+      if (c >= '0' && c <= '9')
+        hex_digit = c - '0';
+      else if (c >= 'a' && c <= 'f')
+        hex_digit = c + 10 - 'a';
+      else if (c >= 'A' && c <= 'F')
+        hex_digit = c + 10 - 'A';
+      else if (c == ':' && n_colon < 2)
+        n_colon++;
+      else
+        {
+          unformat_put_input (input);
+          break;
+        }
+
+      /* Too many hex quads. */
+      if (n_hex_quads >= ARRAY_LEN (hex_quads))
+        return 0;
+
+      if (hex_digit < 16)
+        {
+          hex_quad = (hex_quad << 4) | hex_digit;
+
+          /* Hex quad must fit in 16 bits. */
+          if (n_hex_digits >= 4)
+            return 0;
+
+          n_colon = 0;
+          n_hex_digits++;
+        }
+
+      /* Save position of :: */
+      if (n_colon == 2)
+        {
+          /* More than one :: ? */
+          if (double_colon_index < ARRAY_LEN (hex_quads))
+            return 0;
+          double_colon_index = n_hex_quads;
+        }
+
+      if (n_colon > 0 && n_hex_digits > 0)
+        {
+          hex_quads[n_hex_quads++] = hex_quad;
+          hex_quad = 0;
+          n_hex_digits = 0;
+        }
+    }
+
+  if (n_hex_digits > 0)
+    hex_quads[n_hex_quads++] = hex_quad;
+
+
+  {
+    word i;
+
+    /* Expand :: to appropriate number of zero hex quads. */
+    if (double_colon_index < ARRAY_LEN (hex_quads))
+      {
+        word n_zero = ARRAY_LEN (hex_quads) - n_hex_quads;
+
+        for (i = n_hex_quads - 1; i >= (signed) double_colon_index; i--)
+          hex_quads[n_zero + i] = hex_quads[i];
+
+        for (i = 0; i < n_zero; i++)
+          {
+            ASSERT ((double_colon_index + i) < ARRAY_LEN (hex_quads));
+            hex_quads[double_colon_index + i] = 0;
+          }
+
+        n_hex_quads = ARRAY_LEN (hex_quads);
+      }
+
+    /* Too few hex quads given. */
+    if (n_hex_quads < ARRAY_LEN (hex_quads))
+      return 0;
+
+    for (i = 0; i < ARRAY_LEN (hex_quads); i++)
+      result->as_u16[i] = clib_host_to_net_u16 (hex_quads[i]);
+
+    return 1;
+  }
+}
+
+u8 *
+format_ip6_address (u8 * s, va_list * args)
+{
+  ip6_address_t *a = va_arg (*args, ip6_address_t *);
+  u32 max_zero_run = 0, this_zero_run = 0;
+  int max_zero_run_index = -1, this_zero_run_index = 0;
+  int in_zero_run = 0, i;
+  int last_double_colon = 0;
+
+  /* Ugh, this is a pain. Scan forward looking for runs of 0's */
+  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
+    {
+      if (a->as_u16[i] == 0)
+        {
+          if (in_zero_run)
+            this_zero_run++;
+          else
+            {
+              in_zero_run = 1;
+              this_zero_run = 1;
+              this_zero_run_index = i;
+            }
+        }
+      else
+        {
+          if (in_zero_run)
+            {
+              /* offer to compress the biggest run of > 1 zero */
+              if (this_zero_run > max_zero_run && this_zero_run > 1)
+                {
+                  max_zero_run_index = this_zero_run_index;
+                  max_zero_run = this_zero_run;
+                }
+            }
+          in_zero_run = 0;
+          this_zero_run = 0;
+        }
+    }
+
+  if (in_zero_run)
+    {
+      if (this_zero_run > max_zero_run && this_zero_run > 1)
+        {
+          max_zero_run_index = this_zero_run_index;
+          max_zero_run = this_zero_run;
+        }
+    }
+
+  for (i = 0; i < ARRAY_LEN (a->as_u16); i++)
+    {
+      if (i == max_zero_run_index)
+        {
+          s = format (s, "::");
+          i += max_zero_run - 1;
+          last_double_colon = 1;
+        }
+      else
+        {
+          s = format (s, "%s%x",
+                      (last_double_colon || i == 0) ? "" : ":",
+                      clib_net_to_host_u16 (a->as_u16[i]));
+          last_double_colon = 0;
+        }
+    }
+
+  return s;
+}
+
+void *vl_api_ip4_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_address_t *a)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    if (!p) return 0;
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "%U", unformat_ip4_address, a);
+    return mp;
+}
+
+void *vl_api_ip4_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    if (!p) return 0;
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "%U/%d", unformat_ip4_address, &a->address, &a->len);
+    return mp;
+}
+
+void *vl_api_ip4_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a)
+{
+  return vl_api_ip4_prefix_t_fromjson(mp, len, o, a);
+}
+void *vl_api_ip6_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_address_t *a)
+{
+    unformat_input_t input;
+    char *p = cJSON_GetStringValue(o);
+    if (!p) return 0;
+    unformat_init_string (&input, p, strlen(p));
+    unformat(&input, "%U", unformat_ip6_address, a);
+    return mp;
+}
+
+void *vl_api_ip6_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a)
+{
+  unformat_input_t input;
+  char *p = cJSON_GetStringValue(o);
+  if (!p) return 0;
+  unformat_init_string (&input, p, strlen(p));
+  unformat(&input, "%U/%d", unformat_ip6_address, &a->address, &a->len);
+  return mp;
+}
+
+void *vl_api_ip6_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a)
+{
+  return vl_api_ip6_prefix_t_fromjson(mp, len, o, a);
+}
+
+void *vl_api_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_address_t *a)
+{
+  unformat_input_t input;
+
+  char *p = cJSON_GetStringValue(o);
+  if (!p) return 0;
+  unformat_init_string (&input, p, strlen(p));
+  if (a->af == ADDRESS_IP4)
+    unformat(&input, "%U", unformat_ip4_address, &a->un.ip4);
+  else if (a->af == ADDRESS_IP6)
+    unformat(&input, "%U", unformat_ip6_address, &a->un.ip6);
+  else
+    return 0;
+  return mp;
+}
+
+void *vl_api_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a)
+{
+  unformat_input_t input;
+
+  char *p = cJSON_GetStringValue(o);
+  if (!p) return 0;
+  unformat_init_string (&input, p, strlen(p));
+  if (a->address.af == ADDRESS_IP4)
+    unformat(&input, "%U/%d", unformat_ip4_address, &a->address.un.ip4, &a->len);
+  else if (a->address.af == ADDRESS_IP6)
+    unformat(&input, "%U/%d", unformat_ip6_address, &a->address.un.ip6, &a->len);
+  else
+    return 0;
+  return mp;
+}
+
+void *vl_api_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a)
+{
+  return vl_api_prefix_t_fromjson(mp, len, o, a);
+}
+
+uword
+unformat_mac_address (unformat_input_t * input, va_list * args)
+{
+  mac_address_t *mac = va_arg (*args, mac_address_t *);
+  u32 i, a[3];
+
+  if (unformat (input, "%_%X:%X:%X:%X:%X:%X%_",
+                1, &mac->bytes[0], 1, &mac->bytes[1], 1, &mac->bytes[2],
+                1, &mac->bytes[3], 1, &mac->bytes[4], 1, &mac->bytes[5]))
+    return (1);
+  else if (unformat (input, "%_%x.%x.%x%_", &a[0], &a[1], &a[2]))
+    {
+      for (i = 0; i < ARRAY_LEN (a); i++)
+        if (a[i] >= (1 << 16))
+          return 0;
+
+      mac->bytes[0] = (a[0] >> 8) & 0xff;
+      mac->bytes[1] = (a[0] >> 0) & 0xff;
+      mac->bytes[2] = (a[1] >> 8) & 0xff;
+      mac->bytes[3] = (a[1] >> 0) & 0xff;
+      mac->bytes[4] = (a[2] >> 8) & 0xff;
+      mac->bytes[5] = (a[2] >> 0) & 0xff;
+
+      return (1);
+    }
+  return (0);
+}
+
+void *vl_api_mac_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_mac_address_t *a)
+{
+  unformat_input_t input;
+
+  char *p = cJSON_GetStringValue(o);
+  unformat_init_string (&input, p, strlen(p));
+  unformat(&input, "%U", unformat_mac_address, a);
+  return mp;
+}
+
+/* Format an IP4 address. */
+u8 *
+format_ip4_address (u8 * s, va_list * args)
+{
+  u8 *a = va_arg (*args, u8 *);
+  return format (s, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
+}
+
+int
+vl_api_c_string_to_api_string (const char *buf, vl_api_string_t * str)
+{
+  /* copy without nul terminator */
+  u32 len = strlen (buf);
+  if (len > 0)
+    clib_memcpy_fast (str->buf, buf, len);
+  str->length = htonl (len);
+  return len + sizeof (u32);
+}
+
+u8 *
+format_vl_api_interface_index_t (u8 *s, va_list *args)
+{
+  u32 *a = va_arg (*args, u32 *);
+  return format (s, "%u", *a);
+}
+
+uword
+unformat_vl_api_interface_index_t (unformat_input_t * input, va_list * args)
+{
+    u32 *a = va_arg (*args, u32 *);
+
+    if (!unformat (input, "%u", a))
+        return 0;
+    return 1;
+}
+
+void
+vl_api_string_cJSON_AddToObject(cJSON * const object, const char * const name, vl_api_string_t *astr)
+{
+
+    if (astr == 0) return;
+    u32 length = clib_net_to_host_u32 (astr->length);
+
+    char *cstr = malloc(length + 1);
+    memcpy(cstr, astr->buf, length);
+    cstr[length] = '\0';
+    cJSON_AddStringToObject(object, name, cstr);
+    free(cstr);
+}
+
+u8 *
+format_vl_api_timestamp_t(u8 * s, va_list * args)
+{
+    f64 timestamp = va_arg (*args, f64);
+    struct tm *tm;
+    word msec;
+
+    time_t t = timestamp;
+    tm = gmtime (&t);
+    msec = 1e6 * (timestamp - t);
+    return format (s, "%4d-%02d-%02dT%02d:%02d:%02d.%06dZ", 1900 + tm->tm_year,
+                   1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min,
+                   tm->tm_sec, msec);
+}
+
+u8 *
+format_vl_api_timedelta_t(u8 * s, va_list * args)
+{
+    return format_vl_api_timestamp_t(s, args);
+}
+
+uword
+unformat_vl_api_timedelta_t(unformat_input_t * input, va_list * args)
+{
+    return 0;
+}
+
+uword
+unformat_vl_api_timestamp_t(unformat_input_t * input, va_list * args)
+{
+    return 0;
+}
+u8 *format_vl_api_gbp_scope_t(u8 * s, va_list * args)
+{
+    return 0;
+}
+uword unformat_vl_api_gbp_scope_t(unformat_input_t * input, va_list * args)
+{
+    return 0;
+}
+
+cJSON *
+vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a) {
+  u8 *s = format(0, "%U", format_vl_api_ip4_address_t, a);
+  cJSON *o = cJSON_CreateString((char *)s);
+  vec_free(s);
+  return o;
+}
+cJSON *
+vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a) {
+  u8 *s = format(0, "%U", format_vl_api_ip6_address_t, a);
+  cJSON *o = cJSON_CreateString((char *)s);
+  vec_free(s);
+  return o;
+}
+cJSON *
+vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a) {
+  u8 *s = format(0, "%U", format_vl_api_address_t, a);
+  cJSON *o = cJSON_CreateString((char *)s);
+  vec_free(s);
+  return o;
+}
+u8 *
+format_vl_api_mac_address_t (u8 * s, va_list * args)
+{
+  const mac_address_t *mac = va_arg (*args, mac_address_t *);
+
+  return format (s, "%02x:%02x:%02x:%02x:%02x:%02x",
+                 mac->bytes[0], mac->bytes[1], mac->bytes[2],
+                 mac->bytes[3], mac->bytes[4], mac->bytes[5]);
+}
+#define _(T)                                                \
+  cJSON *vl_api_ ##T## _t_tojson (vl_api_ ##T## _t *a) {   \
+  u8 *s = format(0, "%U", format_vl_api_ ##T## _t, a);      \
+  cJSON *o = cJSON_CreateString((char *)s);                 \
+  vec_free(s);                                              \
+  return o;                                                 \
+  }
+foreach_vat2_tojson
+#undef _
diff --git a/src/vat2/jsonconvert.h b/src/vat2/jsonconvert.h
new file mode 100644 (file)
index 0000000..2e723fa
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_json_convert_h
+#define included_json_convert_h
+
+#include <stdbool.h>
+#include <vppinfra/cJSON.h>
+#include <vnet/ethernet/mac_address.h>
+#include <vnet/ip/ip6_packet.h>
+#include <vnet/ip/ip_types.api_types.h>
+#include <vnet/ethernet/ethernet_types.api_types.h>
+
+#define foreach_vat2_fromjson                   \
+  _(i8)                                         \
+  _(u8)                                         \
+  _(i16)                                        \
+  _(u16)                                        \
+  _(i32)                                        \
+  _(u32)                                        \
+  _(u64)                                        \
+  _(f64)
+
+#define _(T)                                    \
+  int vl_api_ ##T## _fromjson(cJSON *o, T *d);
+  foreach_vat2_fromjson
+#undef _
+
+int vl_api_bool_fromjson(cJSON *o, bool *d);
+void *vl_api_ip4_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_address_t *a);
+void *vl_api_ip4_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a);
+void *vl_api_ip4_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip4_prefix_t *a);
+void *vl_api_ip6_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_address_t *a);
+void *vl_api_ip6_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a);
+void *vl_api_ip6_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_ip6_prefix_t *a);
+void *vl_api_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_address_t *a);
+void *vl_api_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a);
+void *vl_api_address_with_prefix_t_fromjson(void *mp, int *len, cJSON *o, vl_api_prefix_t *a);
+void *vl_api_mac_address_t_fromjson(void *mp, int *len, cJSON *o, vl_api_mac_address_t *a);
+
+uword unformat_ip4_address(unformat_input_t * input, va_list * args);
+uword unformat_ip6_address(unformat_input_t * input, va_list * args);
+u8 *format_ip6_address(u8 * s, va_list * args);
+uword unformat_mac_address(unformat_input_t * input, va_list * args);
+u8 *format_ip4_address(u8 * s, va_list * args);
+u8 *format_vl_api_interface_index_t(u8 *s, va_list *args);
+uword unformat_vl_api_interface_index_t(unformat_input_t * input, va_list * args);
+u8 *format_vl_api_timestamp_t(u8 * s, va_list * args);
+u8 *format_vl_api_timedelta_t(u8 * s, va_list * args);
+uword unformat_vl_api_timedelta_t(unformat_input_t * input, va_list * args);
+uword unformat_vl_api_timestamp_t(unformat_input_t * input, va_list * args);
+u8 *format_vl_api_gbp_scope_t(u8 * s, va_list * args);
+uword unformat_vl_api_gbp_scope_t(unformat_input_t * input, va_list * args);
+
+int vl_api_c_string_to_api_string(const char *buf, vl_api_string_t * str);
+void vl_api_string_cJSON_AddToObject(cJSON * const object, const char * const name, vl_api_string_t *astr);
+
+u8 *u8string_fromjson(cJSON *o, char *fieldname);
+int u8string_fromjson2(cJSON *o, char *fieldname, u8 *data);
+int vl_api_u8_string_fromjson(cJSON *o, u8 *s, int len);
+
+#define foreach_vat2_tojson                     \
+  _(ip4_address)                                \
+  _(ip4_prefix)                                 \
+  _(ip6_address)                                \
+  _(ip6_prefix)                                 \
+  _(address)                                    \
+  _(prefix)                                     \
+  _(mac_address)
+
+#define _(T)                                    \
+  cJSON *vl_api_ ##T## _t_tojson(vl_api_ ##T## _t *);
+  foreach_vat2_tojson
+#undef _
+
+cJSON *vl_api_ip4_address_with_prefix_t_tojson (vl_api_ip4_prefix_t *a);
+cJSON *vl_api_ip6_address_with_prefix_t_tojson (vl_api_ip6_prefix_t *a);
+cJSON *vl_api_address_with_prefix_t_tojson (vl_api_prefix_t *a);
+
+#endif
diff --git a/src/vat2/main.c b/src/vat2/main.c
new file mode 100644 (file)
index 0000000..5b042e2
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <vlib/vlib.h>
+#include <vlibapi/api_types.h>
+#include <vppinfra/cJSON.h>
+
+/* VPP API client includes */
+#include <vpp-api/client/vppapiclient.h>
+
+#include <limits.h>
+#include "vat2.h"
+
+uword *function_by_name;
+bool debug = false;
+
+char *vat2_plugin_path;
+static void
+vat2_find_plugin_path ()
+{
+  char *p, path[PATH_MAX];
+  int rv;
+  u8 *s;
+
+  /* find executable path */
+  if ((rv = readlink ("/proc/self/exe", path, PATH_MAX - 1)) == -1)
+    return;
+
+  /* readlink doesn't provide null termination */
+  path[rv] = 0;
+
+  /* strip filename */
+  if ((p = strrchr (path, '/')) == 0)
+    return;
+  *p = 0;
+
+  /* strip bin/ */
+  if ((p = strrchr (path, '/')) == 0)
+    return;
+  *p = 0;
+
+  s = format (0, "%s/lib/" CLIB_TARGET_TRIPLET "/vat2_plugins:"
+              "%s/lib/vat2_plugins", path, path);
+  vec_add1 (s, 0);
+  vat2_plugin_path = (char *) s;
+}
+
+void
+vac_callback (unsigned char *data, int len)
+{
+  u16 result_msg_id = ntohs(*((u16 *)data));
+  DBG("Received something async: %d\n", result_msg_id);
+}
+
+int vat2_load_plugins (char *path, char *filter, int *loaded);
+
+static int
+register_function (void)
+{
+  int loaded;
+
+  vat2_find_plugin_path();
+  DBG("Plugin Path %s\n", vat2_plugin_path);
+  int rv = vat2_load_plugins(vat2_plugin_path, 0, &loaded);
+  DBG("Loaded %u plugins\n", loaded);
+  return rv;
+}
+
+void
+vat2_register_function(char *name, cJSON (*f)(cJSON *))
+{
+  hash_set_mem(function_by_name, name, f);
+}
+
+int main (int argc, char **argv)
+{
+  /* Create a heap of 64MB */
+  clib_mem_init (0, 64 << 20);
+  char *filename = 0;
+  int index;
+  int c;
+  opterr = 0;
+  cJSON *o = 0;
+  uword *p = 0;
+
+  while ((c = getopt (argc, argv, "df:")) != -1) {
+    switch (c) {
+      case 'd':
+        debug = true;
+        break;
+      case 'f':
+        filename = optarg;
+        break;
+      case '?':
+        if (optopt == 'f')
+          fprintf (stderr, "Option -%c requires an argument.\n", optopt);
+        else if (isprint (optopt))
+          fprintf (stderr, "Unknown option `-%c'.\n", optopt);
+        else
+          fprintf (stderr,
+                   "Unknown option character `\\x%x'.\n",
+                   optopt);
+        return 1;
+      default:
+        abort ();
+    }
+  }
+
+  DBG("debug = %d, filename = %s\n", debug, filename);
+
+  for (index = optind; index < argc; index++)
+    DBG ("Non-option argument %s\n", argv[index]);
+
+  index = optind;
+
+  /* Load plugins */
+  function_by_name = hash_create_string (0, sizeof (uword));
+  int res = register_function();
+  if (res < 0) {
+    fprintf(stderr, "%s: loading plugins failed\n", argv[0]);
+    exit(-1);
+  }
+
+  if (argc > index + 2) {
+    fprintf(stderr, "%s: Too many arguments\n", argv[0]);
+    exit(-1);
+  }
+
+  /* Read JSON from stdin, command line or file */
+  if (argc >= (index + 1)) {
+    p = hash_get_mem (function_by_name, argv[index]);
+    if (p == 0) {
+      fprintf(stderr, "%s: Unknown command: %s\n", argv[0], argv[index]);
+      exit(-1);
+    }
+  }
+
+  if (argc == (index + 2)) {
+    o = cJSON_Parse(argv[index+1]);
+    if (!o) {
+      fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
+      exit(-1);
+    }
+  }
+
+  if (filename) {
+    if (argc > index + 1) {
+      fprintf(stderr, "%s: Superfluous arguments when filename given\n", argv[0]);
+      exit(-1);
+    }
+
+    FILE *f = fopen(filename, "r");
+    size_t bufsize = 1024;
+    size_t n_read = 0;
+    size_t n;
+
+    if (!f) {
+      fprintf(stderr, "%s: can't open file: %s\n", argv[0], filename);
+      exit(-1);
+    }
+    char *buf = malloc(bufsize);
+    while ((n = fread(buf, 1, bufsize, f))) {
+      n_read += n;
+      if (n == bufsize)
+        buf = realloc(buf, bufsize);
+    }
+    fclose(f);
+    if (n_read) {
+      o = cJSON_Parse(buf);
+      free(buf);
+      if (!o) {
+        fprintf(stderr, "%s: Failed parsing JSON input: %s\n", argv[0], cJSON_GetErrorPtr());
+        exit(-1);
+      }
+    }
+  }
+
+  if (!o) {
+    fprintf(stderr, "%s: Failed parsing JSON input\n", argv[0]);
+    exit(-1);
+  }
+
+  if (vac_connect("vat2", 0, 0, 1024)) {
+    fprintf(stderr, "Failed connecting to VPP\n");
+    exit(-1);
+  }
+  if (!p) {
+    fprintf(stderr, "No such command\n");
+    exit(-1);
+  }
+
+  cJSON * (*fp) (cJSON *);
+  fp = (void *) p[0];
+  cJSON *r = (*fp) (o);
+
+  if (o)
+    cJSON_Delete(o);
+
+  if (r) {
+    char *output = cJSON_Print(r);
+    cJSON_Delete(r);
+    printf("%s\n", output);
+    free(output);
+  } else {
+    fprintf(stderr, "Call failed\n");
+    exit(-1);
+  }
+
+  vac_disconnect();
+  exit (0);
+
+}
diff --git a/src/vat2/plugin.c b/src/vat2/plugin.c
new file mode 100644 (file)
index 0000000..6b6d55a
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <vlib/vlib.h>
+#include "vat2.h"
+
+typedef struct
+{
+  u8 *name;
+  u8 *filename;
+  struct stat file_info;
+  void *handle;
+} plugin_info_t;
+
+/* loaded plugin info */
+plugin_info_t *plugin_info;
+
+static int
+load_one_plugin (plugin_info_t * pi)
+{
+  void *handle, *register_handle;
+  clib_error_t *(*fp) (void);
+  clib_error_t *error;
+
+  handle = dlopen ((char *) pi->name, RTLD_LAZY);
+
+  /*
+   * Note: this can happen if the plugin has an undefined symbol reference,
+   * so print a warning. Otherwise, the poor slob won't know what happened.
+   * Ask me how I know that...
+   */
+  if (handle == 0)
+    {
+      clib_warning ("%s", dlerror ());
+      return -1;
+    }
+
+  pi->handle = handle;
+
+  register_handle = dlsym (pi->handle, "vat2_register_plugin");
+  if (register_handle == 0)
+    {
+      clib_warning ("%s: symbol vat2_register_plugin not found", pi->name);
+      dlclose (handle);
+      return -1;
+    }
+
+  fp = register_handle;
+
+  error = (*fp) ();
+
+  if (error)
+    {
+      clib_error_report (error);
+      dlclose (handle);
+      return -1;
+    }
+
+  return 0;
+}
+
+static u8 **
+split_plugin_path (char *plugin_path)
+{
+  int i;
+  u8 **rv = 0;
+  u8 *path = (u8 *) plugin_path;
+  u8 *this = 0;
+
+  for (i = 0; i < vec_len (plugin_path); i++)
+    {
+      if (path[i] != ':')
+       {
+         vec_add1 (this, path[i]);
+         continue;
+       }
+      vec_add1 (this, 0);
+      vec_add1 (rv, this);
+      this = 0;
+    }
+  if (this)
+    {
+      vec_add1 (this, 0);
+      vec_add1 (rv, this);
+    }
+  return rv;
+}
+
+int
+vat2_load_plugins (char *path, char *filter, int *loaded)
+{
+  DIR *dp;
+  struct dirent *entry;
+  struct stat statb;
+  uword *p;
+  plugin_info_t *pi;
+  u8 **plugin_path;
+  int i;
+  int res = 0;
+  uword *plugin_by_name_hash = hash_create_string (0, sizeof (uword));
+
+  *loaded = 0;
+  plugin_path = split_plugin_path (path);
+
+  for (i = 0; i < vec_len (plugin_path); i++)
+    {
+      DBG ("Opening path: %s\n", plugin_path[i]);
+      dp = opendir ((char *) plugin_path[i]);
+
+      if (dp == 0)
+       continue;
+
+      while ((entry = readdir (dp)))
+       {
+         u8 *plugin_name;
+
+         if (filter)
+           {
+             int j;
+             for (j = 0; j < vec_len (filter); j++)
+               if (entry->d_name[j] != filter[j])
+                 goto next;
+           }
+
+         plugin_name = format (0, "%s/%s%c", plugin_path[i],
+                               entry->d_name, 0);
+
+         /* unreadable */
+         if (stat ((char *) plugin_name, &statb) < 0)
+           {
+           ignore:
+             vec_free (plugin_name);
+             continue;
+           }
+
+         /* a dir or other things which aren't plugins */
+         if (!S_ISREG (statb.st_mode))
+           goto ignore;
+
+         p = hash_get_mem (plugin_by_name_hash, plugin_name);
+         if (p == 0)
+           {
+             vec_add2 (plugin_info, pi, 1);
+             pi->name = plugin_name;
+             pi->file_info = statb;
+
+             if (load_one_plugin (pi))
+               {
+                 res = -1;
+                 vec_free (plugin_name);
+                 _vec_len (plugin_info) = vec_len (plugin_info) - 1;
+                 continue;
+               }
+             clib_memset (pi, 0, sizeof (*pi));
+             hash_set_mem (plugin_by_name_hash, plugin_name,
+                           pi - plugin_info);
+             *loaded = *loaded + 1;
+           }
+       next:
+         ;
+       }
+      closedir (dp);
+      vec_free (plugin_path[i]);
+    }
+  vec_free (plugin_path);
+  return res;
+}
+
+#define QUOTE_(x) #x
+#define QUOTE(x) QUOTE_(x)
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vat2/vat2.h b/src/vat2/vat2.h
new file mode 100644 (file)
index 0000000..d477b72
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef included_vat2_h
+#define included_vat2_h
+
+#include <stdbool.h>
+
+extern bool debug;
+
+#define DBG(fmt, args...) do {if (debug) fprintf(stderr, fmt, ## args); } while(0)
+#define ERR(fmt, args...) fprintf(stderr, "VAT2: %s:%d:%s(): " fmt, \
+                                  __FILE__, __LINE__, __func__, ##args)
+
+#endif
diff --git a/src/vat2/vat2_helpers.h b/src/vat2/vat2_helpers.h
new file mode 100644 (file)
index 0000000..929c012
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_vat2_helpers_h
+#define included_vat2_helpers_h
+
+/* For control ping */
+#define vl_endianfun
+#include <vpp/api/vpe.api.h>
+#undef vl_endianfun
+
+static inline void
+vat2_control_ping (u32 context)
+{
+    vl_api_control_ping_t mp = {0};
+    mp._vl_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_CRC);
+    mp.context = context;
+    vl_api_control_ping_t_endian(&mp);
+    vac_write((char *)&mp, sizeof(mp));
+}
+
+#endif
index 9f4dbb0..e387d25 100644 (file)
@@ -864,7 +864,6 @@ list(APPEND VNET_HEADERS
 
 list(APPEND VNET_API_FILES
   srmpls/sr_mpls.api
-  srv6/sr_types.api
 )
 
 ##############################################################################
@@ -1501,3 +1500,10 @@ add_vpp_library (vatclient
 )
 
 ##############################################################################
+# VAT2 plugins
+##############################################################################
+add_vpp_test_library(vnet
+  ${VNET_API_FILES}
+)
+
+##############################################################################
index 7ac9de7..8b18c1e 100644 (file)
@@ -124,7 +124,7 @@ autoreply define ip_table_flush
     @param context - sender context
     @param table - description of the table
 */
-manual_endian manual_print define ip_table_details
+define ip_table_details
 {
   u32 context;
   vl_api_ip_table_t table;
@@ -184,7 +184,7 @@ define ip_route_dump
 /** \brief IP FIB table entry response
     @param route The route entry in the table
 */
-manual_endian manual_print define ip_route_details
+define ip_route_details
 {
   u32 context;
   vl_api_ip_route_t route;
@@ -339,7 +339,7 @@ define ip_mroute_dump
 /** \brief IP Multicast Route Details
     @param route - Details of the route
 */
-manual_endian manual_print define ip_mroute_details
+define ip_mroute_details
 {
   u32 context;
   vl_api_ip_mroute_t route;
index b6bd4b5..416874d 100644 (file)
@@ -315,7 +315,7 @@ typedef bridge_domain_sw_if
     @param bd_tag - optional textual tag for the bridge domain
     @param n_sw_ifs - number of sw_if_index's in the domain
 */
-manual_print manual_endian define bridge_domain_details
+define bridge_domain_details
 {
   u32 context;
   u32 bd_id;
index c8690c6..746c7fd 100644 (file)
@@ -42,9 +42,6 @@
 #include <vnet/vnet_all_api_h.h>
 #undef vl_endianfun
 
-#define vl_api_bridge_domain_details_t_endian vl_noop_handler
-#define vl_api_bridge_domain_details_t_print vl_noop_handler
-
 /* instantiate all the print functions we know about */
 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
 #define vl_printfun
index fb3ab1a..9d4ec0b 100644 (file)
@@ -86,7 +86,7 @@ define mpls_tunnel_dump
 
 /** \brief mpls tunnel details
 */
-manual_endian manual_print define mpls_tunnel_details
+define mpls_tunnel_details
 {
   u32 context;
   vl_api_mpls_tunnel_t mt_tunnel;
@@ -192,7 +192,7 @@ define mpls_route_dump
     @param count - the number of fib_path in path
     @param path  - array of of fib_path structures
 */
-manual_endian manual_print define mpls_route_details
+define mpls_route_details
 {
   u32 context;
   vl_api_mpls_route_t mr_route;
index 2127a62..20adc95 100644 (file)
@@ -402,7 +402,7 @@ vac_disconnect (void)
 
   vl_client_disconnect();
   vl_client_api_unmap();
-  vac_callback = 0;
+  //vac_callback = 0;
 
   cleanup();
 
@@ -534,9 +534,9 @@ vac_write (char *p, int l)
 }
 
 int
-vac_get_msg_index (unsigned char * name)
+vac_get_msg_index (char * name)
 {
-  return vl_msg_api_get_msg_index (name);
+  return vl_msg_api_get_msg_index ((u8 *)name);
 }
 
 int
index 680003e..3ccd84f 100644 (file)
@@ -27,7 +27,7 @@ int vac_read(char **data, int *l, unsigned short timeout);
 int vac_write(char *data, int len);
 void vac_free(void * msg);
 
-int vac_get_msg_index(unsigned char * name);
+int vac_get_msg_index(char * name);
 int vac_msg_table_size(void);
 int vac_msg_table_max_index(void);
 
index 0a8ec31..df83837 100644 (file)
@@ -134,3 +134,10 @@ add_vpp_library(vppmem_preload
 
 install(FILES conf/startup.conf DESTINATION etc/vpp COMPONENT vpp)
 install(FILES conf/80-vpp.conf DESTINATION etc/sysctl.d COMPONENT vpp)
+
+##############################################################################
+# VAT2 plugins
+##############################################################################
+add_vpp_test_library(vpp
+  ${VPP_API_FILES}
+)
index a670181..a972d90 100644 (file)
@@ -38,6 +38,11 @@ install(
 
 add_definitions(-fvisibility=hidden)
 
+# Ensure symbols from cJSON are exported
+set_source_files_properties( cJSON.c PROPERTIES
+  COMPILE_DEFINITIONS " CJSON_API_VISIBILITY " )
+
+
 ##############################################################################
 # vppinfra sources
 ##############################################################################
@@ -88,6 +93,7 @@ set(VPPINFRA_SRCS
   valloc.c
   vec.c
   vector.c
+  cJSON.c
 )
 
 set(VPPINFRA_HEADERS
@@ -108,6 +114,7 @@ set(VPPINFRA_HEADERS
   cache.h
   callback.h
   callback_data.h
+  cJSON.h
   clib_error.h
   clib.h
   cpu.h
diff --git a/src/vppinfra/cJSON.c b/src/vppinfra/cJSON.c
new file mode 100644 (file)
index 0000000..4c6a308
--- /dev/null
@@ -0,0 +1,3095 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#define NAN 0.0/0.0
+#endif
+
+typedef struct {
+    const unsigned char *json;
+    size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+    return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) 
+{
+    if (!cJSON_IsString(item)) 
+    {
+        return NULL;
+    }
+
+    return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) 
+{
+    if (!cJSON_IsNumber(item)) 
+    {
+        return (double) NAN;
+    }
+
+    return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14)
+    #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+    static char version[15];
+    sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+    return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+    if ((string1 == NULL) || (string2 == NULL))
+    {
+        return 1;
+    }
+
+    if (string1 == string2)
+    {
+        return 0;
+    }
+
+    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+    {
+        if (*string1 == '\0')
+        {
+            return 0;
+        }
+    }
+
+    return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+    void *(CJSON_CDECL *allocate)(size_t size);
+    void (CJSON_CDECL *deallocate)(void *pointer);
+    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+    return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+    free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+    return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+    size_t length = 0;
+    unsigned char *copy = NULL;
+
+    if (string == NULL)
+    {
+        return NULL;
+    }
+
+    length = strlen((const char*)string) + sizeof("");
+    copy = (unsigned char*)hooks->allocate(length);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    memcpy(copy, string, length);
+
+    return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+    if (hooks == NULL)
+    {
+        /* Reset hooks */
+        global_hooks.allocate = malloc;
+        global_hooks.deallocate = free;
+        global_hooks.reallocate = realloc;
+        return;
+    }
+
+    global_hooks.allocate = malloc;
+    if (hooks->malloc_fn != NULL)
+    {
+        global_hooks.allocate = hooks->malloc_fn;
+    }
+
+    global_hooks.deallocate = free;
+    if (hooks->free_fn != NULL)
+    {
+        global_hooks.deallocate = hooks->free_fn;
+    }
+
+    /* use realloc only if both free and malloc are used */
+    global_hooks.reallocate = NULL;
+    if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+    {
+        global_hooks.reallocate = realloc;
+    }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+    if (node)
+    {
+        memset(node, '\0', sizeof(cJSON));
+    }
+
+    return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+    cJSON *next = NULL;
+    while (item != NULL)
+    {
+        next = item->next;
+        if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+        {
+            cJSON_Delete(item->child);
+        }
+        if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+        {
+            global_hooks.deallocate(item->valuestring);
+        }
+        if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+        {
+            global_hooks.deallocate(item->string);
+        }
+        global_hooks.deallocate(item);
+        item = next;
+    }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+    struct lconv *lconv = localeconv();
+    return (unsigned char) lconv->decimal_point[0];
+#else
+    return '.';
+#endif
+}
+
+typedef struct
+{
+    const unsigned char *content;
+    size_t length;
+    size_t offset;
+    size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+    internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+    double number = 0;
+    unsigned char *after_end = NULL;
+    unsigned char number_c_string[64];
+    unsigned char decimal_point = get_decimal_point();
+    size_t i = 0;
+
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false;
+    }
+
+    /* copy the number into a temporary buffer and replace '.' with the decimal point
+     * of the current locale (for strtod)
+     * This also takes care of '\0' not necessarily being available for marking the end of the input */
+    for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+    {
+        switch (buffer_at_offset(input_buffer)[i])
+        {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case '+':
+            case '-':
+            case 'e':
+            case 'E':
+                number_c_string[i] = buffer_at_offset(input_buffer)[i];
+                break;
+
+            case '.':
+                number_c_string[i] = decimal_point;
+                break;
+
+            default:
+                goto loop_end;
+        }
+    }
+loop_end:
+    number_c_string[i] = '\0';
+
+    number = strtod((const char*)number_c_string, (char**)&after_end);
+    if (number_c_string == after_end)
+    {
+        return false; /* parse_error */
+    }
+
+    item->valuedouble = number;
+
+    /* use saturation in case of overflow */
+    if (number >= INT_MAX)
+    {
+        item->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        item->valueint = INT_MIN;
+    }
+    else
+    {
+        item->valueint = (int)number;
+    }
+
+    item->type = cJSON_Number;
+
+    input_buffer->offset += (size_t)(after_end - number_c_string);
+    return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+    if (number >= INT_MAX)
+    {
+        object->valueint = INT_MAX;
+    }
+    else if (number <= (double)INT_MIN)
+    {
+        object->valueint = INT_MIN;
+    }
+    else
+    {
+        object->valueint = (int)number;
+    }
+
+    return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+    char *copy = NULL;
+    /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+    if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+    {
+        return NULL;
+    }
+    if (strlen(valuestring) <= strlen(object->valuestring))
+    {
+        strcpy(object->valuestring, valuestring);
+        return object->valuestring;
+    }
+    copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+    if (copy == NULL)
+    {
+        return NULL;
+    }
+    if (object->valuestring != NULL)
+    {
+        cJSON_free(object->valuestring);
+    }
+    object->valuestring = copy;
+
+    return copy;
+}
+
+typedef struct
+{
+    unsigned char *buffer;
+    size_t length;
+    size_t offset;
+    size_t depth; /* current nesting depth (for formatted printing) */
+    cJSON_bool noalloc;
+    cJSON_bool format; /* is this print a formatted print */
+    internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+    unsigned char *newbuffer = NULL;
+    size_t newsize = 0;
+
+    if ((p == NULL) || (p->buffer == NULL))
+    {
+        return NULL;
+    }
+
+    if ((p->length > 0) && (p->offset >= p->length))
+    {
+        /* make sure that offset is valid */
+        return NULL;
+    }
+
+    if (needed > INT_MAX)
+    {
+        /* sizes bigger than INT_MAX are currently not supported */
+        return NULL;
+    }
+
+    needed += p->offset + 1;
+    if (needed <= p->length)
+    {
+        return p->buffer + p->offset;
+    }
+
+    if (p->noalloc) {
+        return NULL;
+    }
+
+    /* calculate new buffer size */
+    if (needed > (INT_MAX / 2))
+    {
+        /* overflow of int, use INT_MAX if possible */
+        if (needed <= INT_MAX)
+        {
+            newsize = INT_MAX;
+        }
+        else
+        {
+            return NULL;
+        }
+    }
+    else
+    {
+        newsize = needed * 2;
+    }
+
+    if (p->hooks.reallocate != NULL)
+    {
+        /* reallocate with realloc if available */
+        newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+        if (newbuffer == NULL)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+    }
+    else
+    {
+        /* otherwise reallocate manually */
+        newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+        if (!newbuffer)
+        {
+            p->hooks.deallocate(p->buffer);
+            p->length = 0;
+            p->buffer = NULL;
+
+            return NULL;
+        }
+        if (newbuffer)
+        {
+            memcpy(newbuffer, p->buffer, p->offset + 1);
+        }
+        p->hooks.deallocate(p->buffer);
+    }
+    p->length = newsize;
+    p->buffer = newbuffer;
+
+    return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+    const unsigned char *buffer_pointer = NULL;
+    if ((buffer == NULL) || (buffer->buffer == NULL))
+    {
+        return;
+    }
+    buffer_pointer = buffer->buffer + buffer->offset;
+
+    buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+    return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    double d = item->valuedouble;
+    int length = 0;
+    size_t i = 0;
+    unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
+    double test = 0.0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* This checks for NaN and Infinity */
+    if (isnan(d) || isinf(d))
+    {
+        length = sprintf((char*)number_buffer, "null");
+    }
+    else
+    {
+        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+        length = sprintf((char*)number_buffer, "%1.15g", d);
+
+        /* Check whether the original double can be recovered */
+        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+        {
+            /* If not, print with 17 decimal places of precision */
+            length = sprintf((char*)number_buffer, "%1.17g", d);
+        }
+    }
+
+    /* sprintf failed or buffer overrun occurred */
+    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+    {
+        return false;
+    }
+
+    /* reserve appropriate space in the output */
+    output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    /* copy the printed number to the output and replace locale
+     * dependent decimal point with '.' */
+    for (i = 0; i < ((size_t)length); i++)
+    {
+        if (number_buffer[i] == decimal_point)
+        {
+            output_pointer[i] = '.';
+            continue;
+        }
+
+        output_pointer[i] = number_buffer[i];
+    }
+    output_pointer[i] = '\0';
+
+    output_buffer->offset += (size_t)length;
+
+    return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+    unsigned int h = 0;
+    size_t i = 0;
+
+    for (i = 0; i < 4; i++)
+    {
+        /* parse digit */
+        if ((input[i] >= '0') && (input[i] <= '9'))
+        {
+            h += (unsigned int) input[i] - '0';
+        }
+        else if ((input[i] >= 'A') && (input[i] <= 'F'))
+        {
+            h += (unsigned int) 10 + input[i] - 'A';
+        }
+        else if ((input[i] >= 'a') && (input[i] <= 'f'))
+        {
+            h += (unsigned int) 10 + input[i] - 'a';
+        }
+        else /* invalid */
+        {
+            return 0;
+        }
+
+        if (i < 3)
+        {
+            /* shift left to make place for the next nibble */
+            h = h << 4;
+        }
+    }
+
+    return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+    long unsigned int codepoint = 0;
+    unsigned int first_code = 0;
+    const unsigned char *first_sequence = input_pointer;
+    unsigned char utf8_length = 0;
+    unsigned char utf8_position = 0;
+    unsigned char sequence_length = 0;
+    unsigned char first_byte_mark = 0;
+
+    if ((input_end - first_sequence) < 6)
+    {
+        /* input ends unexpectedly */
+        goto fail;
+    }
+
+    /* get the first utf16 sequence */
+    first_code = parse_hex4(first_sequence + 2);
+
+    /* check that the code is valid */
+    if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+    {
+        goto fail;
+    }
+
+    /* UTF16 surrogate pair */
+    if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+    {
+        const unsigned char *second_sequence = first_sequence + 6;
+        unsigned int second_code = 0;
+        sequence_length = 12; /* \uXXXX\uXXXX */
+
+        if ((input_end - second_sequence) < 6)
+        {
+            /* input ends unexpectedly */
+            goto fail;
+        }
+
+        if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+        {
+            /* missing second half of the surrogate pair */
+            goto fail;
+        }
+
+        /* get the second utf16 sequence */
+        second_code = parse_hex4(second_sequence + 2);
+        /* check that the code is valid */
+        if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+        {
+            /* invalid second half of the surrogate pair */
+            goto fail;
+        }
+
+
+        /* calculate the unicode codepoint from the surrogate pair */
+        codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+    }
+    else
+    {
+        sequence_length = 6; /* \uXXXX */
+        codepoint = first_code;
+    }
+
+    /* encode as UTF-8
+     * takes at maximum 4 bytes to encode:
+     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+    if (codepoint < 0x80)
+    {
+        /* normal ascii, encoding 0xxxxxxx */
+        utf8_length = 1;
+    }
+    else if (codepoint < 0x800)
+    {
+        /* two bytes, encoding 110xxxxx 10xxxxxx */
+        utf8_length = 2;
+        first_byte_mark = 0xC0; /* 11000000 */
+    }
+    else if (codepoint < 0x10000)
+    {
+        /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 3;
+        first_byte_mark = 0xE0; /* 11100000 */
+    }
+    else if (codepoint <= 0x10FFFF)
+    {
+        /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+        utf8_length = 4;
+        first_byte_mark = 0xF0; /* 11110000 */
+    }
+    else
+    {
+        /* invalid unicode codepoint */
+        goto fail;
+    }
+
+    /* encode as utf8 */
+    for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+    {
+        /* 10xxxxxx */
+        (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+        codepoint >>= 6;
+    }
+    /* encode first byte */
+    if (utf8_length > 1)
+    {
+        (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+    }
+    else
+    {
+        (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+    }
+
+    *output_pointer += utf8_length;
+
+    return sequence_length;
+
+fail:
+    return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+    const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+    const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+    unsigned char *output_pointer = NULL;
+    unsigned char *output = NULL;
+
+    /* not a string */
+    if (buffer_at_offset(input_buffer)[0] != '\"')
+    {
+        goto fail;
+    }
+
+    {
+        /* calculate approximate size of the output (overestimate) */
+        size_t allocation_length = 0;
+        size_t skipped_bytes = 0;
+        while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+        {
+            /* is escape sequence */
+            if (input_end[0] == '\\')
+            {
+                if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+                {
+                    /* prevent buffer overflow when last input character is a backslash */
+                    goto fail;
+                }
+                skipped_bytes++;
+                input_end++;
+            }
+            input_end++;
+        }
+        if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+        {
+            goto fail; /* string ended unexpectedly */
+        }
+
+        /* This is at most how much we need for the output */
+        allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+        output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+        if (output == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+    }
+
+    output_pointer = output;
+    /* loop through the string literal */
+    while (input_pointer < input_end)
+    {
+        if (*input_pointer != '\\')
+        {
+            *output_pointer++ = *input_pointer++;
+        }
+        /* escape sequence */
+        else
+        {
+            unsigned char sequence_length = 2;
+            if ((input_end - input_pointer) < 1)
+            {
+                goto fail;
+            }
+
+            switch (input_pointer[1])
+            {
+                case 'b':
+                    *output_pointer++ = '\b';
+                    break;
+                case 'f':
+                    *output_pointer++ = '\f';
+                    break;
+                case 'n':
+                    *output_pointer++ = '\n';
+                    break;
+                case 'r':
+                    *output_pointer++ = '\r';
+                    break;
+                case 't':
+                    *output_pointer++ = '\t';
+                    break;
+                case '\"':
+                case '\\':
+                case '/':
+                    *output_pointer++ = input_pointer[1];
+                    break;
+
+                /* UTF-16 literal */
+                case 'u':
+                    sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+                    if (sequence_length == 0)
+                    {
+                        /* failed to convert UTF16-literal to UTF-8 */
+                        goto fail;
+                    }
+                    break;
+
+                default:
+                    goto fail;
+            }
+            input_pointer += sequence_length;
+        }
+    }
+
+    /* zero terminate the output */
+    *output_pointer = '\0';
+
+    item->type = cJSON_String;
+    item->valuestring = (char*)output;
+
+    input_buffer->offset = (size_t) (input_end - input_buffer->content);
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (output != NULL)
+    {
+        input_buffer->hooks.deallocate(output);
+    }
+
+    if (input_pointer != NULL)
+    {
+        input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+    }
+
+    return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+    const unsigned char *input_pointer = NULL;
+    unsigned char *output = NULL;
+    unsigned char *output_pointer = NULL;
+    size_t output_length = 0;
+    /* numbers of additional characters needed for escaping */
+    size_t escape_characters = 0;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* empty string */
+    if (input == NULL)
+    {
+        output = ensure(output_buffer, sizeof("\"\""));
+        if (output == NULL)
+        {
+            return false;
+        }
+        strcpy((char*)output, "\"\"");
+
+        return true;
+    }
+
+    /* set "flag" to 1 if something needs to be escaped */
+    for (input_pointer = input; *input_pointer; input_pointer++)
+    {
+        switch (*input_pointer)
+        {
+            case '\"':
+            case '\\':
+            case '\b':
+            case '\f':
+            case '\n':
+            case '\r':
+            case '\t':
+                /* one character escape sequence */
+                escape_characters++;
+                break;
+            default:
+                if (*input_pointer < 32)
+                {
+                    /* UTF-16 escape sequence uXXXX */
+                    escape_characters += 5;
+                }
+                break;
+        }
+    }
+    output_length = (size_t)(input_pointer - input) + escape_characters;
+
+    output = ensure(output_buffer, output_length + sizeof("\"\""));
+    if (output == NULL)
+    {
+        return false;
+    }
+
+    /* no characters have to be escaped */
+    if (escape_characters == 0)
+    {
+        output[0] = '\"';
+        memcpy(output + 1, input, output_length);
+        output[output_length + 1] = '\"';
+        output[output_length + 2] = '\0';
+
+        return true;
+    }
+
+    output[0] = '\"';
+    output_pointer = output + 1;
+    /* copy the string */
+    for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+    {
+        if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+        {
+            /* normal character, copy */
+            *output_pointer = *input_pointer;
+        }
+        else
+        {
+            /* character needs to be escaped */
+            *output_pointer++ = '\\';
+            switch (*input_pointer)
+            {
+                case '\\':
+                    *output_pointer = '\\';
+                    break;
+                case '\"':
+                    *output_pointer = '\"';
+                    break;
+                case '\b':
+                    *output_pointer = 'b';
+                    break;
+                case '\f':
+                    *output_pointer = 'f';
+                    break;
+                case '\n':
+                    *output_pointer = 'n';
+                    break;
+                case '\r':
+                    *output_pointer = 'r';
+                    break;
+                case '\t':
+                    *output_pointer = 't';
+                    break;
+                default:
+                    /* escape and print as unicode codepoint */
+                    sprintf((char*)output_pointer, "u%04x", *input_pointer);
+                    output_pointer += 4;
+                    break;
+            }
+        }
+    }
+    output[output_length + 1] = '\"';
+    output[output_length + 2] = '\0';
+
+    return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+    return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL))
+    {
+        return NULL;
+    }
+
+    if (cannot_access_at_index(buffer, 0))
+    {
+        return buffer;
+    }
+
+    while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+    {
+       buffer->offset++;
+    }
+
+    if (buffer->offset == buffer->length)
+    {
+        buffer->offset--;
+    }
+
+    return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+    if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+    {
+        return NULL;
+    }
+
+    if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+    {
+        buffer->offset += 3;
+    }
+
+    return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    size_t buffer_length;
+
+    if (NULL == value)
+    {
+        return NULL;
+    }
+
+    /* Adding null character size due to require_null_terminated. */
+    buffer_length = strlen(value) + sizeof("");
+
+    return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+    parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+    cJSON *item = NULL;
+
+    /* reset error position */
+    global_error.json = NULL;
+    global_error.position = 0;
+
+    if (value == NULL || 0 == buffer_length)
+    {
+        goto fail;
+    }
+
+    buffer.content = (const unsigned char*)value;
+    buffer.length = buffer_length; 
+    buffer.offset = 0;
+    buffer.hooks = global_hooks;
+
+    item = cJSON_New_Item(&global_hooks);
+    if (item == NULL) /* memory fail */
+    {
+        goto fail;
+    }
+
+    if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+    {
+        /* parse failure. ep is set. */
+        goto fail;
+    }
+
+    /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+    if (require_null_terminated)
+    {
+        buffer_skip_whitespace(&buffer);
+        if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+        {
+            goto fail;
+        }
+    }
+    if (return_parse_end)
+    {
+        *return_parse_end = (const char*)buffer_at_offset(&buffer);
+    }
+
+    return item;
+
+fail:
+    if (item != NULL)
+    {
+        cJSON_Delete(item);
+    }
+
+    if (value != NULL)
+    {
+        error local_error;
+        local_error.json = (const unsigned char*)value;
+        local_error.position = 0;
+
+        if (buffer.offset < buffer.length)
+        {
+            local_error.position = buffer.offset;
+        }
+        else if (buffer.length > 0)
+        {
+            local_error.position = buffer.length - 1;
+        }
+
+        if (return_parse_end != NULL)
+        {
+            *return_parse_end = (const char*)local_error.json + local_error.position;
+        }
+
+        global_error = local_error;
+    }
+
+    return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+    return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+    return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+    static const size_t default_buffer_size = 256;
+    printbuffer buffer[1];
+    unsigned char *printed = NULL;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    /* create buffer */
+    buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+    buffer->length = default_buffer_size;
+    buffer->format = format;
+    buffer->hooks = *hooks;
+    if (buffer->buffer == NULL)
+    {
+        goto fail;
+    }
+
+    /* print the value */
+    if (!print_value(item, buffer))
+    {
+        goto fail;
+    }
+    update_offset(buffer);
+
+    /* check if reallocate is available */
+    if (hooks->reallocate != NULL)
+    {
+        printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+        if (printed == NULL) {
+            goto fail;
+        }
+        buffer->buffer = NULL;
+    }
+    else /* otherwise copy the JSON over to a new buffer */
+    {
+        printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+        if (printed == NULL)
+        {
+            goto fail;
+        }
+        memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+        printed[buffer->offset] = '\0'; /* just to be sure */
+
+        /* free the buffer */
+        hooks->deallocate(buffer->buffer);
+    }
+
+    return printed;
+
+fail:
+    if (buffer->buffer != NULL)
+    {
+        hooks->deallocate(buffer->buffer);
+    }
+
+    if (printed != NULL)
+    {
+        hooks->deallocate(printed);
+    }
+
+    return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+    return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+    return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if (prebuffer < 0)
+    {
+        return NULL;
+    }
+
+    p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+    if (!p.buffer)
+    {
+        return NULL;
+    }
+
+    p.length = (size_t)prebuffer;
+    p.offset = 0;
+    p.noalloc = false;
+    p.format = fmt;
+    p.hooks = global_hooks;
+
+    if (!print_value(item, &p))
+    {
+        global_hooks.deallocate(p.buffer);
+        return NULL;
+    }
+
+    return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+    printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+    if ((length < 0) || (buffer == NULL))
+    {
+        return false;
+    }
+
+    p.buffer = (unsigned char*)buffer;
+    p.length = (size_t)length;
+    p.offset = 0;
+    p.noalloc = true;
+    p.format = format;
+    p.hooks = global_hooks;
+
+    return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+    if ((input_buffer == NULL) || (input_buffer->content == NULL))
+    {
+        return false; /* no input */
+    }
+
+    /* parse the different types of values */
+    /* null */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+    {
+        item->type = cJSON_NULL;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* false */
+    if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+    {
+        item->type = cJSON_False;
+        input_buffer->offset += 5;
+        return true;
+    }
+    /* true */
+    if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+    {
+        item->type = cJSON_True;
+        item->valueint = 1;
+        input_buffer->offset += 4;
+        return true;
+    }
+    /* string */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+    {
+        return parse_string(item, input_buffer);
+    }
+    /* number */
+    if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+    {
+        return parse_number(item, input_buffer);
+    }
+    /* array */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+    {
+        return parse_array(item, input_buffer);
+    }
+    /* object */
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+    {
+        return parse_object(item, input_buffer);
+    }
+
+    return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output = NULL;
+
+    if ((item == NULL) || (output_buffer == NULL))
+    {
+        return false;
+    }
+
+    switch ((item->type) & 0xFF)
+    {
+        case cJSON_NULL:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "null");
+            return true;
+
+        case cJSON_False:
+            output = ensure(output_buffer, 6);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "false");
+            return true;
+
+        case cJSON_True:
+            output = ensure(output_buffer, 5);
+            if (output == NULL)
+            {
+                return false;
+            }
+            strcpy((char*)output, "true");
+            return true;
+
+        case cJSON_Number:
+            return print_number(item, output_buffer);
+
+        case cJSON_Raw:
+        {
+            size_t raw_length = 0;
+            if (item->valuestring == NULL)
+            {
+                return false;
+            }
+
+            raw_length = strlen(item->valuestring) + sizeof("");
+            output = ensure(output_buffer, raw_length);
+            if (output == NULL)
+            {
+                return false;
+            }
+            memcpy(output, item->valuestring, raw_length);
+            return true;
+        }
+
+        case cJSON_String:
+            return print_string(item, output_buffer);
+
+        case cJSON_Array:
+            return print_array(item, output_buffer);
+
+        case cJSON_Object:
+            return print_object(item, output_buffer);
+
+        default:
+            return false;
+    }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* head of the linked list */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (buffer_at_offset(input_buffer)[0] != '[')
+    {
+        /* not an array */
+        goto fail;
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+    {
+        /* empty array */
+        goto success;
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse next value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+    {
+        goto fail; /* expected end of array */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if (head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Array;
+    item->child = head;
+
+    input_buffer->offset++;
+
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_element = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output array. */
+    /* opening square bracket */
+    output_pointer = ensure(output_buffer, 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer = '[';
+    output_buffer->offset++;
+    output_buffer->depth++;
+
+    while (current_element != NULL)
+    {
+        if (!print_value(current_element, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+        if (current_element->next)
+        {
+            length = (size_t) (output_buffer->format ? 2 : 1);
+            output_pointer = ensure(output_buffer, length + 1);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            *output_pointer++ = ',';
+            if(output_buffer->format)
+            {
+                *output_pointer++ = ' ';
+            }
+            *output_pointer = '\0';
+            output_buffer->offset += length;
+        }
+        current_element = current_element->next;
+    }
+
+    output_pointer = ensure(output_buffer, 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    *output_pointer++ = ']';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+    cJSON *head = NULL; /* linked list head */
+    cJSON *current_item = NULL;
+
+    if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+    {
+        return false; /* to deeply nested */
+    }
+    input_buffer->depth++;
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+    {
+        goto fail; /* not an object */
+    }
+
+    input_buffer->offset++;
+    buffer_skip_whitespace(input_buffer);
+    if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+    {
+        goto success; /* empty object */
+    }
+
+    /* check if we skipped to the end of the buffer */
+    if (cannot_access_at_index(input_buffer, 0))
+    {
+        input_buffer->offset--;
+        goto fail;
+    }
+
+    /* step back to character in front of the first element */
+    input_buffer->offset--;
+    /* loop through the comma separated array elements */
+    do
+    {
+        /* allocate next item */
+        cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+        if (new_item == NULL)
+        {
+            goto fail; /* allocation failure */
+        }
+
+        /* attach next item to list */
+        if (head == NULL)
+        {
+            /* start the linked list */
+            current_item = head = new_item;
+        }
+        else
+        {
+            /* add to the end and advance */
+            current_item->next = new_item;
+            new_item->prev = current_item;
+            current_item = new_item;
+        }
+
+        /* parse the name of the child */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_string(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse name */
+        }
+        buffer_skip_whitespace(input_buffer);
+
+        /* swap valuestring and string, because we parsed the name */
+        current_item->string = current_item->valuestring;
+        current_item->valuestring = NULL;
+
+        if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+        {
+            goto fail; /* invalid object */
+        }
+
+        /* parse the value */
+        input_buffer->offset++;
+        buffer_skip_whitespace(input_buffer);
+        if (!parse_value(current_item, input_buffer))
+        {
+            goto fail; /* failed to parse value */
+        }
+        buffer_skip_whitespace(input_buffer);
+    }
+    while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+    if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+    {
+        goto fail; /* expected end of object */
+    }
+
+success:
+    input_buffer->depth--;
+
+    if (head != NULL) {
+        head->prev = current_item;
+    }
+
+    item->type = cJSON_Object;
+    item->child = head;
+
+    input_buffer->offset++;
+    return true;
+
+fail:
+    if (head != NULL)
+    {
+        cJSON_Delete(head);
+    }
+
+    return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+    unsigned char *output_pointer = NULL;
+    size_t length = 0;
+    cJSON *current_item = item->child;
+
+    if (output_buffer == NULL)
+    {
+        return false;
+    }
+
+    /* Compose the output: */
+    length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+    output_pointer = ensure(output_buffer, length + 1);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+
+    *output_pointer++ = '{';
+    output_buffer->depth++;
+    if (output_buffer->format)
+    {
+        *output_pointer++ = '\n';
+    }
+    output_buffer->offset += length;
+
+    while (current_item)
+    {
+        if (output_buffer->format)
+        {
+            size_t i;
+            output_pointer = ensure(output_buffer, output_buffer->depth);
+            if (output_pointer == NULL)
+            {
+                return false;
+            }
+            for (i = 0; i < output_buffer->depth; i++)
+            {
+                *output_pointer++ = '\t';
+            }
+            output_buffer->offset += output_buffer->depth;
+        }
+
+        /* print key */
+        if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        length = (size_t) (output_buffer->format ? 2 : 1);
+        output_pointer = ensure(output_buffer, length);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        *output_pointer++ = ':';
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\t';
+        }
+        output_buffer->offset += length;
+
+        /* print value */
+        if (!print_value(current_item, output_buffer))
+        {
+            return false;
+        }
+        update_offset(output_buffer);
+
+        /* print comma if not last */
+        length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+        output_pointer = ensure(output_buffer, length + 1);
+        if (output_pointer == NULL)
+        {
+            return false;
+        }
+        if (current_item->next)
+        {
+            *output_pointer++ = ',';
+        }
+
+        if (output_buffer->format)
+        {
+            *output_pointer++ = '\n';
+        }
+        *output_pointer = '\0';
+        output_buffer->offset += length;
+
+        current_item = current_item->next;
+    }
+
+    output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+    if (output_pointer == NULL)
+    {
+        return false;
+    }
+    if (output_buffer->format)
+    {
+        size_t i;
+        for (i = 0; i < (output_buffer->depth - 1); i++)
+        {
+            *output_pointer++ = '\t';
+        }
+    }
+    *output_pointer++ = '}';
+    *output_pointer = '\0';
+    output_buffer->depth--;
+
+    return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+    cJSON *child = NULL;
+    size_t size = 0;
+
+    if (array == NULL)
+    {
+        return 0;
+    }
+
+    child = array->child;
+
+    while(child != NULL)
+    {
+        size++;
+        child = child->next;
+    }
+
+    /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+    return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+    cJSON *current_child = NULL;
+
+    if (array == NULL)
+    {
+        return NULL;
+    }
+
+    current_child = array->child;
+    while ((current_child != NULL) && (index > 0))
+    {
+        index--;
+        current_child = current_child->next;
+    }
+
+    return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+    if (index < 0)
+    {
+        return NULL;
+    }
+
+    return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+    cJSON *current_element = NULL;
+
+    if ((object == NULL) || (name == NULL))
+    {
+        return NULL;
+    }
+
+    current_element = object->child;
+    if (case_sensitive)
+    {
+        while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+    else
+    {
+        while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+        {
+            current_element = current_element->next;
+        }
+    }
+
+    if ((current_element == NULL) || (current_element->string == NULL)) {
+        return NULL;
+    }
+
+    return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+    return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+    return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+    prev->next = item;
+    item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+    cJSON *reference = NULL;
+    if (item == NULL)
+    {
+        return NULL;
+    }
+
+    reference = cJSON_New_Item(hooks);
+    if (reference == NULL)
+    {
+        return NULL;
+    }
+
+    memcpy(reference, item, sizeof(cJSON));
+    reference->string = NULL;
+    reference->type |= cJSON_IsReference;
+    reference->next = reference->prev = NULL;
+    return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+    cJSON *child = NULL;
+
+    if ((item == NULL) || (array == NULL) || (array == item))
+    {
+        return false;
+    }
+
+    child = array->child;
+    /*
+     * To find the last item in array quickly, we use prev in array
+     */
+    if (child == NULL)
+    {
+        /* list is empty, start new one */
+        array->child = item;
+        item->prev = item;
+        item->next = NULL;
+    }
+    else
+    {
+        /* append to the end */
+        if (child->prev)
+        {
+            suffix_object(child->prev, item);
+            array->child->prev = item;
+        }
+    }
+
+    return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+    return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+    return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+    #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+    char *new_key = NULL;
+    int new_type = cJSON_Invalid;
+
+    if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+    {
+        return false;
+    }
+
+    if (constant_key)
+    {
+        new_key = (char*)cast_away_const(string);
+        new_type = item->type | cJSON_StringIsConst;
+    }
+    else
+    {
+        new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+        if (new_key == NULL)
+        {
+            return false;
+        }
+
+        new_type = item->type & ~cJSON_StringIsConst;
+    }
+
+    if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+    {
+        hooks->deallocate(item->string);
+    }
+
+    item->string = new_key;
+    item->type = new_type;
+
+    return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+    return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+    return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+    if (array == NULL)
+    {
+        return false;
+    }
+
+    return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+    if ((object == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+    cJSON *null = cJSON_CreateNull();
+    if (add_item_to_object(object, name, null, &global_hooks, false))
+    {
+        return null;
+    }
+
+    cJSON_Delete(null);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+    cJSON *true_item = cJSON_CreateTrue();
+    if (add_item_to_object(object, name, true_item, &global_hooks, false))
+    {
+        return true_item;
+    }
+
+    cJSON_Delete(true_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+    cJSON *false_item = cJSON_CreateFalse();
+    if (add_item_to_object(object, name, false_item, &global_hooks, false))
+    {
+        return false_item;
+    }
+
+    cJSON_Delete(false_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+    cJSON *bool_item = cJSON_CreateBool(boolean);
+    if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+    {
+        return bool_item;
+    }
+
+    cJSON_Delete(bool_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+    cJSON *number_item = cJSON_CreateNumber(number);
+    if (add_item_to_object(object, name, number_item, &global_hooks, false))
+    {
+        return number_item;
+    }
+
+    cJSON_Delete(number_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+    cJSON *string_item = cJSON_CreateString(string);
+    if (add_item_to_object(object, name, string_item, &global_hooks, false))
+    {
+        return string_item;
+    }
+
+    cJSON_Delete(string_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+    cJSON *raw_item = cJSON_CreateRaw(raw);
+    if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+    {
+        return raw_item;
+    }
+
+    cJSON_Delete(raw_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+    cJSON *object_item = cJSON_CreateObject();
+    if (add_item_to_object(object, name, object_item, &global_hooks, false))
+    {
+        return object_item;
+    }
+
+    cJSON_Delete(object_item);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+    cJSON *array = cJSON_CreateArray();
+    if (add_item_to_object(object, name, array, &global_hooks, false))
+    {
+        return array;
+    }
+
+    cJSON_Delete(array);
+    return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+    if ((parent == NULL) || (item == NULL))
+    {
+        return NULL;
+    }
+
+    if (item != parent->child)
+    {
+        /* not the first element */
+        item->prev->next = item->next;
+    }
+    if (item->next != NULL)
+    {
+        /* not the last element */
+        item->next->prev = item->prev;
+    }
+
+    if (item == parent->child)
+    {
+        /* first element */
+        parent->child = item->next;
+    }
+    else if (item->next == NULL)
+    {
+        /* last element */
+        parent->child->prev = item->prev;
+    }
+
+    /* make sure the detached item doesn't point anywhere anymore */
+    item->prev = NULL;
+    item->next = NULL;
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+    if (which < 0)
+    {
+        return NULL;
+    }
+
+    return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+    cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+    return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+    cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    cJSON *after_inserted = NULL;
+
+    if (which < 0)
+    {
+        return false;
+    }
+
+    after_inserted = get_array_item(array, (size_t)which);
+    if (after_inserted == NULL)
+    {
+        return add_item_to_array(array, newitem);
+    }
+
+    newitem->next = after_inserted;
+    newitem->prev = after_inserted->prev;
+    after_inserted->prev = newitem;
+    if (after_inserted == array->child)
+    {
+        array->child = newitem;
+    }
+    else
+    {
+        newitem->prev->next = newitem;
+    }
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+    if ((parent == NULL) || (replacement == NULL) || (item == NULL))
+    {
+        return false;
+    }
+
+    if (replacement == item)
+    {
+        return true;
+    }
+
+    replacement->next = item->next;
+    replacement->prev = item->prev;
+
+    if (replacement->next != NULL)
+    {
+        replacement->next->prev = replacement;
+    }
+    if (parent->child == item)
+    {
+        if (parent->child->prev == parent->child)
+        {
+            replacement->prev = replacement;
+        }
+        parent->child = replacement;
+    }
+    else
+    {   /*
+         * To find the last item in array quickly, we use prev in array.
+         * We can't modify the last item's next pointer where this item was the parent's child
+         */
+        if (replacement->prev != NULL)
+        {
+            replacement->prev->next = replacement;
+        }
+        if (replacement->next == NULL)
+        {
+            parent->child->prev = replacement;
+        }
+    }
+
+    item->next = NULL;
+    item->prev = NULL;
+    cJSON_Delete(item);
+
+    return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+    if (which < 0)
+    {
+        return false;
+    }
+
+    return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+    if ((replacement == NULL) || (string == NULL))
+    {
+        return false;
+    }
+
+    /* replace the name in the replacement */
+    if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+    {
+        cJSON_free(replacement->string);
+    }
+    replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+    replacement->type &= ~cJSON_StringIsConst;
+
+    return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+    return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+    return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_NULL;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_True;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = boolean ? cJSON_True : cJSON_False;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_Number;
+        item->valuedouble = num;
+
+        /* use saturation in case of overflow */
+        if (num >= INT_MAX)
+        {
+            item->valueint = INT_MAX;
+        }
+        else if (num <= (double)INT_MIN)
+        {
+            item->valueint = INT_MIN;
+        }
+        else
+        {
+            item->valueint = (int)num;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_String;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+        if(!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL)
+    {
+        item->type = cJSON_String | cJSON_IsReference;
+        item->valuestring = (char*)cast_away_const(string);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Object | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item != NULL) {
+        item->type = cJSON_Array | cJSON_IsReference;
+        item->child = (cJSON*)cast_away_const(child);
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type = cJSON_Raw;
+        item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+        if(!item->valuestring)
+        {
+            cJSON_Delete(item);
+            return NULL;
+        }
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if(item)
+    {
+        item->type=cJSON_Array;
+    }
+
+    return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+    cJSON *item = cJSON_New_Item(&global_hooks);
+    if (item)
+    {
+        item->type = cJSON_Object;
+    }
+
+    return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if (!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+    a->child->prev = n;
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber((double)numbers[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+    a->child->prev = n;
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (numbers == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for(i = 0;a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateNumber(numbers[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p, n);
+        }
+        p = n;
+    }
+    a->child->prev = n;
+
+    return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+    size_t i = 0;
+    cJSON *n = NULL;
+    cJSON *p = NULL;
+    cJSON *a = NULL;
+
+    if ((count < 0) || (strings == NULL))
+    {
+        return NULL;
+    }
+
+    a = cJSON_CreateArray();
+
+    for (i = 0; a && (i < (size_t)count); i++)
+    {
+        n = cJSON_CreateString(strings[i]);
+        if(!n)
+        {
+            cJSON_Delete(a);
+            return NULL;
+        }
+        if(!i)
+        {
+            a->child = n;
+        }
+        else
+        {
+            suffix_object(p,n);
+        }
+        p = n;
+    }
+    a->child->prev = n;
+
+    return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+    cJSON *newitem = NULL;
+    cJSON *child = NULL;
+    cJSON *next = NULL;
+    cJSON *newchild = NULL;
+
+    /* Bail on bad ptr */
+    if (!item)
+    {
+        goto fail;
+    }
+    /* Create new item */
+    newitem = cJSON_New_Item(&global_hooks);
+    if (!newitem)
+    {
+        goto fail;
+    }
+    /* Copy over all vars */
+    newitem->type = item->type & (~cJSON_IsReference);
+    newitem->valueint = item->valueint;
+    newitem->valuedouble = item->valuedouble;
+    if (item->valuestring)
+    {
+        newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+        if (!newitem->valuestring)
+        {
+            goto fail;
+        }
+    }
+    if (item->string)
+    {
+        newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+        if (!newitem->string)
+        {
+            goto fail;
+        }
+    }
+    /* If non-recursive, then we're done! */
+    if (!recurse)
+    {
+        return newitem;
+    }
+    /* Walk the ->next chain for the child. */
+    child = item->child;
+    while (child != NULL)
+    {
+        newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+        if (!newchild)
+        {
+            goto fail;
+        }
+        if (next != NULL)
+        {
+            /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+            next->next = newchild;
+            newchild->prev = next;
+            next = newchild;
+        }
+        else
+        {
+            /* Set newitem->child and move to it */
+            newitem->child = newchild;
+            next = newchild;
+        }
+        child = child->next;
+    }
+    if (newitem && newitem->child)
+    {
+        newitem->child->prev = newchild;
+    }
+
+    return newitem;
+
+fail:
+    if (newitem != NULL)
+    {
+        cJSON_Delete(newitem);
+    }
+
+    return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+    *input += static_strlen("//");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if ((*input)[0] == '\n') {
+            *input += static_strlen("\n");
+            return;
+        }
+    }
+}
+
+static void skip_multiline_comment(char **input)
+{
+    *input += static_strlen("/*");
+
+    for (; (*input)[0] != '\0'; ++(*input))
+    {
+        if (((*input)[0] == '*') && ((*input)[1] == '/'))
+        {
+            *input += static_strlen("*/");
+            return;
+        }
+    }
+}
+
+static void minify_string(char **input, char **output) {
+    (*output)[0] = (*input)[0];
+    *input += static_strlen("\"");
+    *output += static_strlen("\"");
+
+
+    for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+        (*output)[0] = (*input)[0];
+
+        if ((*input)[0] == '\"') {
+            (*output)[0] = '\"';
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+            return;
+        } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+            (*output)[1] = (*input)[1];
+            *input += static_strlen("\"");
+            *output += static_strlen("\"");
+        }
+    }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+    char *into = json;
+
+    if (json == NULL)
+    {
+        return;
+    }
+
+    while (json[0] != '\0')
+    {
+        switch (json[0])
+        {
+            case ' ':
+            case '\t':
+            case '\r':
+            case '\n':
+                json++;
+                break;
+
+            case '/':
+                if (json[1] == '/')
+                {
+                    skip_oneline_comment(&json);
+                }
+                else if (json[1] == '*')
+                {
+                    skip_multiline_comment(&json);
+                } else {
+                    json++;
+                }
+                break;
+
+            case '\"':
+                minify_string(&json, (char**)&into);
+                break;
+
+            default:
+                into[0] = json[0];
+                json++;
+                into++;
+        }
+    }
+
+    /* and null-terminate. */
+    *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+    if (item == NULL)
+    {
+        return false;
+    }
+
+    return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
+    {
+        return false;
+    }
+
+    /* check if type is valid */
+    switch (a->type & 0xFF)
+    {
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+        case cJSON_Number:
+        case cJSON_String:
+        case cJSON_Raw:
+        case cJSON_Array:
+        case cJSON_Object:
+            break;
+
+        default:
+            return false;
+    }
+
+    /* identical objects are equal */
+    if (a == b)
+    {
+        return true;
+    }
+
+    switch (a->type & 0xFF)
+    {
+        /* in these cases and equal type is enough */
+        case cJSON_False:
+        case cJSON_True:
+        case cJSON_NULL:
+            return true;
+
+        case cJSON_Number:
+            if (compare_double(a->valuedouble, b->valuedouble))
+            {
+                return true;
+            }
+            return false;
+
+        case cJSON_String:
+        case cJSON_Raw:
+            if ((a->valuestring == NULL) || (b->valuestring == NULL))
+            {
+                return false;
+            }
+            if (strcmp(a->valuestring, b->valuestring) == 0)
+            {
+                return true;
+            }
+
+            return false;
+
+        case cJSON_Array:
+        {
+            cJSON *a_element = a->child;
+            cJSON *b_element = b->child;
+
+            for (; (a_element != NULL) && (b_element != NULL);)
+            {
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+
+                a_element = a_element->next;
+                b_element = b_element->next;
+            }
+
+            /* one of the arrays is longer than the other */
+            if (a_element != b_element) {
+                return false;
+            }
+
+            return true;
+        }
+
+        case cJSON_Object:
+        {
+            cJSON *a_element = NULL;
+            cJSON *b_element = NULL;
+            cJSON_ArrayForEach(a_element, a)
+            {
+                /* TODO This has O(n^2) runtime, which is horrible! */
+                b_element = get_object_item(b, a_element->string, case_sensitive);
+                if (b_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(a_element, b_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            /* doing this twice, once on a and b to prevent true comparison if a subset of b
+             * TODO: Do this the proper way, this is just a fix for now */
+            cJSON_ArrayForEach(b_element, b)
+            {
+                a_element = get_object_item(a, b_element->string, case_sensitive);
+                if (a_element == NULL)
+                {
+                    return false;
+                }
+
+                if (!cJSON_Compare(b_element, a_element, case_sensitive))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        default:
+            return false;
+    }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+    return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+    global_hooks.deallocate(object);
+}
diff --git a/src/vppinfra/cJSON.h b/src/vppinfra/cJSON.h
new file mode 100644 (file)
index 0000000..e97e5f4
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type)   type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 14
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False  (1 << 0)
+#define cJSON_True   (1 << 1)
+#define cJSON_NULL   (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array  (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw    (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON *next;
+    struct cJSON *prev;
+    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+    struct cJSON *child;
+
+    /* The type of the item, as above. */
+    int type;
+
+    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
+    char *valuestring;
+    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+    int valueint;
+    /* The item's number, if type==cJSON_Number */
+    double valuedouble;
+
+    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+    char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+      void *(CJSON_CDECL *malloc_fn)(size_t sz);
+      void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant, 
+ * but should point to a readable and writable adress area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif