diff --git a/.github/workflows/upload_component.yml b/.github/workflows/upload_component.yml index c889f0d..733a7cf 100644 --- a/.github/workflows/upload_component.yml +++ b/.github/workflows/upload_component.yml @@ -15,6 +15,6 @@ jobs: - name: Upload components to component service uses: espressif/github-actions/upload_components@master with: - directories: "cbor;jsmn;libsodium;pid_ctrl;qrcode;nghttp;sh2lib;expat;esp_encrypted_img;coap;pcap" + directories: "cbor;jsmn;libsodium;pid_ctrl;qrcode;nghttp;sh2lib;expat;esp_encrypted_img;coap;pcap;json_generator;json_parser" namespace: "espressif" api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/json_generator/CMakeLists.txt b/json_generator/CMakeLists.txt new file mode 100644 index 0000000..6fdfa98 --- /dev/null +++ b/json_generator/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "src/json_generator.c" + INCLUDE_DIRS "include" + ) diff --git a/json_generator/LICENSE b/json_generator/LICENSE new file mode 100644 index 0000000..d613ddc --- /dev/null +++ b/json_generator/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Piyush Shah + + 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. diff --git a/json_generator/README.md b/json_generator/README.md new file mode 100644 index 0000000..b0340aa --- /dev/null +++ b/json_generator/README.md @@ -0,0 +1,13 @@ +# JSON Generator +A simple JSON (JavasScript Object Notation) generator with flushing capability. +Details of JSON can be found at [http://www.json.org/](http://www.json.org/). +The JSON strings generated can be validated using any standard JSON validator. Eg. [https://jsonlint.com/](https://jsonlint.com/) + +# Files +- `src/json_generator.c`: Actual source file for the JSON generator with implementation of all APIS +- `include/json_generator.h`: Header file documenting and exposing all available APIs + +# Usage + +Include the C and H files in your project's build system and that should be enough. +`json_generator` requires only standard library functions for compilation diff --git a/json_generator/idf_component.yml b/json_generator/idf_component.yml new file mode 100644 index 0000000..03f89c6 --- /dev/null +++ b/json_generator/idf_component.yml @@ -0,0 +1,3 @@ +version: "1.1.1" +description: A simple JSON (JavasScript Object Notation) generator with flushing capability +url: https://github.com/espressif/json_generator diff --git a/json_generator/include/json_generator.h b/json_generator/include/json_generator.h new file mode 100644 index 0000000..620b2c5 --- /dev/null +++ b/json_generator/include/json_generator.h @@ -0,0 +1,511 @@ +/* + * Copyright 2020 Piyush Shah + * + * 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. + */ + +/* + * JSON String Generator + * + * This module can be used to create JSON strings with a facility + * to flush out data if the destination buffer is full. All commas + * and colons as required are automatically added by the APIs + * + */ +#ifndef _JSON_GENERATOR_H +#define _JSON_GENERATOR_H + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** Float precision i.e. number of digits after decimal point */ +#ifndef JSON_FLOAT_PRECISION +#define JSON_FLOAT_PRECISION 5 +#endif + +/** JSON string flush callback prototype + * + * This is a prototype of the function that needs to be passed to + * json_gen_str_start() and which will be invoked by the JSON generator + * module either when the buffer is full or json_gen_str_end() ins invoked. + * + * \param[in] buf Pointer to a NULL terminated JSON string + * \param[in] priv Private data to be passed to the flush callback. Will + * be the same as the one passed to json_gen_str_start() + */ +typedef void (*json_gen_flush_cb_t) (char *buf, void *priv); + +/** JSON String structure + * + * Please do not set/modify any elements. + * Just define this structure and pass a pointer to it in the APIs below + */ +typedef struct { + /** Pointer to the JSON buffer provided by the calling function */ + char *buf; + /** Size of the above buffer */ + int buf_size; + /** (Optional) callback function to invoke when the buffer gets full */ + json_gen_flush_cb_t flush_cb; + /** (Optional) Private data to pass to the callback function */ + void *priv; + /** (For Internal use only) */ + bool comma_req; + /** (For Internal use only) */ + char *free_ptr; + /** Total length */ + int total_len; +} json_gen_str_t; + +/** Start a JSON String + * + * This is the first function to be called for creating a JSON string. + * It initializes the internal data structures. After the JSON string + * generation is over, the json_gen_str_end() function should be called. + * + * \param[out] jstr Pointer to an allocated \ref json_gen_str_t structure. + * This will be initialised internally and needs to be passed to all + * subsequent function calls + * \param[out] buf Pointer to an allocated buffer into which the JSON + * string will be written + * \param[in] buf_size Size of the buffer + * \param[in] flush_cb Pointer to the flushing function of type \ref json_gen_flush_cb_t + * which will be invoked either when the buffer is full or when json_gen_str_end() + * is invoked. Can be left NULL. + * \param[in] priv Private data to be passed to the flushing function callback. + * Can be something like a session identifier (Eg. socket). Can be left NULL. + */ +void json_gen_str_start(json_gen_str_t *jstr, char *buf, int buf_size, + json_gen_flush_cb_t flush_cb, void *priv); + +/** End JSON string + * + * This should be the last function to be called after the entire JSON string + * has been generated. + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return Total length of the JSON created, including the NULL termination byte. + */ +int json_gen_str_end(json_gen_str_t *jstr); + +/** Start a JSON object + * + * This starts a JSON object by adding a '{' + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_start_object(json_gen_str_t *jstr); + +/** End a JSON object + * + * This ends a JSON object by adding a '}' + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_end_object(json_gen_str_t *jstr); + +/** Start a JSON array + * + * This starts a JSON object by adding a '[' + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_start_array(json_gen_str_t *jstr); + +/** End a JSON object + * + * This ends a JSON object by adding a ']' + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_end_array(json_gen_str_t *jstr); + +/** Push a named JSON object + * + * This adds a JSON object like "name":{ + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the object + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_push_object(json_gen_str_t *jstr, char *name); + +/** Pop a named JSON object + * + * This ends a JSON object by adding a '}'. This is basically same as + * json_gen_end_object() but included so as to complement json_gen_push_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_pop_object(json_gen_str_t *jstr); + +/** Push a JSON object string + * + * This adds a complete pre-formatted JSON object string to the JSON object. + * + * Eg. json_gen_push_object_str(jstr, "pre-formatted", "{\"a\":1,\"b\":2}"); + * This will add "pre-formatted":{"a":1,"b":2} + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the JSON object string + * \param[in] object_str The pre-formatted JSON object string + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that. + */ +int json_gen_push_object_str(json_gen_str_t *jstr, char *name, char *object_str); + +/** Push a named JSON array + * + * This adds a JSON array like "name":[ + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the array + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_push_array(json_gen_str_t *jstr, char *name); + +/** Pop a named JSON array + * + * This ends a JSON array by adding a ']'. This is basically same as + * json_gen_end_array() but included so as to complement json_gen_push_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_pop_array(json_gen_str_t *jstr); + +/** Push a JSON array string + * + * This adds a complete pre-formatted JSON array string to the JSON object. + * + * Eg. json_gen_push_object_str(jstr, "pre-formatted", "[1,2,3]"); + * This will add "pre-formatted":[1,2,3] + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the JSON array string + * \param[in] array_str The pre-formatted JSON array string + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that. + */ +int json_gen_push_array_str(json_gen_str_t *jstr, char *name, char *array_str); + +/** Add a boolean element to an object + * + * This adds a boolean element to an object. Eg. "bool_val":true + * + * \note This must be called between json_gen_start_object()/json_gen_push_object() + * and json_gen_end_object()/json_gen_pop_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the element + * \param[in] val Boolean value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_obj_set_bool(json_gen_str_t *jstr, char *name, bool val); + +/** Add an integer element to an object + * + * This adds an integer element to an object. Eg. "int_val":28 + * + * \note This must be called between json_gen_start_object()/json_gen_push_object() + * and json_gen_end_object()/json_gen_pop_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the element + * \param[in] val Integer value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_obj_set_int(json_gen_str_t *jstr, char *name, int val); + +/** Add a float element to an object + * + * This adds a float element to an object. Eg. "float_val":23.8 + * + * \note This must be called between json_gen_start_object()/json_gen_push_object() + * and json_gen_end_object()/json_gen_pop_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the element + * \param[in] val Float value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_obj_set_float(json_gen_str_t *jstr, char *name, float val); + +/** Add a string element to an object + * + * This adds a string element to an object. Eg. "string_val":"my_string" + * + * \note This must be called between json_gen_start_object()/json_gen_push_object() + * and json_gen_end_object()/json_gen_pop_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the element + * \param[in] val Null terminated string value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_obj_set_string(json_gen_str_t *jstr, char *name, char *val); + +/** Add a NULL element to an object + * + * This adds a NULL element to an object. Eg. "null_val":null + * + * \note This must be called between json_gen_start_object()/json_gen_push_object() + * and json_gen_end_object()/json_gen_pop_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_obj_set_null(json_gen_str_t *jstr, char *name); + +/** Add a boolean element to an array + * + * \note This must be called between json_gen_start_array()/json_gen_push_array() + * and json_gen_end_array()/json_gen_pop_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] val Boolean value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_arr_set_bool(json_gen_str_t *jstr, bool val); + +/** Add an integer element to an array + * + * \note This must be called between json_gen_start_array()/json_gen_push_array() + * and json_gen_end_array()/json_gen_pop_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] val Integer value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_arr_set_int(json_gen_str_t *jstr, int val); + +/** Add a float element to an array + * + * \note This must be called between json_gen_start_array()/json_gen_push_array() + * and json_gen_end_array()/json_gen_pop_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] val Float value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_arr_set_float(json_gen_str_t *jstr, float val); + +/** Add a string element to an array + * + * \note This must be called between json_gen_start_array()/json_gen_push_array() + * and json_gen_end_array()/json_gen_pop_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] val Null terminated string value of the element + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_arr_set_string(json_gen_str_t *jstr, char *val); + +/** Add a NULL element to an array + * + * \note This must be called between json_gen_start_array()/json_gen_push_array() + * and json_gen_end_array()/json_gen_pop_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_arr_set_null(json_gen_str_t *jstr); + +/** Start a Long string in an object + * + * This starts a string in an object, but does not end it (i.e., does not add the + * terminating quotes. This is useful for long strings. Eg. "string_val":"my_string. + * The API json_gen_add_to_long_string() must be used to add to this string and the API + * json_gen_end_long_string() must be used to terminate it (i.e. add the ending quotes). + * + * \note This must be called between json_gen_start_object()/json_gen_push_object() + * and json_gen_end_object()/json_gen_pop_object() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] name Name of the element + * \param[in] val Null terminated initial part of the string value. It can also be NULL + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_obj_start_long_string(json_gen_str_t *jstr, char *name, char *val); + +/** Start a Long string in an array + * + * This starts a string in an arrayt, but does not end it (i.e., does not add the + * terminating quotes. This is useful for long strings. + * The API json_gen_add_to_long_string() must be used to add to this string and the API + * json_gen_end_long_string() must be used to terminate it (i.e. add the ending quotes). + * + * \note This must be called between json_gen_start_array()/json_gen_push_array() + * and json_gen_end_array()/json_gen_pop_array() + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by + * json_gen_str_start() + * \param[in] val Null terminated initial part of the string value. It can also be NULL + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_arr_start_long_string(json_gen_str_t *jstr, char *val); + +/** Add to a JSON Long string + * + * This extends the string initialised by json_gen_obj_start_long_string() or + * json_gen_arr_start_long_string(). After the entire string is created, it should be terminated + * with json_gen_end_long_string(). + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by json_gen_str_start() + * \param[in] val Null terminated extending part of the string value. + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_add_to_long_string(json_gen_str_t *jstr, char *val); + +/** End a JSON Long string + * + * This ends the string initialised by json_gen_obj_start_long_string() or + * json_gen_arr_start_long_string() by adding the ending quotes. + * + * \param[in] jstr Pointer to the \ref json_gen_str_t structure initialised by json_gen_str_start() + * + * + * \return 0 on Success + * \return -1 if buffer is out of space (possible only if no callback function + * is passed to json_gen_str_start(). Else, buffer will be flushed out and new data + * added after that + */ +int json_gen_end_long_string(json_gen_str_t *jstr); +#ifdef __cplusplus +} +#endif +#endif diff --git a/json_generator/src/json_generator.c b/json_generator/src/json_generator.c new file mode 100644 index 0000000..cf14d28 --- /dev/null +++ b/json_generator/src/json_generator.c @@ -0,0 +1,312 @@ +/* + * Copyright 2020 Piyush Shah + * + * 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 + +#define MAX_INT_IN_STR 24 +#define MAX_FLOAT_IN_STR 30 + +static inline int json_gen_get_empty_len(json_gen_str_t *jstr) +{ + return (jstr->buf_size - (jstr->free_ptr - jstr->buf) - 1); +} + +/* This will add the incoming string to the JSON string buffer + * and flush it out if the buffer is full. Note that the data being + * flushed out will always be equal to the size of the buffer unless + * this is the last chunk being flushed out on json_gen_end_str() + */ +static int json_gen_add_to_str(json_gen_str_t *jstr, char *str) +{ + if (!str) { + return 0; + } + int len = strlen(str); + jstr->total_len += len; + if (jstr->buf == NULL) { + return 0; + } + char *cur_ptr = str; + while (1) { + int len_remaining = json_gen_get_empty_len(jstr); + int copy_len = len_remaining > len ? len : len_remaining; + memmove(jstr->free_ptr, cur_ptr, copy_len); + cur_ptr += copy_len; + jstr->free_ptr += copy_len; + len -= copy_len; + if (len) { + *jstr->free_ptr = '\0'; + /* Report error if the buffer is full and no flush callback + * is registered + */ + if (!jstr->flush_cb) { + return -1; + } + jstr->flush_cb(jstr->buf, jstr->priv); + jstr->free_ptr = jstr->buf; + } else { + break; + } + } + return 0; +} + + +void json_gen_str_start(json_gen_str_t *jstr, char *buf, int buf_size, + json_gen_flush_cb_t flush_cb, void *priv) +{ + memset(jstr, 0, sizeof(json_gen_str_t)); + jstr->buf = buf; + jstr->buf_size = buf_size; + jstr->flush_cb = flush_cb; + jstr->free_ptr = buf; + jstr->priv = priv; +} + +int json_gen_str_end(json_gen_str_t *jstr) +{ + int total_len = jstr->total_len; + if (jstr->buf) { + *jstr->free_ptr = '\0'; + if (jstr->flush_cb) { + jstr->flush_cb(jstr->buf, jstr->priv); + } + } + memset(jstr, 0, sizeof(json_gen_str_t)); + return total_len + 1; /* +1 for the NULL termination */ +} + +static inline void json_gen_handle_comma(json_gen_str_t *jstr) +{ + if (jstr->comma_req) { + json_gen_add_to_str(jstr, ","); + } +} + + +static int json_gen_handle_name(json_gen_str_t *jstr, char *name) +{ + json_gen_add_to_str(jstr, "\""); + json_gen_add_to_str(jstr, name); + return json_gen_add_to_str(jstr, "\":"); +} + + +int json_gen_start_object(json_gen_str_t *jstr) +{ + json_gen_handle_comma(jstr); + jstr->comma_req = false; + return json_gen_add_to_str(jstr, "{"); +} + +int json_gen_end_object(json_gen_str_t *jstr) +{ + jstr->comma_req = true; + return json_gen_add_to_str(jstr, "}"); +} + + +int json_gen_start_array(json_gen_str_t *jstr) +{ + json_gen_handle_comma(jstr); + jstr->comma_req = false; + return json_gen_add_to_str(jstr, "["); +} + +int json_gen_end_array(json_gen_str_t *jstr) +{ + jstr->comma_req = true; + return json_gen_add_to_str(jstr, "]"); +} + +int json_gen_push_object(json_gen_str_t *jstr, char *name) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + jstr->comma_req = false; + return json_gen_add_to_str(jstr, "{"); +} + +int json_gen_pop_object(json_gen_str_t *jstr) +{ + jstr->comma_req = true; + return json_gen_add_to_str(jstr, "}"); +} + +int json_gen_push_object_str(json_gen_str_t *jstr, char *name, char *object_str) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + jstr->comma_req = true; + return json_gen_add_to_str(jstr, object_str); +} + +int json_gen_push_array(json_gen_str_t *jstr, char *name) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + jstr->comma_req = false; + return json_gen_add_to_str(jstr, "["); +} +int json_gen_pop_array(json_gen_str_t *jstr) +{ + jstr->comma_req = true; + return json_gen_add_to_str(jstr, "]"); +} + +int json_gen_push_array_str(json_gen_str_t *jstr, char *name, char *array_str) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + jstr->comma_req = true; + return json_gen_add_to_str(jstr, array_str); +} + +static int json_gen_set_bool(json_gen_str_t *jstr, bool val) +{ + jstr->comma_req = true; + if (val) { + return json_gen_add_to_str(jstr, "true"); + } else { + return json_gen_add_to_str(jstr, "false"); + } +} +int json_gen_obj_set_bool(json_gen_str_t *jstr, char *name, bool val) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + return json_gen_set_bool(jstr, val); +} + +int json_gen_arr_set_bool(json_gen_str_t *jstr, bool val) +{ + json_gen_handle_comma(jstr); + return json_gen_set_bool(jstr, val); +} + +static int json_gen_set_int(json_gen_str_t *jstr, int val) +{ + jstr->comma_req = true; + char str[MAX_INT_IN_STR]; + snprintf(str, MAX_INT_IN_STR, "%d", val); + return json_gen_add_to_str(jstr, str); +} + +int json_gen_obj_set_int(json_gen_str_t *jstr, char *name, int val) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + return json_gen_set_int(jstr, val); +} + +int json_gen_arr_set_int(json_gen_str_t *jstr, int val) +{ + json_gen_handle_comma(jstr); + return json_gen_set_int(jstr, val); +} + + +static int json_gen_set_float(json_gen_str_t *jstr, float val) +{ + jstr->comma_req = true; + char str[MAX_FLOAT_IN_STR]; + snprintf(str, MAX_FLOAT_IN_STR, "%.*f", JSON_FLOAT_PRECISION, val); + return json_gen_add_to_str(jstr, str); +} +int json_gen_obj_set_float(json_gen_str_t *jstr, char *name, float val) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + return json_gen_set_float(jstr, val); +} +int json_gen_arr_set_float(json_gen_str_t *jstr, float val) +{ + json_gen_handle_comma(jstr); + return json_gen_set_float(jstr, val); +} + +static int json_gen_set_string(json_gen_str_t *jstr, char *val) +{ + jstr->comma_req = true; + json_gen_add_to_str(jstr, "\""); + json_gen_add_to_str(jstr, val); + return json_gen_add_to_str(jstr, "\""); +} + +int json_gen_obj_set_string(json_gen_str_t *jstr, char *name, char *val) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + return json_gen_set_string(jstr, val); +} + +int json_gen_arr_set_string(json_gen_str_t *jstr, char *val) +{ + json_gen_handle_comma(jstr); + return json_gen_set_string(jstr, val); +} + +static int json_gen_set_long_string(json_gen_str_t *jstr, char *val) +{ + jstr->comma_req = true; + json_gen_add_to_str(jstr, "\""); + return json_gen_add_to_str(jstr, val); +} + +int json_gen_obj_start_long_string(json_gen_str_t *jstr, char *name, char *val) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + return json_gen_set_long_string(jstr, val); +} + +int json_gen_arr_start_long_string(json_gen_str_t *jstr, char *val) +{ + json_gen_handle_comma(jstr); + return json_gen_set_long_string(jstr, val); +} + +int json_gen_add_to_long_string(json_gen_str_t *jstr, char *val) +{ + return json_gen_add_to_str(jstr, val); +} + +int json_gen_end_long_string(json_gen_str_t *jstr) +{ + return json_gen_add_to_str(jstr, "\""); +} +static int json_gen_set_null(json_gen_str_t *jstr) +{ + jstr->comma_req = true; + return json_gen_add_to_str(jstr, "null"); +} +int json_gen_obj_set_null(json_gen_str_t *jstr, char *name) +{ + json_gen_handle_comma(jstr); + json_gen_handle_name(jstr, name); + return json_gen_set_null(jstr); +} + +int json_gen_arr_set_null(json_gen_str_t *jstr) +{ + json_gen_handle_comma(jstr); + return json_gen_set_null(jstr); +} diff --git a/json_parser/CMakeLists.txt b/json_parser/CMakeLists.txt new file mode 100644 index 0000000..d62abc7 --- /dev/null +++ b/json_parser/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "src/json_parser.c" + INCLUDE_DIRS "include" + REQUIRES "jsmn" + ) diff --git a/json_parser/LICENSE b/json_parser/LICENSE new file mode 100644 index 0000000..d613ddc --- /dev/null +++ b/json_parser/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 Piyush Shah + + 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. diff --git a/json_parser/README.md b/json_parser/README.md new file mode 100644 index 0000000..09500eb --- /dev/null +++ b/json_parser/README.md @@ -0,0 +1,8 @@ +# JSON Parser + +This is a simple, light weight JSON parser built on top of [jsmn](https://github.com/zserge/jsmn). + +Files + +- `src/json_parser.c`: Source file which has all the logic for implementing the APIs built on top of JSMN +- `include/json_parser.h`: Header file that exposes all APIs diff --git a/json_parser/idf_component.yml b/json_parser/idf_component.yml new file mode 100644 index 0000000..37b98b5 --- /dev/null +++ b/json_parser/idf_component.yml @@ -0,0 +1,5 @@ +version: "1.0.1" +description: This is a simple, light weight JSON parser built on top of jsmn +url: https://github.com/espressif/json_parser +dependencies: + jsmn: "~1.1" diff --git a/json_parser/include/json_parser.h b/json_parser/include/json_parser.h new file mode 100644 index 0000000..6604416 --- /dev/null +++ b/json_parser/include/json_parser.h @@ -0,0 +1,76 @@ +/* + * Copyright 2020 Piyush Shah + * + * 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 _JSON_PARSER_H_ +#define _JSON_PARSER_H_ + +#define JSMN_HEADER +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define OS_SUCCESS 0 +#define OS_FAIL -1 + +typedef jsmn_parser json_parser_t; +typedef jsmntok_t json_tok_t; + +typedef struct { + json_parser_t parser; + char *js; + json_tok_t *tokens; + json_tok_t *cur; + int num_tokens; +} jparse_ctx_t; + +int json_parse_start(jparse_ctx_t *jctx, char *js, int len); +int json_parse_end(jparse_ctx_t *jctx); + +int json_obj_get_array(jparse_ctx_t *jctx, char *name, int *num_elem); +int json_obj_leave_array(jparse_ctx_t *jctx); +int json_obj_get_object(jparse_ctx_t *jctx, char *name); +int json_obj_leave_object(jparse_ctx_t *jctx); +int json_obj_get_bool(jparse_ctx_t *jctx, char *name, bool *val); +int json_obj_get_int(jparse_ctx_t *jctx, char *name, int *val); +int json_obj_get_int64(jparse_ctx_t *jctx, char *name, int64_t *val); +int json_obj_get_float(jparse_ctx_t *jctx, char *name, float *val); +int json_obj_get_string(jparse_ctx_t *jctx, char *name, char *val, int size); +int json_obj_get_strlen(jparse_ctx_t *jctx, char *name, int *strlen); +int json_obj_get_object_str(jparse_ctx_t *jctx, char *name, char *val, int size); +int json_obj_get_object_strlen(jparse_ctx_t *jctx, char *name, int *strlen); +int json_obj_get_array_str(jparse_ctx_t *jctx, char *name, char *val, int size); +int json_obj_get_array_strlen(jparse_ctx_t *jctx, char *name, int *strlen); + +int json_arr_get_array(jparse_ctx_t *jctx, uint32_t index); +int json_arr_leave_array(jparse_ctx_t *jctx); +int json_arr_get_object(jparse_ctx_t *jctx, uint32_t index); +int json_arr_leave_object(jparse_ctx_t *jctx); +int json_arr_get_bool(jparse_ctx_t *jctx, uint32_t index, bool *val); +int json_arr_get_int(jparse_ctx_t *jctx, uint32_t index, int *val); +int json_arr_get_int64(jparse_ctx_t *jctx, uint32_t index, int64_t *val); +int json_arr_get_float(jparse_ctx_t *jctx, uint32_t index, float *val); +int json_arr_get_string(jparse_ctx_t *jctx, uint32_t index, char *val, int size); +int json_arr_get_strlen(jparse_ctx_t *jctx, uint32_t index, int *strlen); + +#ifdef __cplusplus +} +#endif + +#endif /* _JSON_PARSER_H_ */ diff --git a/json_parser/src/json_parser.c b/json_parser/src/json_parser.c new file mode 100644 index 0000000..85a4a7b --- /dev/null +++ b/json_parser/src/json_parser.c @@ -0,0 +1,438 @@ +/* + * Copyright 2020 Piyush Shah + * + * 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 +#define JSMN_PARENT_LINKS +#define JSMN_STRICT +#define JSMN_STATIC +#include +#include + +static bool token_matches_str(jparse_ctx_t *ctx, json_tok_t *tok, char *str) +{ + char *js = ctx->js; + return ((strncmp(js + tok->start, str, strlen(str)) == 0) + && (strlen(str) == (size_t) (tok->end - tok->start))); +} + +static json_tok_t *json_skip_elem(json_tok_t *token) +{ + json_tok_t *cur = token; + int cnt = cur->size; + while (cnt--) { + cur++; + cur = json_skip_elem(cur); + } + return cur; +} + +static int json_tok_to_bool(jparse_ctx_t *jctx, json_tok_t *tok, bool *val) +{ + if (token_matches_str(jctx, tok, "true") || token_matches_str(jctx, tok, "1")) { + *val = true; + } else if (token_matches_str(jctx, tok, "false") || token_matches_str(jctx, tok, "0")) { + *val = false; + } else { + return -OS_FAIL; + } + return OS_SUCCESS; +} + +static int json_tok_to_int(jparse_ctx_t *jctx, json_tok_t *tok, int *val) +{ + char *tok_start = &jctx->js[tok->start]; + char *tok_end = &jctx->js[tok->end]; + char *endptr; + int i = strtoul(tok_start, &endptr, 10); + if (endptr == tok_end) { + *val = i; + return OS_SUCCESS; + } + return -OS_FAIL; +} + +static int json_tok_to_int64(jparse_ctx_t *jctx, json_tok_t *tok, int64_t *val) +{ + char *tok_start = &jctx->js[tok->start]; + char *tok_end = &jctx->js[tok->end]; + char *endptr; + int64_t i64 = strtoull(tok_start, &endptr, 10); + if (endptr == tok_end) { + *val = i64; + return OS_SUCCESS; + } + return -OS_FAIL; +} + +static int json_tok_to_float(jparse_ctx_t *jctx, json_tok_t *tok, float *val) +{ + char *tok_start = &jctx->js[tok->start]; + char *tok_end = &jctx->js[tok->end]; + char *endptr; + float f = strtof(tok_start, &endptr); + if (endptr == tok_end) { + *val = f; + return OS_SUCCESS; + } + return -OS_FAIL; +} + +static int json_tok_to_string(jparse_ctx_t *jctx, json_tok_t *tok, char *val, int size) +{ + if ((tok->end - tok->start) > (size - 1)) { + return -OS_FAIL; + } + strncpy(val, jctx->js + tok->start, tok->end - tok->start); + val[tok->end - tok->start] = 0; + return OS_SUCCESS; +} + +static json_tok_t *json_obj_search(jparse_ctx_t *jctx, char *key) +{ + json_tok_t *tok = jctx->cur; + int size = tok->size; + if (size <= 0) { + return NULL; + } + if (tok->type != JSMN_OBJECT) { + return NULL; + } + + while (size--) { + tok++; + if (token_matches_str(jctx, tok, key)) { + return tok; + } + tok = json_skip_elem(tok); + } + return NULL; +} + +static json_tok_t *json_obj_get_val_tok(jparse_ctx_t *jctx, char *name, jsmntype_t type) +{ + json_tok_t *tok = json_obj_search(jctx, name); + if (!tok) { + return NULL; + } + tok++; + if (tok->type != type) { + return NULL; + } + return tok; +} + +int json_obj_get_array(jparse_ctx_t *jctx, char *name, int *num_elem) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_ARRAY); + if (!tok) { + return -OS_FAIL; + } + jctx->cur = tok; + *num_elem = tok->size; + return OS_SUCCESS; +} + +int json_obj_leave_array(jparse_ctx_t *jctx) +{ + /* The array's parent will be the key */ + if (jctx->cur->parent < 0) { + return -OS_FAIL; + } + jctx->cur = &jctx->tokens[jctx->cur->parent]; + + /* The key's parent will be the actual parent object */ + if (jctx->cur->parent < 0) { + return -OS_FAIL; + } + jctx->cur = &jctx->tokens[jctx->cur->parent]; + return OS_SUCCESS; +} + +int json_obj_get_object(jparse_ctx_t *jctx, char *name) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_OBJECT); + if (!tok) { + return -OS_FAIL; + } + jctx->cur = tok; + return OS_SUCCESS; +} + +int json_obj_leave_object(jparse_ctx_t *jctx) +{ + /* The objects's parent will be the key */ + if (jctx->cur->parent < 0) { + return -OS_FAIL; + } + jctx->cur = &jctx->tokens[jctx->cur->parent]; + + /* The key's parent will be the actual parent object */ + if (jctx->cur->parent < 0) { + return -OS_FAIL; + } + jctx->cur = &jctx->tokens[jctx->cur->parent]; + return OS_SUCCESS; +} + +int json_obj_get_bool(jparse_ctx_t *jctx, char *name, bool *val) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_bool(jctx, tok, val); +} + +int json_obj_get_int(jparse_ctx_t *jctx, char *name, int *val) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_int(jctx, tok, val); +} + +int json_obj_get_int64(jparse_ctx_t *jctx, char *name, int64_t *val) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_int64(jctx, tok, val); +} + +int json_obj_get_float(jparse_ctx_t *jctx, char *name, float *val) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_float(jctx, tok, val); +} + +int json_obj_get_string(jparse_ctx_t *jctx, char *name, char *val, int size) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_STRING); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_string(jctx, tok, val, size); +} + +int json_obj_get_strlen(jparse_ctx_t *jctx, char *name, int *strlen) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_STRING); + if (!tok) { + return -OS_FAIL; + } + *strlen = tok->end - tok->start; + return OS_SUCCESS; +} + +int json_obj_get_object_str(jparse_ctx_t *jctx, char *name, char *val, int size) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_OBJECT); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_string(jctx, tok, val, size); +} + +int json_obj_get_object_strlen(jparse_ctx_t *jctx, char *name, int *strlen) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_OBJECT); + if (!tok) { + return -OS_FAIL; + } + *strlen = tok->end - tok->start; + return OS_SUCCESS; +} +int json_obj_get_array_str(jparse_ctx_t *jctx, char *name, char *val, int size) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_ARRAY); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_string(jctx, tok, val, size); +} + +int json_obj_get_array_strlen(jparse_ctx_t *jctx, char *name, int *strlen) +{ + json_tok_t *tok = json_obj_get_val_tok(jctx, name, JSMN_ARRAY); + if (!tok) { + return -OS_FAIL; + } + *strlen = tok->end - tok->start; + return OS_SUCCESS; +} + +static json_tok_t *json_arr_search(jparse_ctx_t *ctx, uint32_t index) +{ + json_tok_t *tok = ctx->cur; + if ((tok->type != JSMN_ARRAY) || (tok->size <= 0)) { + return NULL; + } + if (index > (uint32_t)(tok->size - 1)) { + return NULL; + } + /* Increment by 1, so that token points to index 0 */ + tok++; + while (index--) { + tok = json_skip_elem(tok); + tok++; + } + return tok; +} +static json_tok_t *json_arr_get_val_tok(jparse_ctx_t *jctx, uint32_t index, jsmntype_t type) +{ + json_tok_t *tok = json_arr_search(jctx, index); + if (!tok) { + return NULL; + } + if (tok->type != type) { + return NULL; + } + return tok; +} + +int json_arr_get_array(jparse_ctx_t *jctx, uint32_t index) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_ARRAY); + if (!tok) { + return -OS_FAIL; + } + jctx->cur = tok; + return OS_SUCCESS; +} + +int json_arr_leave_array(jparse_ctx_t *jctx) +{ + if (jctx->cur->parent < 0) { + return -OS_FAIL; + } + jctx->cur = &jctx->tokens[jctx->cur->parent]; + return OS_SUCCESS; +} + +int json_arr_get_object(jparse_ctx_t *jctx, uint32_t index) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_OBJECT); + if (!tok) { + return -OS_FAIL; + } + jctx->cur = tok; + return OS_SUCCESS; +} + +int json_arr_leave_object(jparse_ctx_t *jctx) +{ + if (jctx->cur->parent < 0) { + return -OS_FAIL; + } + jctx->cur = &jctx->tokens[jctx->cur->parent]; + return OS_SUCCESS; +} + +int json_arr_get_bool(jparse_ctx_t *jctx, uint32_t index, bool *val) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_bool(jctx, tok, val); +} + +int json_arr_get_int(jparse_ctx_t *jctx, uint32_t index, int *val) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_int(jctx, tok, val); +} + +int json_arr_get_int64(jparse_ctx_t *jctx, uint32_t index, int64_t *val) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_int64(jctx, tok, val); +} + +int json_arr_get_float(jparse_ctx_t *jctx, uint32_t index, float *val) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_PRIMITIVE); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_float(jctx, tok, val); +} + +int json_arr_get_string(jparse_ctx_t *jctx, uint32_t index, char *val, int size) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_STRING); + if (!tok) { + return -OS_FAIL; + } + return json_tok_to_string(jctx, tok, val, size); +} + +int json_arr_get_strlen(jparse_ctx_t *jctx, uint32_t index, int *strlen) +{ + json_tok_t *tok = json_arr_get_val_tok(jctx, index, JSMN_STRING); + if (!tok) { + return -OS_FAIL; + } + *strlen = tok->end - tok->start; + return OS_SUCCESS; +} + +int json_parse_start(jparse_ctx_t *jctx, char *js, int len) +{ + memset(jctx, 0, sizeof(jparse_ctx_t)); + jsmn_init(&jctx->parser); + int num_tokens = jsmn_parse(&jctx->parser, js, len, NULL, 0); + if (num_tokens <= 0) { + return -OS_FAIL; + } + jctx->num_tokens = num_tokens; + jctx->tokens = calloc(num_tokens, sizeof(json_tok_t)); + if (!jctx->tokens) { + return -OS_FAIL; + } + jctx->js = js; + jsmn_init(&jctx->parser); + int ret = jsmn_parse(&jctx->parser, js, len, jctx->tokens, jctx->num_tokens); + if (ret <= 0) { + free(jctx->tokens); + memset(jctx, 0, sizeof(jparse_ctx_t)); + return -OS_FAIL; + } + jctx->cur = jctx->tokens; + return OS_SUCCESS; +} + +int json_parse_end(jparse_ctx_t *jctx) +{ + if (jctx->tokens) { + free(jctx->tokens); + } + memset(jctx, 0, sizeof(jparse_ctx_t)); + return OS_SUCCESS; +}