From df87f8092f5b6b54eef0d5acf3c27c2e398a401a Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Wed, 18 Nov 2020 19:17:48 +0100 Subject: api: vat2 and json autogeneration for api messages 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 Change-Id: Ib45ddeafb180ea7da8c5dc274a9274d7a4edc876 Signed-off-by: Ole Troan --- src/CMakeLists.txt | 2 +- src/cmake/api.cmake | 21 +- src/cmake/library.cmake | 32 + src/cmake/plugin.cmake | 6 +- src/plugins/acl/CMakeLists.txt | 3 - src/plugins/acl/acl.api | 22 +- src/plugins/acl/acl.c | 53 +- src/plugins/acl/acl_test.c | 1 - src/plugins/acl/manual_fns.h | 433 ----- src/plugins/dns/dns.api | 8 +- src/plugins/dns/dns.c | 53 - src/plugins/flowprobe/flowprobe.api | 2 +- src/plugins/flowprobe/flowprobe.c | 18 - src/plugins/gbp/CMakeLists.txt | 1 - src/plugins/gbp/gbp.api | 28 +- src/plugins/gbp/gbp_api.c | 1 - src/plugins/gbp/gbp_api_print.h | 352 ---- src/plugins/lb/api.c | 77 - src/plugins/lb/lb.api | 8 +- src/plugins/map/map_api.c | 5 +- src/plugins/nat/nat44.api | 4 +- src/plugins/nat/nat44_api.c | 3 - src/plugins/stn/stn.api | 2 +- src/plugins/stn/stn_api.c | 18 - src/tools/vppapigen/vppapigen.py | 662 ++++---- src/tools/vppapigen/vppapigen_c.py | 1126 +++++++++++-- src/vat2/CMakeLists.txt | 43 + src/vat2/jsonconvert.c | 514 ++++++ src/vat2/jsonconvert.h | 92 ++ src/vat2/main.c | 227 +++ src/vat2/plugin.c | 195 +++ src/vat2/vat2.h | 12 + src/vat2/vat2_helpers.h | 34 + src/vnet/CMakeLists.txt | 8 +- src/vnet/ip/ip.api | 6 +- src/vnet/l2/l2.api | 2 +- src/vnet/l2/l2_api.c | 3 - src/vnet/mpls/mpls.api | 4 +- src/vpp-api/client/client.c | 6 +- src/vpp-api/client/vppapiclient.h | 2 +- src/vpp/CMakeLists.txt | 7 + src/vppinfra/CMakeLists.txt | 7 + src/vppinfra/cJSON.c | 3095 +++++++++++++++++++++++++++++++++++ src/vppinfra/cJSON.h | 293 ++++ 44 files changed, 6019 insertions(+), 1472 deletions(-) delete mode 100644 src/plugins/acl/manual_fns.h delete mode 100644 src/plugins/gbp/gbp_api_print.h create mode 100644 src/vat2/CMakeLists.txt create mode 100644 src/vat2/jsonconvert.c create mode 100644 src/vat2/jsonconvert.h create mode 100644 src/vat2/main.c create mode 100644 src/vat2/plugin.c create mode 100644 src/vat2/vat2.h create mode 100644 src/vat2/vat2_helpers.h create mode 100644 src/vppinfra/cJSON.c create mode 100644 src/vppinfra/cJSON.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6fc243ca4d2..8947c8afff6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 ) diff --git a/src/cmake/api.cmake b/src/cmake/api.cmake index fe2cad6bc46..f3af68748d6 100644 --- a/src/cmake/api.cmake +++ b/src/cmake/api.cmake @@ -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) diff --git a/src/cmake/library.cmake b/src/cmake/library.cmake index a5b6c76d2d1..3f17e30daab 100644 --- a/src/cmake/library.cmake +++ b/src/cmake/library.cmake @@ -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() diff --git a/src/cmake/plugin.cmake b/src/cmake/plugin.cmake index 2aae89510c1..c165b5914dc 100644 --- a/src/cmake/plugin.cmake +++ b/src/cmake/plugin.cmake @@ -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 diff --git a/src/plugins/acl/CMakeLists.txt b/src/plugins/acl/CMakeLists.txt index 78cc818d1ea..c43dd23ea51 100644 --- a/src/plugins/acl/CMakeLists.txt +++ b/src/plugins/acl/CMakeLists.txt @@ -30,7 +30,4 @@ add_vpp_plugin(acl API_TEST_SOURCES acl_test.c - - INSTALL_HEADERS - manual_fns.h ) diff --git a/src/plugins/acl/acl.api b/src/plugins/acl/acl.api index 25c231db82a..a4706c3e529 100644 --- a/src/plugins/acl/acl.api +++ b/src/plugins/acl/acl.api @@ -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; diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c index b4770a70db0..b18e8515cde 100644 --- a/src/plugins/acl/acl.c +++ b/src/plugins/acl/acl.c @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include @@ -34,7 +37,6 @@ #include #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); } diff --git a/src/plugins/acl/acl_test.c b/src/plugins/acl/acl_test.c index c139b325568..79058cdc268 100644 --- a/src/plugins/acl/acl_test.c +++ b/src/plugins/acl/acl_test.c @@ -37,7 +37,6 @@ uword unformat_sw_if_index (unformat_input_t * input, va_list * args); #include #include #define vl_print(handle, ...) -#include #undef vl_print #define vl_endianfun /* define message structures */ #include diff --git a/src/plugins/acl/manual_fns.h b/src/plugins/acl/manual_fns.h deleted file mode 100644 index f2585a9985d..00000000000 --- a/src/plugins/acl/manual_fns.h +++ /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 -#include -#include - -#define vl_endianfun /* define message structures */ -#include -#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_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 */ diff --git a/src/plugins/dns/dns.api b/src/plugins/dns/dns.api index 866ed8abae3..17ab5244388 100644 --- a/src/plugins/dns/dns.api +++ b/src/plugins/dns/dns.api @@ -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; diff --git a/src/plugins/dns/dns.c b/src/plugins/dns/dns.c index 045f4d616a3..de1862dd52c 100644 --- a/src/plugins/dns/dns.c +++ b/src/plugins/dns/dns.c @@ -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 static clib_error_t * dns_init (vlib_main_t * vm) diff --git a/src/plugins/flowprobe/flowprobe.api b/src/plugins/flowprobe/flowprobe.api index 8e8b90fc4e3..55dd51d3c30 100644 --- a/src/plugins/flowprobe/flowprobe.api +++ b/src/plugins/flowprobe/flowprobe.api @@ -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; diff --git a/src/plugins/flowprobe/flowprobe.c b/src/plugins/flowprobe/flowprobe.c index 9bc12c8b60c..3df877aa4a1 100644 --- a/src/plugins/flowprobe/flowprobe.c +++ b/src/plugins/flowprobe/flowprobe.c @@ -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; \ diff --git a/src/plugins/gbp/CMakeLists.txt b/src/plugins/gbp/CMakeLists.txt index d9034fbf3fe..95f664ff08e 100644 --- a/src/plugins/gbp/CMakeLists.txt +++ b/src/plugins/gbp/CMakeLists.txt @@ -51,5 +51,4 @@ add_vpp_plugin(gbp INSTALL_HEADERS gbp.h - gbp_api_print.h ) diff --git a/src/plugins/gbp/gbp.api b/src/plugins/gbp/gbp.api index f7643c76c02..2f46634af52 100644 --- a/src/plugins/gbp/gbp.api +++ b/src/plugins/gbp/gbp.api @@ -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; diff --git a/src/plugins/gbp/gbp_api.c b/src/plugins/gbp/gbp_api.c index aea03d80226..ab89172b1af 100644 --- a/src/plugins/gbp/gbp_api.c +++ b/src/plugins/gbp/gbp_api.c @@ -42,7 +42,6 @@ #include #include #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 index 39c25b6e476..00000000000 --- a/src/plugins/gbp/gbp_api_print.h +++ /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 - -/* 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__ */ diff --git a/src/plugins/lb/api.c b/src/plugins/lb/api.c index 253cf5b5ba8..176da24ce16 100644 --- a/src/plugins/lb/api.c +++ b/src/plugins/lb/api.c @@ -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 static clib_error_t * lb_api_init (vlib_main_t * vm) { diff --git a/src/plugins/lb/lb.api b/src/plugins/lb/lb.api index 564fe232451..4bf30e76b59 100644 --- a/src/plugins/lb/lb.api +++ b/src/plugins/lb/lb.api @@ -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; diff --git a/src/plugins/map/map_api.c b/src/plugins/map/map_api.c index 94d2458dd79..e65174eaf88 100644 --- a/src/plugins/map/map_api.c +++ b/src/plugins/map/map_api.c @@ -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, diff --git a/src/plugins/nat/nat44.api b/src/plugins/nat/nat44.api index 4bbd254f67a..fd06c10149c 100644 --- a/src/plugins/nat/nat44.api +++ b/src/plugins/nat/nat44.api @@ -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; diff --git a/src/plugins/nat/nat44_api.c b/src/plugins/nat/nat44_api.c index 6e282856943..37c3dbadfc0 100644 --- a/src/plugins/nat/nat44_api.c +++ b/src/plugins/nat/nat44_api.c @@ -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 #include diff --git a/src/plugins/stn/stn.api b/src/plugins/stn/stn.api index 613d1804684..fad6299479b 100644 --- a/src/plugins/stn/stn.api +++ b/src/plugins/stn/stn.api @@ -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; diff --git a/src/plugins/stn/stn_api.c b/src/plugins/stn/stn_api.c index a22bbffd2fe..818c4a65c0a 100644 --- a/src/plugins/stn/stn_api.c +++ b/src/plugins/stn/stn_api.c @@ -37,24 +37,6 @@ 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) { diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index b828706f814..da008231a32 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -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 diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index a8790744054..4369dd86690 100644 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -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 \n\n') + write('#include \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 \n\n') + write('#include \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 .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 +#include +#include +#include +#include + +#define vl_typedefs /* define message structures */ +#include +#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 + +#include + +''' + + 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 index 00000000000..690267c6113 --- /dev/null +++ b/src/vat2/CMakeLists.txt @@ -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 index 00000000000..3aeaeedb2f7 --- /dev/null +++ b/src/vat2/jsonconvert.c @@ -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 +#include +#include +#include +#include +#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 index 00000000000..2e723fa637d --- /dev/null +++ b/src/vat2/jsonconvert.h @@ -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 +#include +#include +#include +#include +#include + +#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 index 00000000000..5b042e23503 --- /dev/null +++ b/src/vat2/main.c @@ -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 +#include +#include +#include +#include +#include + +/* VPP API client includes */ +#include + +#include +#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 index 00000000000..6b6d55ac9b0 --- /dev/null +++ b/src/vat2/plugin.c @@ -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 +#include +#include +#include +#include +#include +#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 index 00000000000..d477b7279b3 --- /dev/null +++ b/src/vat2/vat2.h @@ -0,0 +1,12 @@ +#ifndef included_vat2_h +#define included_vat2_h + +#include + +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 index 00000000000..929c012485f --- /dev/null +++ b/src/vat2/vat2_helpers.h @@ -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 +#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 diff --git a/src/vnet/CMakeLists.txt b/src/vnet/CMakeLists.txt index 9f4dbb085d3..e387d25f913 100644 --- a/src/vnet/CMakeLists.txt +++ b/src/vnet/CMakeLists.txt @@ -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} +) + +############################################################################## diff --git a/src/vnet/ip/ip.api b/src/vnet/ip/ip.api index 7ac9de783bb..8b18c1e764a 100644 --- a/src/vnet/ip/ip.api +++ b/src/vnet/ip/ip.api @@ -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; diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index b6bd4b5f7b1..416874d94c2 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -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; diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index c8690c6ccae..746c7fdf2a2 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -42,9 +42,6 @@ #include #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 diff --git a/src/vnet/mpls/mpls.api b/src/vnet/mpls/mpls.api index fb3ab1a4798..9d4ec0bf7bf 100644 --- a/src/vnet/mpls/mpls.api +++ b/src/vnet/mpls/mpls.api @@ -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; diff --git a/src/vpp-api/client/client.c b/src/vpp-api/client/client.c index 2127a62269f..20adc957869 100644 --- a/src/vpp-api/client/client.c +++ b/src/vpp-api/client/client.c @@ -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 diff --git a/src/vpp-api/client/vppapiclient.h b/src/vpp-api/client/vppapiclient.h index 680003e7d4f..3ccd84ffc5f 100644 --- a/src/vpp-api/client/vppapiclient.h +++ b/src/vpp-api/client/vppapiclient.h @@ -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); diff --git a/src/vpp/CMakeLists.txt b/src/vpp/CMakeLists.txt index 0a8ec31601b..df8383709b8 100644 --- a/src/vpp/CMakeLists.txt +++ b/src/vpp/CMakeLists.txt @@ -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} +) diff --git a/src/vppinfra/CMakeLists.txt b/src/vppinfra/CMakeLists.txt index a670181732e..a972d906769 100644 --- a/src/vppinfra/CMakeLists.txt +++ b/src/vppinfra/CMakeLists.txt @@ -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 index 00000000000..4c6a308eece --- /dev/null +++ b/src/vppinfra/cJSON.c @@ -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 +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#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 index 00000000000..e97e5f4cdc4 --- /dev/null +++ b/src/vppinfra/cJSON.h @@ -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 + +/* 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 -- cgit 1.2.3-korg