From df628a1b0e99f23386e530f107ae71093a4be9d1 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 28 May 2023 12:04:52 +0200 Subject: [PATCH 01/28] Apply new .clang-format --- .clang-format | 4 ++-- lwjson/src/include/lwjson/lwjson.h | 18 +++++++++--------- lwjson/src/lwjson/lwjson.c | 8 +++----- lwjson/src/lwjson/lwjson_debug.c | 3 +-- lwjson/src/lwjson/lwjson_stream.c | 3 +-- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/.clang-format b/.clang-format index 1fad350..0227352 100644 --- a/.clang-format +++ b/.clang-format @@ -16,14 +16,14 @@ AlignConsecutiveBitFields: AlignConsecutiveDeclarations: None AlignEscapedNewlines: Right AlignOperands: Align -SortIncludes: false +SortIncludes: true InsertBraces: true # Control statements must have curly brackets AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortEnumsOnASingleLine: true AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: true AllowShortFunctionsOnASingleLine: All AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index b4a1918..698eec1 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -95,8 +95,8 @@ typedef struct lwjson_token { token_value_len; /*!< Length of token value (this is needed to support const input strings to parse) */ } str; /*!< String data */ - lwjson_real_t num_real; /*!< Real number format */ - lwjson_int_t num_int; /*!< Int number format */ + lwjson_real_t num_real; /*!< Real number format */ + lwjson_int_t num_int; /*!< Int number format */ struct lwjson_token* first_child; /*!< First children object for object or array type */ } u; /*!< Union with different data types */ } lwjson_token_t; @@ -105,11 +105,11 @@ typedef struct lwjson_token { * \brief JSON result enumeration */ typedef enum { - lwjsonOK = 0x00, /*!< Function returns successfully */ - lwjsonERR, /*!< Generic error message */ - lwjsonERRJSON, /*!< Error JSON format */ - lwjsonERRMEM, /*!< Memory error */ - lwjsonERRPAR, /*!< Parameter error */ + lwjsonOK = 0x00, /*!< Function returns successfully */ + lwjsonERR, /*!< Generic error message */ + lwjsonERRJSON, /*!< Error JSON format */ + lwjsonERRMEM, /*!< Memory error */ + lwjsonERRPAR, /*!< Parameter error */ lwjsonSTREAMWAITFIRSTCHAR, /*!< Streaming parser did not yet receive first valid character indicating start of JSON sequence */ @@ -196,7 +196,7 @@ typedef struct lwjson_stream_parser { stack[LWJSON_CFG_STREAM_STACK_SIZE]; /*!< Stack used for parsing. TODO: Add conditional compilation flag */ size_t stack_pos; /*!< Current stack position */ - lwjson_stream_state_t parse_state; /*!< Parser state */ + lwjson_stream_state_t parse_state; /*!< Parser state */ lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */ @@ -217,7 +217,7 @@ typedef struct lwjson_stream_parser { } prim; /*!< Primitive object. Used for all types, except key or string */ /* Todo: Add other types */ - } data; /*!< Data union used to parse various */ + } data; /*!< Data union used to parse various */ char prev_c; /*!< History of characters */ } lwjson_stream_parser_t; diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index 048f2ff..263fd03 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -132,8 +132,7 @@ prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) { case 'f': case 'n': case 'r': - case 't': - break; + case 't': break; case 'u': ++pobj->p; for (size_t i = 0; i < 4; ++i, ++len) { @@ -146,8 +145,7 @@ prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) { } } break; - default: - return lwjsonERRJSON; + default: return lwjsonERRJSON; } } else if (*pobj->p == '"') { ++pobj->p; @@ -222,7 +220,7 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou for (int_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p) { int_num = int_num * 10 + (*pobj->p - '0'); } - + real_num = (lwjson_real_t)int_num; if (pobj->p != NULL && *pobj->p == '.') { /* Number has exponent */ diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index 0111552..b3c260b 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -102,8 +102,7 @@ prv_print_token(lwjson_token_print_t* p, const lwjson_token_t* token) { printf("NULL"); break; } - default: - break; + default: break; } if (token->next != NULL) { printf(","); diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 0d2659d..8927dce 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -430,8 +430,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } /* TODO: Add other case statements */ - default: - break; + default: break; } jsp->prev_c = c; /* Save current c as previous for next round */ return lwjsonSTREAMINPROG; From c28a6c9ff98f71997d31f0a32508e7522d9585d0 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 20 Jul 2023 22:15:58 +0200 Subject: [PATCH 02/28] Update link for new LwCELL, add C11 standard note --- README.md | 2 +- docs/index.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 94f0198..b64d5f9 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ First one being optimized for ultra small microcontrollers, second one being rea ## Features -* Written in ANSI C99, compatible with ``size_t`` for size data types +* Written in C (C11), compatible with ``size_t`` for size data types * RFC 4627 and RFC 8259 compliant * Based on static token allocation with optional application dynamic pre-allocation * No recursion during parse operation diff --git a/docs/index.rst b/docs/index.rst index 8ad00bf..bfb8c6f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,7 +16,7 @@ LwJSON is a generic JSON parser library optimized for embedded systems. Features ^^^^^^^^ -* Written in ANSI C99, compatible with ``size_t`` for size data types +* Written in C (C11), compatible with ``size_t`` for size data types * RFC 4627 and RFC 8259 compliant * Based on static token allocation with optional application dynamic pre-allocation * No recursion during parse operation @@ -84,7 +84,7 @@ Table of contents LwESP - ESP-AT library LwEVT - Event manager LwGPS - GPS NMEA parser - LwGSM - GSM-AT library + LwCELL - Cellular modem host AT library LwJSON - JSON parser LwMEM - Memory manager LwOW - OneWire with UART From 6eda090fe61ecbc43505cef94222535c37a84438 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sun, 27 Aug 2023 19:48:33 +0200 Subject: [PATCH 03/28] Fix double colon --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b64d5f9..1faed67 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ First one being optimized for ultra small microcontrollers, second one being rea ## Contribute -Fresh contributions are always welcome. Simple instructions to proceed:: +Fresh contributions are always welcome. Simple instructions to proceed: 1. Fork Github repository 2. Follow [C style & coding rules](https://github.com/MaJerle/c-code-style) already used in the project From f19d71fd3b843847ed0b09b3e6a02e4c06544c8f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 28 Aug 2023 21:42:55 +0200 Subject: [PATCH 04/28] Add readthedocs YAML file --- .readthedocs.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..dd64b05 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +version: 2 +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Python configuration +python: + install: + - requirements: docs/requirements.txt + +formats: + - pdf + - epub From e37dbf337ac5b089abfa062c4e2d475ac3107b75 Mon Sep 17 00:00:00 2001 From: erics Date: Thu, 31 Aug 2023 18:42:42 +0300 Subject: [PATCH 05/28] add user context to stream callback --- dev/main.c | 2 +- examples/example_stream.c | 40 ++++++++++++++++++++++++++++-- lwjson/src/include/lwjson/lwjson.h | 3 ++- lwjson/src/lwjson/lwjson_stream.c | 3 ++- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/dev/main.c b/dev/main.c index e293e6e..45efd37 100644 --- a/dev/main.c +++ b/dev/main.c @@ -68,7 +68,7 @@ main() { } /* Now parse as a stream */ - lwjson_stream_init(&stream_parser, jsp_stream_callback); + lwjson_stream_init(&stream_parser, jsp_stream_callback,NULL); for (const char* str = json_text; str != NULL && *str != '\0'; ++str) { lwjsonr_t res = lwjson_stream_parse(&stream_parser, *str); if (res == lwjsonSTREAMWAITFIRSTCHAR) { diff --git a/examples/example_stream.c b/examples/example_stream.c index 17ea749..c6ed6dc 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -1,8 +1,15 @@ #include #include "lwjson/lwjson.h" +#include +typedef struct example_data_struct_t { + uint8_t k1[10]; + char *k2; + int k2_len; + int k2_pos; +} example_data_struct_t; /* Test string to parser */ -static const char* json_str = "{\"k1\":\"v1\",\"k2\":[true, false]}"; +static const char* json_str = "{\"k1\":\"v1\",\"k2\":[true, false, true]}"; /* LwJSON stream parser */ static lwjson_stream_parser_t stream_parser; @@ -15,11 +22,26 @@ static lwjson_stream_parser_t stream_parser; void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { /* Get a value corresponsing to "k1" key */ + example_data_struct_t* data = (example_data_struct_t *)jsp->user_data; + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ && strcmp(jsp->stack[1].meta.name, "k1") == 0) { printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); + strncpy((char *)data->k1, jsp->data.str.buff, sizeof(data->k1) - 1); + } + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ + && strcmp(jsp->stack[1].meta.name, "k2") == 0) { + printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); + if (jsp->stack_pos >= 3 + && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[2].meta.index < data->k2_len) { + printf("Got array value '%s' index = %d \r\n", jsp->data.str.buff, jsp->stack[2].meta.index); + data->k2[jsp->stack[2].meta.index] = (strncmp(jsp->data.str.buff, "true", 4) == 0); + data->k2_pos = jsp->stack[2].meta.index + 1; + } } (void)type; } @@ -29,7 +51,12 @@ void example_stream_run(void) { lwjsonr_t res; printf("\r\n\r\nParsing stream\r\n"); - lwjson_stream_init(&stream_parser, prv_example_callback_func); + example_data_struct_t data; + char k2_buff[10]; + data.k2 = k2_buff; + data.k2_len = sizeof(k2_buff); + data.k2_pos = 0; + lwjson_stream_init(&stream_parser, prv_example_callback_func, &data); /* Demonstrate as stream inputs */ for (const char* c = json_str; *c != '\0'; ++c) { @@ -45,4 +72,13 @@ example_stream_run(void) { } } printf("Parsing completed\r\n"); + printf("data: k1 = '%s'\r\n", data.k1); + for(int i = 0; i < data.k2_pos; i++) { + printf("data: k2[%d] = %d\r\n", i, data.k2[i]); + } } + +int main(void) { + example_stream_run(); + return 0; +} \ No newline at end of file diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 698eec1..1a925b5 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -200,6 +200,7 @@ typedef struct lwjson_stream_parser { lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */ + void *user_data; /*!< User data for callback function */ /* State */ union { struct { @@ -222,7 +223,7 @@ typedef struct lwjson_stream_parser { char prev_c; /*!< History of characters */ } lwjson_stream_parser_t; -lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); +lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data); lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 8927dce..e6d711d 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -138,10 +138,11 @@ prv_stack_get_top(lwjson_stream_parser_t* jsp) { * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise */ lwjsonr_t -lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn) { +lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data) { memset(jsp, 0x00, sizeof(*jsp)); jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; jsp->evt_fn = evt_fn; + jsp->user_data = user_data; return lwjsonOK; } From 0109d70cfd10cf71751c8b5e5fb6264b71921c94 Mon Sep 17 00:00:00 2001 From: erics Date: Sun, 3 Sep 2023 11:44:44 +0300 Subject: [PATCH 06/28] create new init function to avoid breaking the API --- dev/main.c | 2 +- examples/example_stream.c | 2 +- lwjson/src/include/lwjson/lwjson.h | 3 ++- lwjson/src/lwjson/lwjson_stream.c | 7 ++++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dev/main.c b/dev/main.c index 45efd37..e293e6e 100644 --- a/dev/main.c +++ b/dev/main.c @@ -68,7 +68,7 @@ main() { } /* Now parse as a stream */ - lwjson_stream_init(&stream_parser, jsp_stream_callback,NULL); + lwjson_stream_init(&stream_parser, jsp_stream_callback); for (const char* str = json_text; str != NULL && *str != '\0'; ++str) { lwjsonr_t res = lwjson_stream_parse(&stream_parser, *str); if (res == lwjsonSTREAMWAITFIRSTCHAR) { diff --git a/examples/example_stream.c b/examples/example_stream.c index c6ed6dc..46a9591 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -56,7 +56,7 @@ example_stream_run(void) { data.k2 = k2_buff; data.k2_len = sizeof(k2_buff); data.k2_pos = 0; - lwjson_stream_init(&stream_parser, prv_example_callback_func, &data); + lwjson_stream_init_with_user_data(&stream_parser, prv_example_callback_func, &data); /* Demonstrate as stream inputs */ for (const char* c = json_str; *c != '\0'; ++c) { diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 1a925b5..26d537a 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -223,7 +223,8 @@ typedef struct lwjson_stream_parser { char prev_c; /*!< History of characters */ } lwjson_stream_parser_t; -lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data); +lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); +lwjsonr_t lwjson_stream_init_with_user_data(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data); lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index e6d711d..05bc6f4 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -138,7 +138,7 @@ prv_stack_get_top(lwjson_stream_parser_t* jsp) { * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise */ lwjsonr_t -lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data) { +lwjson_stream_init_with_user_data(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data) { memset(jsp, 0x00, sizeof(*jsp)); jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; jsp->evt_fn = evt_fn; @@ -146,6 +146,11 @@ lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn return lwjsonOK; } +lwjsonr_t +lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn) +{ + return lwjson_stream_init_with_user_data(jsp, evt_fn, NULL); +} /** * \brief Reset LwJSON stream structure * From 5b7308ca721a515cc483e86b42a74ee113182033 Mon Sep 17 00:00:00 2001 From: Eric Sidorov Date: Tue, 5 Sep 2023 13:58:51 +0300 Subject: [PATCH 07/28] use add_user_data function instead of a seconf init function --- examples/example_stream.c | 4 ++-- lwjson/src/include/lwjson/lwjson.h | 2 +- lwjson/src/lwjson/lwjson_stream.c | 22 +++++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/example_stream.c b/examples/example_stream.c index 46a9591..efeed15 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -56,8 +56,8 @@ example_stream_run(void) { data.k2 = k2_buff; data.k2_len = sizeof(k2_buff); data.k2_pos = 0; - lwjson_stream_init_with_user_data(&stream_parser, prv_example_callback_func, &data); - + lwjson_stream_init(&stream_parser, prv_example_callback_func); + lwjson_stream_set_user_data(&stream_parser, &data); /* Demonstrate as stream inputs */ for (const char* c = json_str; *c != '\0'; ++c) { res = lwjson_stream_parse(&stream_parser, *c); diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 26d537a..b8c58cf 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -224,7 +224,7 @@ typedef struct lwjson_stream_parser { } lwjson_stream_parser_t; lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); -lwjsonr_t lwjson_stream_init_with_user_data(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data); +void lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data); lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 05bc6f4..21052e5 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -138,19 +138,15 @@ prv_stack_get_top(lwjson_stream_parser_t* jsp) { * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise */ lwjsonr_t -lwjson_stream_init_with_user_data(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn, void* user_data) { +lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn) { + memset(jsp, 0x00, sizeof(*jsp)); jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; jsp->evt_fn = evt_fn; - jsp->user_data = user_data; + jsp->user_data = NULL; return lwjsonOK; } -lwjsonr_t -lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn) -{ - return lwjson_stream_init_with_user_data(jsp, evt_fn, NULL); -} /** * \brief Reset LwJSON stream structure * @@ -161,9 +157,21 @@ lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp) { jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; jsp->stack_pos = 0; + jsp->user_data = NULL; return lwjsonOK; } +/** + * \brief set user_data in stream parser + * + * \param jsp: LwJSON stream parser + * \param user_data: user data +*/ +void +lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data) { + jsp->user_data = user_data; +} + /** * \brief Parse JSON string in streaming mode * \param[in,out] jsp: Stream JSON structure From 94f90dd83adee1d051ba4bcd2b3693de18fbf2a4 Mon Sep 17 00:00:00 2001 From: erics Date: Tue, 5 Sep 2023 14:15:53 +0300 Subject: [PATCH 08/28] example for stream with user data --- examples/example_stream.c | 40 +---------- examples/example_stream_with_user_data.c | 84 ++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 38 deletions(-) create mode 100644 examples/example_stream_with_user_data.c diff --git a/examples/example_stream.c b/examples/example_stream.c index efeed15..17ea749 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -1,15 +1,8 @@ #include #include "lwjson/lwjson.h" -#include -typedef struct example_data_struct_t { - uint8_t k1[10]; - char *k2; - int k2_len; - int k2_pos; -} example_data_struct_t; /* Test string to parser */ -static const char* json_str = "{\"k1\":\"v1\",\"k2\":[true, false, true]}"; +static const char* json_str = "{\"k1\":\"v1\",\"k2\":[true, false]}"; /* LwJSON stream parser */ static lwjson_stream_parser_t stream_parser; @@ -22,26 +15,11 @@ static lwjson_stream_parser_t stream_parser; void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { /* Get a value corresponsing to "k1" key */ - example_data_struct_t* data = (example_data_struct_t *)jsp->user_data; - if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ && strcmp(jsp->stack[1].meta.name, "k1") == 0) { printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); - strncpy((char *)data->k1, jsp->data.str.buff, sizeof(data->k1) - 1); - } - if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ - && strcmp(jsp->stack[1].meta.name, "k2") == 0) { - printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); - if (jsp->stack_pos >= 3 - && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[2].meta.index < data->k2_len) { - printf("Got array value '%s' index = %d \r\n", jsp->data.str.buff, jsp->stack[2].meta.index); - data->k2[jsp->stack[2].meta.index] = (strncmp(jsp->data.str.buff, "true", 4) == 0); - data->k2_pos = jsp->stack[2].meta.index + 1; - } } (void)type; } @@ -51,13 +29,8 @@ void example_stream_run(void) { lwjsonr_t res; printf("\r\n\r\nParsing stream\r\n"); - example_data_struct_t data; - char k2_buff[10]; - data.k2 = k2_buff; - data.k2_len = sizeof(k2_buff); - data.k2_pos = 0; lwjson_stream_init(&stream_parser, prv_example_callback_func); - lwjson_stream_set_user_data(&stream_parser, &data); + /* Demonstrate as stream inputs */ for (const char* c = json_str; *c != '\0'; ++c) { res = lwjson_stream_parse(&stream_parser, *c); @@ -72,13 +45,4 @@ example_stream_run(void) { } } printf("Parsing completed\r\n"); - printf("data: k1 = '%s'\r\n", data.k1); - for(int i = 0; i < data.k2_pos; i++) { - printf("data: k2[%d] = %d\r\n", i, data.k2[i]); - } } - -int main(void) { - example_stream_run(); - return 0; -} \ No newline at end of file diff --git a/examples/example_stream_with_user_data.c b/examples/example_stream_with_user_data.c new file mode 100644 index 0000000..efeed15 --- /dev/null +++ b/examples/example_stream_with_user_data.c @@ -0,0 +1,84 @@ +#include +#include "lwjson/lwjson.h" +#include +typedef struct example_data_struct_t { + uint8_t k1[10]; + char *k2; + int k2_len; + int k2_pos; +} example_data_struct_t; + +/* Test string to parser */ +static const char* json_str = "{\"k1\":\"v1\",\"k2\":[true, false, true]}"; + +/* LwJSON stream parser */ +static lwjson_stream_parser_t stream_parser; + +/** + * \brief Callback function for various events + * \param jsp: JSON stream parser object + * \param type: Event type + */ +void +prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + /* Get a value corresponsing to "k1" key */ + example_data_struct_t* data = (example_data_struct_t *)jsp->user_data; + + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ + && strcmp(jsp->stack[1].meta.name, "k1") == 0) { + printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); + strncpy((char *)data->k1, jsp->data.str.buff, sizeof(data->k1) - 1); + } + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ + && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ + && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ + && strcmp(jsp->stack[1].meta.name, "k2") == 0) { + printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); + if (jsp->stack_pos >= 3 + && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[2].meta.index < data->k2_len) { + printf("Got array value '%s' index = %d \r\n", jsp->data.str.buff, jsp->stack[2].meta.index); + data->k2[jsp->stack[2].meta.index] = (strncmp(jsp->data.str.buff, "true", 4) == 0); + data->k2_pos = jsp->stack[2].meta.index + 1; + } + } + (void)type; +} + +/* Parse JSON */ +void +example_stream_run(void) { + lwjsonr_t res; + printf("\r\n\r\nParsing stream\r\n"); + example_data_struct_t data; + char k2_buff[10]; + data.k2 = k2_buff; + data.k2_len = sizeof(k2_buff); + data.k2_pos = 0; + lwjson_stream_init(&stream_parser, prv_example_callback_func); + lwjson_stream_set_user_data(&stream_parser, &data); + /* Demonstrate as stream inputs */ + for (const char* c = json_str; *c != '\0'; ++c) { + res = lwjson_stream_parse(&stream_parser, *c); + if (res == lwjsonSTREAMINPROG) { + } else if (res == lwjsonSTREAMWAITFIRSTCHAR) { + printf("Waiting first character\r\n"); + } else if (res == lwjsonSTREAMDONE) { + printf("Done\r\n"); + } else { + printf("Error\r\n"); + break; + } + } + printf("Parsing completed\r\n"); + printf("data: k1 = '%s'\r\n", data.k1); + for(int i = 0; i < data.k2_pos; i++) { + printf("data: k2[%d] = %d\r\n", i, data.k2[i]); + } +} + +int main(void) { + example_stream_run(); + return 0; +} \ No newline at end of file From 24d44e9f24c406370314f253b85b1424d7b4cfe5 Mon Sep 17 00:00:00 2001 From: erics Date: Tue, 5 Sep 2023 14:28:29 +0300 Subject: [PATCH 09/28] add getter function for user data --- examples/example_stream_with_user_data.c | 4 ++-- lwjson/src/include/lwjson/lwjson.h | 3 ++- lwjson/src/lwjson/lwjson_stream.c | 14 +++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/examples/example_stream_with_user_data.c b/examples/example_stream_with_user_data.c index efeed15..f50830d 100644 --- a/examples/example_stream_with_user_data.c +++ b/examples/example_stream_with_user_data.c @@ -21,8 +21,8 @@ static lwjson_stream_parser_t stream_parser; */ void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { - /* Get a value corresponsing to "k1" key */ - example_data_struct_t* data = (example_data_struct_t *)jsp->user_data; + //Get the data struct from user data + example_data_struct_t* data = (example_data_struct_t *)lwjson_stream_get_user_data(jsp); if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index b8c58cf..98e5b48 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -224,7 +224,8 @@ typedef struct lwjson_stream_parser { } lwjson_stream_parser_t; lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); -void lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data); +lwjsonr_t lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data); +void *lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 21052e5..3279d88 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -167,9 +167,21 @@ lwjson_stream_reset(lwjson_stream_parser_t* jsp) { * \param jsp: LwJSON stream parser * \param user_data: user data */ -void +lwjsonr_t lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data) { jsp->user_data = user_data; + return lwjsonOK; +} + +/** + * \brief get user_data in stream parser + * + * \param jsp: LwJSON stream parser + * \return pointer user data + * */ +void * +lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp) { + return jsp->user_data; } /** From 62cb23b805502c6d556104c5e52d466822a902ba Mon Sep 17 00:00:00 2001 From: erics Date: Wed, 6 Sep 2023 12:17:49 +0300 Subject: [PATCH 10/28] small fixes after review --- examples/example_stream_with_user_data.c | 2 +- lwjson/src/lwjson/lwjson_stream.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/example_stream_with_user_data.c b/examples/example_stream_with_user_data.c index f50830d..1f69d23 100644 --- a/examples/example_stream_with_user_data.c +++ b/examples/example_stream_with_user_data.c @@ -22,7 +22,7 @@ static lwjson_stream_parser_t stream_parser; void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { //Get the data struct from user data - example_data_struct_t* data = (example_data_struct_t *)lwjson_stream_get_user_data(jsp); + example_data_struct_t* data = lwjson_stream_get_user_data(jsp); if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 3279d88..24b0a65 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -177,8 +177,8 @@ lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data) { * \brief get user_data in stream parser * * \param jsp: LwJSON stream parser - * \return pointer user data - * */ + * \return pointer to user data + */ void * lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp) { return jsp->user_data; From c2f2776e33dfce9e130950902a76d36a6a1835ef Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Mon, 20 Nov 2023 18:28:59 +0100 Subject: [PATCH 11/28] added clang-tidy --- .clang-tidy | 19 +++ .vscode/settings.json | 6 +- CHANGELOG.md | 2 + CMakeLists.txt | 2 + dev/lwjson_opts.h | 10 +- lwjson/src/include/lwjson/lwjson_opt.h | 18 +++ lwjson/src/lwjson/lwjson.c | 160 ++++++++++++++----------- lwjson/src/lwjson/lwjson_debug.c | 20 ++-- lwjson/src/lwjson/lwjson_stream.c | 130 ++++++++++---------- 9 files changed, 217 insertions(+), 150 deletions(-) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..1aaa846 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,19 @@ +--- +Checks: "*, + -abseil-*, + -altera-*, + -android-*, + -fuchsia-*, + -google-*, + -llvm*, + -modernize-use-trailing-return-type, + -zircon-*, + -readability-else-after-return, + -readability-static-accessed-through-instance, + -readability-avoid-const-params-in-decls, + -cppcoreguidelines-non-private-member-variables-in-classes, + -misc-non-private-member-variables-in-classes, +" +WarningsAsErrors: '' +HeaderFilterRegex: '' +FormatStyle: none \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e696cf0..0efcc4a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,9 @@ "lwevt.h": "c", "string.h": "c", "lwevt_opt.h": "c", - "lwjson.h": "c" + "lwjson.h": "c", + "lwjson_opt.h": "c" }, - "esbonio.sphinx.confDir": "" + "esbonio.sphinx.confDir": "", + "C_Cpp.codeAnalysis.clangTidy.useBuildPath": true } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a645d0c..2d89675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Develop +- Add clang-tidy + ## 1.6.1 - Fix critical issue - missing correct return when waiting for first character. Should be `lwjsonSTREAMWAITFIRSTCHAR` diff --git a/CMakeLists.txt b/CMakeLists.txt index 02fa3a3..07f877a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ project(LwLibPROJECT) if(NOT PROJECT_IS_TOP_LEVEL) add_subdirectory(lwjson) else() + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + # Set as executable add_executable(${PROJECT_NAME}) diff --git a/dev/lwjson_opts.h b/dev/lwjson_opts.h index 519a5f8..0574a38 100644 --- a/dev/lwjson_opts.h +++ b/dev/lwjson_opts.h @@ -31,8 +31,8 @@ * Author: Tilen MAJERLE * Version: v1.6.1 */ -#ifndef LWJSON_HDR_OPTS_H -#define LWJSON_HDR_OPTS_H +#ifndef LWJSON_OPTS_HDR_H +#define LWJSON_OPTS_HDR_H /* Rename this file to "lwjson_opts.h" for your application */ @@ -40,7 +40,7 @@ * Open "include/lwjson/lwjson_opt.h" and * copy & replace here settings you want to change values */ -#define LWJSON_CFG_JSON5 1 -#define LWJSON_CFG_COMMENTS 1 +#define LWJSON_CFG_JSON5 1 +#define LWJSON_CFG_COMMENTS 1 -#endif /* LWJSON_HDR_OPTS_H */ +#endif /* LWJSON_OPTS_HDR_H */ diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index 9a14710..18d6e76 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -81,6 +81,24 @@ extern "C" { #define LWJSON_CFG_COMMENTS 0 #endif +/** + * \brief Memory set function + * + * \note Function footprint is the same as \ref memset + */ +#ifndef LWJSON_MEMSET +#define LWJSON_MEMSET(dst, val, len) memset((dst), (val), (len)) +#endif + +/** + * \brief Memory copy function + * + * \note Function footprint is the same as \ref memcpy + */ +#ifndef LWJSON_MEMCPY +#define LWJSON_MEMCPY(dst, src, len) memcpy((dst), (src), (len)) +#endif + /** * \defgroup LWJSON_OPT_STREAM JSON stream * \brief JSON streaming confiuration diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index 263fd03..4e6155e 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -51,7 +51,7 @@ typedef struct { static lwjson_token_t* prv_alloc_token(lwjson_t* lwobj) { if (lwobj->next_free_token_pos < lwobj->tokens_len) { - memset(&lwobj->tokens[lwobj->next_free_token_pos], 0x00, sizeof(*lwobj->tokens)); + LWJSON_MEMSET(&lwobj->tokens[lwobj->next_free_token_pos], 0x00, sizeof(*lwobj->tokens)); return &lwobj->tokens[lwobj->next_free_token_pos++]; } return NULL; @@ -108,7 +108,8 @@ prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) { lwjsonr_t res; size_t len = 0; - if ((res = prv_skip_blank(pobj)) != lwjsonOK) { + res = prv_skip_blank(pobj); + if (res != lwjsonOK) { return res; } if (*pobj->p++ != '"') { @@ -125,13 +126,13 @@ prv_parse_string(lwjson_int_str_t* pobj, const char** pout, size_t* poutlen) { ++pobj->p; ++len; switch (*pobj->p) { - case '"': - case '\\': - case '/': - case 'b': - case 'f': - case 'n': - case 'r': + case '"': /* fallthrough */ + case '\\': /* fallthrough */ + case '/': /* fallthrough */ + case 'b': /* fallthrough */ + case 'f': /* fallthrough */ + case 'n': /* fallthrough */ + case 'r': /* fallthrough */ case 't': break; case 'u': ++pobj->p; @@ -168,11 +169,13 @@ prv_parse_property_name(lwjson_int_str_t* pobj, lwjson_token_t* t) { lwjsonr_t res; /* Parse property string first */ - if ((res = prv_parse_string(pobj, &t->token_name, &t->token_name_len)) != lwjsonOK) { + res = prv_parse_string(pobj, &t->token_name, &t->token_name_len); + if (res != lwjsonOK) { return res; } /* Skip any spaces */ - if ((res = prv_skip_blank(pobj)) != lwjsonOK) { + res = prv_skip_blank(pobj); + if (res != lwjsonOK) { return res; } /* Must continue with colon */ @@ -180,7 +183,8 @@ prv_parse_property_name(lwjson_int_str_t* pobj, lwjson_token_t* t) { return lwjsonERRJSON; } /* Skip any spaces */ - if ((res = prv_skip_blank(pobj)) != lwjsonOK) { + res = prv_skip_blank(pobj); + if (res != lwjsonOK) { return res; } return lwjsonOK; @@ -202,7 +206,8 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou lwjson_int_t int_num = 0; lwjson_type_t type = LWJSON_TYPE_NUM_INT; - if ((res = prv_skip_blank(pobj)) != lwjsonOK) { + res = prv_skip_blank(pobj); + if (res != lwjsonOK) { return res; } if (*pobj->p == '\0' || (size_t)(pobj->p - pobj->start) >= pobj->len) { @@ -218,13 +223,14 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou /* Parse number */ for (int_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p) { - int_num = int_num * 10 + (*pobj->p - '0'); + int_num = int_num * (lwjson_int_t)10 + (*pobj->p - '0'); } real_num = (lwjson_real_t)int_num; if (pobj->p != NULL && *pobj->p == '.') { /* Number has exponent */ - lwjson_real_t exp, dec_num; + lwjson_real_t exp; + lwjson_int_t dec_num; real_num = (lwjson_real_t)int_num; @@ -233,15 +239,19 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou if (*pobj->p < '0' || *pobj->p > '9') { /* Must be followed by number characters */ return lwjsonERRJSON; } + /* Get number after decimal point */ - for (exp = 1, dec_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p, exp *= 10) { - dec_num = dec_num * 10 + (*pobj->p - '0'); + for (exp = (lwjson_real_t)1, dec_num = 0; *pobj->p >= '0' && *pobj->p <= '9'; + ++pobj->p, exp *= (lwjson_real_t)10) { + dec_num = dec_num * (lwjson_int_t)10 + (lwjson_int_t)(*pobj->p - '0'); } - real_num += dec_num / exp; /* Add decimal part to number */ + + /* Add decimal part to number */ + real_num += (lwjson_real_t)dec_num / exp; } if (pobj->p != NULL && (*pobj->p == 'e' || *pobj->p == 'E')) { /* Engineering mode */ uint8_t is_minus_exp; - int exp_cnt; + lwjson_int_t exp_cnt; type = LWJSON_TYPE_NUM_REAL; /* Format is real */ ++pobj->p; /* Ignore enginnering sing part */ @@ -255,13 +265,15 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou /* Parse exponent number */ for (exp_cnt = 0; *pobj->p >= '0' && *pobj->p <= '9'; ++pobj->p) { - exp_cnt = exp_cnt * 10 + (*pobj->p - '0'); + exp_cnt = exp_cnt * (lwjson_int_t)10 + (lwjson_int_t)(*pobj->p - '0'); } + /* Calculate new value for exponent 10^exponent */ + /* TODO: We could change this to lookup tables... */ if (is_minus_exp) { - for (; exp_cnt > 0; real_num /= 10, --exp_cnt) {} + for (; exp_cnt > 0; real_num /= (lwjson_real_t)10, --exp_cnt) {} } else { - for (; exp_cnt > 0; real_num *= 10, --exp_cnt) {} + for (; exp_cnt > 0; real_num *= (lwjson_real_t)10, --exp_cnt) {} } } @@ -279,22 +291,22 @@ prv_parse_number(lwjson_int_str_t* pobj, lwjson_type_t* tout, lwjson_real_t* fou /** * \brief Create path segment from input path for search operation - * \param[in,out] p: Pointer to pointer to input path. Pointer is modified + * \param[in,out] ppath: Pointer to pointer to input path. Pointer is modified * \param[out] opath: Pointer to pointer to write path segment * \param[out] olen: Pointer to variable to write length of segment * \param[out] is_last: Pointer to write if this is last segment * \return `1` on success, `0` otherwise */ static uint8_t -prv_create_path_segment(const char** p, const char** opath, size_t* olen, uint8_t* is_last) { - const char* s = *p; +prv_create_path_segment(const char** ppath, const char** opath, size_t* olen, uint8_t* is_last) { + const char* segment = *ppath; *is_last = 0; *opath = NULL; *olen = 0; /* Check input path */ - if (s == NULL || *s == '\0') { + if (segment == NULL || *segment == '\0') { *is_last = 1; return 0; } @@ -304,13 +316,13 @@ prv_create_path_segment(const char** p, const char** opath, size_t* olen, uint8_ * - literal text * - "#" followed by dot "." */ - if (*s == '#') { - *opath = s; - for (*olen = 0;; ++s, ++(*olen)) { - if (*s == '.') { - ++s; + if (*segment == '#') { + *opath = segment; + for (*olen = 0;; ++segment, ++(*olen)) { + if (*segment == '.') { + ++segment; break; - } else if (*s == '\0') { + } else if (*segment == '\0') { if (*olen == 1) { return 0; } else { @@ -318,13 +330,13 @@ prv_create_path_segment(const char** p, const char** opath, size_t* olen, uint8_ } } } - *p = s; + *ppath = segment; } else { - *opath = s; - for (*olen = 0; *s != '\0' && *s != '.'; ++(*olen), ++s) {} - *p = s + 1; + *opath = segment; + for (*olen = 0; *segment != '\0' && *segment != '.'; ++(*olen), ++segment) {} + *ppath = segment + 1; } - if (*s == '\0') { + if (*segment == '\0') { *is_last = 1; } return 1; @@ -340,10 +352,11 @@ static const lwjson_token_t* prv_find(const lwjson_token_t* parent, const char* path) { const char* segment; size_t segment_len; - uint8_t is_last, result; + uint8_t is_last, res; /* Get path segments */ - if ((result = prv_create_path_segment(&path, &segment, &segment_len, &is_last)) != 0) { + res = prv_create_path_segment(&path, &segment, &segment_len, &is_last); + if (res != 0) { /* Check if detected an array request */ if (*segment == '#') { /* Parent must be array */ @@ -353,7 +366,7 @@ prv_find(const lwjson_token_t* parent, const char* path) { /* Check if index requested */ if (segment_len > 1) { - const lwjson_token_t* t; + const lwjson_token_t* tkn; size_t index = 0; /* Parse number */ @@ -366,35 +379,37 @@ prv_find(const lwjson_token_t* parent, const char* path) { } /* Start from beginning */ - for (t = parent->u.first_child; t != NULL && index > 0; t = t->next, --index) {} - if (t != NULL) { + for (tkn = parent->u.first_child; tkn != NULL && index > 0; tkn = tkn->next, --index) {} + if (tkn != NULL) { if (is_last) { - return t; + return tkn; } else { - return prv_find(t, path); + return prv_find(tkn, path); } } return NULL; } /* Scan all indexes and get first match */ - for (const lwjson_token_t *tmp_t, *t = parent->u.first_child; t != NULL; t = t->next) { - if ((tmp_t = prv_find(t, path)) != NULL) { - return tmp_t; + for (const lwjson_token_t* tkn = parent->u.first_child; tkn != NULL; tkn = tkn->next) { + const lwjson_token_t* tmp = prv_find(tkn, path); + if (tmp != NULL) { + return tmp; } } } else { if (parent->type != LWJSON_TYPE_OBJECT) { return NULL; } - for (const lwjson_token_t* t = parent->u.first_child; t != NULL; t = t->next) { - if (t->token_name_len == segment_len && !strncmp(t->token_name, segment, segment_len)) { - const lwjson_token_t* tmp_t; + for (const lwjson_token_t* tkn = parent->u.first_child; tkn != NULL; tkn = tkn->next) { + if (tkn->token_name_len == segment_len && !strncmp(tkn->token_name, segment, segment_len)) { + const lwjson_token_t* tmp; if (is_last) { - return t; + return tkn; } - if ((tmp_t = prv_find(t, path)) != NULL) { - return tmp_t; + tmp = prv_find(tkn, path); + if (tmp != NULL) { + return tmp; } } } @@ -414,7 +429,8 @@ prv_check_valid_char_after_open_bracket(lwjson_int_str_t* pobj, lwjson_token_t* lwjsonr_t res; /* Check next character after object open */ - if ((res = prv_skip_blank(pobj)) != lwjsonOK) { + res = prv_skip_blank(pobj); + if (res != lwjsonOK) { return res; } if (*pobj->p == '\0' || (t->type == LWJSON_TYPE_OBJECT && (*pobj->p != '"' && *pobj->p != '}')) @@ -435,8 +451,8 @@ prv_check_valid_char_after_open_bracket(lwjson_int_str_t* pobj, lwjson_token_t* */ lwjsonr_t lwjson_init(lwjson_t* lwobj, lwjson_token_t* tokens, size_t tokens_len) { - memset(lwobj, 0x00, sizeof(*lwobj)); - memset(tokens, 0x00, sizeof(*tokens) * tokens_len); + LWJSON_MEMSET(lwobj, 0x00, sizeof(*lwobj)); + LWJSON_MEMSET(tokens, 0x00, sizeof(*tokens) * tokens_len); lwobj->tokens = tokens; lwobj->tokens_len = tokens_len; lwobj->first_token.type = LWJSON_TYPE_OBJECT; @@ -469,10 +485,11 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { /* values from very beginning */ lwobj->flags.parsed = 0; lwobj->next_free_token_pos = 0; - memset(to, 0x00, sizeof(*to)); + LWJSON_MEMSET(to, 0x00, sizeof(*to)); /* First parse */ - if ((res = prv_skip_blank(&pobj)) != lwjsonOK) { + res = prv_skip_blank(&pobj); + if (res != lwjsonOK) { goto ret; } if (*pobj.p == '{') { @@ -484,14 +501,16 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { goto ret; } ++pobj.p; - if ((res = prv_check_valid_char_after_open_bracket(&pobj, to)) != lwjsonOK) { + res = prv_check_valid_char_after_open_bracket(&pobj, to); + if (res != lwjsonOK) { goto ret; } /* Process all characters as indicated by input user */ while (pobj.p != NULL && *pobj.p != '\0' && (size_t)(pobj.p - pobj.start) < pobj.len) { /* Filter out blanks */ - if ((res = prv_skip_blank(&pobj)) != lwjsonOK) { + res = prv_skip_blank(&pobj); + if (res != lwjsonOK) { goto ret; } if (*pobj.p == ',') { @@ -506,7 +525,8 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { ++pobj.p; /* End of string if to == NULL (no parent), check if properly terminated */ - if ((to = parent) == NULL) { + to = parent; + if (to == NULL) { prv_skip_blank(&pobj); res = (pobj.p == NULL || *pobj.p == '\0' || (size_t)(pobj.p - pobj.start) == pobj.len) ? lwjsonOK : lwjsonERR; @@ -516,7 +536,8 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { } /* Allocate new token */ - if ((t = prv_alloc_token(lwobj)) == NULL) { + t = prv_alloc_token(lwobj); + if (t == NULL) { res = lwjsonERRMEM; goto ret; } @@ -527,7 +548,8 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { res = lwjsonERRJSON; goto ret; } - if ((res = prv_parse_property_name(&pobj, t)) != lwjsonOK) { + res = prv_parse_property_name(&pobj, t); + if (res != lwjsonOK) { goto ret; } } @@ -547,14 +569,17 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { case '[': t->type = *pobj.p == '{' ? LWJSON_TYPE_OBJECT : LWJSON_TYPE_ARRAY; ++pobj.p; - if ((res = prv_check_valid_char_after_open_bracket(&pobj, t)) != lwjsonOK) { + + res = prv_check_valid_char_after_open_bracket(&pobj, t); + if (res != lwjsonOK) { goto ret; } t->next = to; /* Temporary saved as parent object */ to = t; break; case '"': - if ((res = prv_parse_string(&pobj, &t->u.str.token_value, &t->u.str.token_value_len)) == lwjsonOK) { + res = prv_parse_string(&pobj, &t->u.str.token_value, &t->u.str.token_value_len); + if (res == lwjsonOK) { t->type = LWJSON_TYPE_STRING; } else { goto ret; @@ -617,7 +642,8 @@ lwjson_parse_ex(lwjson_t* lwobj, const void* json_data, size_t json_len) { * - End of array indication * - End of object indication */ - if ((res = prv_skip_blank(&pobj)) != lwjsonOK) { + res = prv_skip_blank(&pobj); + if (res != lwjsonOK) { goto ret; } /* Check if valid string is availabe after */ @@ -665,7 +691,7 @@ lwjson_parse(lwjson_t* lwobj, const char* json_str) { */ lwjsonr_t lwjson_free(lwjson_t* lwobj) { - memset(lwobj->tokens, 0x00, sizeof(*lwobj->tokens) * lwobj->tokens_len); + LWJSON_MEMSET(lwobj->tokens, 0x00, sizeof(*lwobj->tokens) * lwobj->tokens_len); lwobj->flags.parsed = 0; return lwjsonOK; } diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index b3c260b..78b03f7 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -44,12 +44,12 @@ typedef struct { /** * \brief Print token value - * \param[in] p: Token print instance + * \param[in] prt: Token print instance * \param[in] token: Token to print */ static void -prv_print_token(lwjson_token_print_t* p, const lwjson_token_t* token) { -#define print_indent() printf("%.*s", (int)((p->indent)), "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); +prv_print_token(lwjson_token_print_t* prt, const lwjson_token_t* token) { +#define print_indent() printf("%.*s", (int)((prt->indent)), "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"); if (token == NULL) { return; @@ -68,11 +68,11 @@ prv_print_token(lwjson_token_print_t* p, const lwjson_token_t* token) { printf("%c", token->type == LWJSON_TYPE_OBJECT ? '{' : '['); if (token->u.first_child != NULL) { printf("\n"); - ++p->indent; + ++prt->indent; for (const lwjson_token_t* t = lwjson_get_first_child(token); t != NULL; t = t->next) { - prv_print_token(p, t); + prv_print_token(prt, t); } - --p->indent; + --prt->indent; print_indent(); } printf("%c", token->type == LWJSON_TYPE_OBJECT ? '}' : ']'); @@ -117,8 +117,8 @@ prv_print_token(lwjson_token_print_t* p, const lwjson_token_t* token) { */ void lwjson_print_token(const lwjson_token_t* token) { - lwjson_token_print_t p = {0}; - prv_print_token(&p, token); + lwjson_token_print_t prt = {0}; + prv_print_token(&prt, token); } /** @@ -128,6 +128,6 @@ lwjson_print_token(const lwjson_token_t* token) { */ void lwjson_print_json(const lwjson_t* lwobj) { - lwjson_token_print_t p = {0}; - prv_print_token(&p, lwjson_get_first_token(lwobj)); + lwjson_token_print_t prt = {0}; + prv_print_token(&prt, lwjson_get_first_token(lwobj)); } diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 24b0a65..e043527 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -47,7 +47,7 @@ } while (0) /* Strings for debug */ -static const char* type_strings[] = { +static const char* const type_strings[] = { [LWJSON_STREAM_TYPE_NONE] = "none", [LWJSON_STREAM_TYPE_OBJECT] = "object", [LWJSON_STREAM_TYPE_OBJECT_END] = "object_end", @@ -106,15 +106,16 @@ prv_stack_push(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { static lwjson_stream_type_t prv_stack_pop(lwjson_stream_parser_t* jsp) { if (jsp->stack_pos > 0) { - lwjson_stream_type_t t = jsp->stack[--jsp->stack_pos].type; + lwjson_stream_type_t type = jsp->stack[--jsp->stack_pos].type; + jsp->stack[jsp->stack_pos].type = LWJSON_STREAM_TYPE_NONE; - LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[t]); + LWJSON_DEBUG(jsp, "Popped from stack: %s\r\n", type_strings[type]); /* Take care of array to indicate number of entries */ if (jsp->stack_pos > 0 && jsp->stack[jsp->stack_pos - 1].type == LWJSON_STREAM_TYPE_ARRAY) { jsp->stack[jsp->stack_pos - 1].meta.index++; } - return t; + return type; } return LWJSON_STREAM_TYPE_NONE; } @@ -139,8 +140,7 @@ prv_stack_get_top(lwjson_stream_parser_t* jsp) { */ lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn) { - - memset(jsp, 0x00, sizeof(*jsp)); + LWJSON_MEMSET(jsp, 0x00, sizeof(*jsp)); jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; jsp->evt_fn = evt_fn; jsp->user_data = NULL; @@ -150,36 +150,36 @@ lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn /** * \brief Reset LwJSON stream structure * - * \param jsp: LwJSON stream parser + * \param[in,out] jsp: LwJSON stream parser * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise */ lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp) { jsp->parse_state = LWJSON_STREAM_STATE_WAITINGFIRSTCHAR; jsp->stack_pos = 0; - jsp->user_data = NULL; return lwjsonOK; } /** - * \brief set user_data in stream parser + * \brief Set user_data in stream parser * - * \param jsp: LwJSON stream parser - * \param user_data: user data -*/ -lwjsonr_t + * \param[in,out] jsp: LwJSON stream parser + * \param[in] user_data: user data + * \return \ref lwjsonOK on success, member of \ref lwjsonr_t otherwise + */ +lwjsonr_t lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data) { jsp->user_data = user_data; return lwjsonOK; } /** - * \brief get user_data in stream parser + * \brief Get user_data in stream parser * - * \param jsp: LwJSON stream parser + * \param[in] jsp: LwJSON stream parser * \return pointer to user data */ -void * +void* lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp) { return jsp->user_data; } @@ -187,23 +187,21 @@ lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp) { /** * \brief Parse JSON string in streaming mode * \param[in,out] jsp: Stream JSON structure - * \param[in] c: Character to parse + * \param[in] chr: Character to parse * \return \ref lwjsonSTREAMWAITFIRSTCHAR when stream did not start parsing since no valid start character has been received * \return \ref lwjsonSTREAMINPROG if parsing is in progress and no hard error detected * \return \ref lwjsonSTREAMDONE when valid JSON was detected and stack level reached back `0` level * \return \ref One of enumeration otherwise */ lwjsonr_t -lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { +lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { /* Get first character first */ - if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR && c != '{' && c != '[') { + if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR && chr != '{' && chr != '[') { return lwjsonSTREAMWAITFIRSTCHAR; } start_over: - /* - * Determine what to do from parsing state - */ + /* Determine what to do from parsing state */ switch (jsp->parse_state) { /* @@ -213,27 +211,27 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR: case LWJSON_STREAM_STATE_PARSING: { /* Determine start of object or an array */ - if (c == '{' || c == '[') { + if (chr == '{' || chr == '[') { /* Reset stack pointer if this character came from waiting for first character */ if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR) { jsp->stack_pos = 0; } - if (!prv_stack_push(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { + if (!prv_stack_push(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); return lwjsonERRMEM; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; - SEND_EVT(jsp, c == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); + SEND_EVT(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); /* Determine end of object or an array */ - } else if (c == '}' || c == ']') { - lwjson_stream_type_t t = prv_stack_get_top(jsp); + } else if (chr == '}' || chr == ']') { + lwjson_stream_type_t type = prv_stack_get_top(jsp); /* * If it is a key last entry on closing area, * it is an error - an example: {"key":} */ - if (t == LWJSON_STREAM_TYPE_KEY) { + if (type == LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG(jsp, "ERROR - key should not be followed by ] without value for a key\r\n"); return lwjsonERRJSON; } @@ -242,9 +240,10 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * Check if closing character matches stack value * Avoid cases like: {"key":"value"] or ["v1", "v2", "v3"} */ - if ((c == '}' && t != LWJSON_STREAM_TYPE_OBJECT) || (c == ']' && t != LWJSON_STREAM_TYPE_ARRAY)) { - LWJSON_DEBUG(jsp, "ERROR - closing character '%c' does not match stack element \"%s\"\r\n", c, - type_strings[t]); + if ((chr == '}' && type != LWJSON_STREAM_TYPE_OBJECT) + || (chr == ']' && type != LWJSON_STREAM_TYPE_ARRAY)) { + LWJSON_DEBUG(jsp, "ERROR - closing character '%c' does not match stack element \"%s\"\r\n", chr, + type_strings[type]); return lwjsonERRJSON; } @@ -262,7 +261,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); } - SEND_EVT(jsp, c == '}' ? LWJSON_STREAM_TYPE_OBJECT_END : LWJSON_STREAM_TYPE_ARRAY_END); + SEND_EVT(jsp, chr == '}' ? LWJSON_STREAM_TYPE_OBJECT_END : LWJSON_STREAM_TYPE_ARRAY_END); /* If that is the end of JSON */ if (jsp->stack_pos == 0) { @@ -270,41 +269,41 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } /* Determine start of string - can be key or regular string (in array or after key) */ - } else if (c == '"') { + } else if (chr == '"') { #if defined(LWJSON_DEV) - lwjson_stream_type_t t = prv_stack_get_top(jsp); - if (t == LWJSON_STREAM_TYPE_OBJECT) { + lwjson_stream_type_t type = prv_stack_get_top(jsp); + if (type == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "Start of string parsing - expected key name in an object\r\n"); - } else if (t == LWJSON_STREAM_TYPE_KEY) { + } else if (type == LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG(jsp, "Start of string parsing - string value associated to previous key in an object\r\n"); - } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + } else if (type == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "Start of string parsing - string entry in an array\r\n"); } #endif /* defined(LWJSON_DEV) */ jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; - memset(&jsp->data.str, 0x00, sizeof(jsp->data.str)); + LWJSON_MEMSET(&jsp->data.str, 0x00, sizeof(jsp->data.str)); /* Check for end of key character */ - } else if (c == ':') { - lwjson_stream_type_t t = prv_stack_get_top(jsp); + } else if (chr == ':') { + lwjson_stream_type_t type = prv_stack_get_top(jsp); /* * Color can only be followed by key on the stack * * It is clear JSON error if this is not the case */ - if (t != LWJSON_STREAM_TYPE_KEY) { + if (type != LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); return lwjsonERRJSON; } /* Check if this is start of number or "true", "false" or "null" */ - } else if (c == '-' || (c >= '0' && c <= '9') || c == 't' || c == 'f' || c == 'n') { + } else if (chr == '-' || (chr >= '0' && chr <= '9') || chr == 't' || chr == 'f' || chr == 'n') { LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, First char: %c\r\n", - (c == '-' || (c >= '0' && c <= '9')) ? "number" : "true,false,null", c); + (chr == '-' || (chr >= '0' && chr <= '9')) ? "number" : "true,false,null", chr); jsp->parse_state = LWJSON_STREAM_STATE_PARSING_PRIMITIVE; - memset(&jsp->data.prim, 0x00, sizeof(jsp->data.prim)); - jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; + LWJSON_MEMSET(&jsp->data.prim, 0x00, sizeof(jsp->data.prim)); + jsp->data.prim.buff[jsp->data.prim.buff_pos++] = chr; } break; } @@ -315,7 +314,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * It is used for key or string in an object or an array */ case LWJSON_STREAM_STATE_PARSING_STRING: { - lwjson_stream_type_t t = prv_stack_get_top(jsp); + lwjson_stream_type_t type = prv_stack_get_top(jsp); /* * Quote character may trigger end of string, @@ -323,15 +322,15 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * * TODO: Handle backslash */ - if (c == '"' && jsp->prev_c != '\\') { + if (chr == '"' && jsp->prev_c != '\\') { #if defined(LWJSON_DEV) - if (t == LWJSON_STREAM_TYPE_OBJECT) { + if (type == LWJSON_STREAM_TYPE_OBJECT) { LWJSON_DEBUG(jsp, "End of string parsing - object key name: \"%s\"\r\n", jsp->data.str.buff); - } else if (t == LWJSON_STREAM_TYPE_KEY) { + } else if (type == LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG( jsp, "End of string parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.str.buff); - } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + } else if (type == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "End of string parsing - an array string entry: \"%s\"\r\n", jsp->data.str.buff); } #endif /* defined(LWJSON_DEV) */ @@ -344,31 +343,31 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * When top of stack is a key - string is a value for a key - notify user and pop the value for key * When top of stack is an array - string is one type - notify user and don't do anything */ - if (t == LWJSON_STREAM_TYPE_OBJECT) { + if (type == LWJSON_STREAM_TYPE_OBJECT) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_KEY); if (prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { size_t len = jsp->data.str.buff_pos; if (len > (sizeof(jsp->stack[0].meta.name) - 1)) { len = sizeof(jsp->stack[0].meta.name) - 1; } - memcpy(jsp->stack[jsp->stack_pos - 1].meta.name, jsp->data.str.buff, len); + LWJSON_MEMCPY(jsp->stack[jsp->stack_pos - 1].meta.name, jsp->data.str.buff, len); jsp->stack[jsp->stack_pos - 1].meta.name[len] = '\0'; } else { LWJSON_DEBUG(jsp, "Cannot push key to stack\r\n"); return lwjsonERRMEM; } - } else if (t == LWJSON_STREAM_TYPE_KEY) { + } else if (type == LWJSON_STREAM_TYPE_KEY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); prv_stack_pop(jsp); /* Next character to wait for is either space or comma or end of object */ - } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + } else if (type == LWJSON_STREAM_TYPE_ARRAY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); jsp->stack[jsp->stack_pos - 1].meta.index++; } jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { /* TODO: Check other backslash elements */ - jsp->data.str.buff[jsp->data.str.buff_pos++] = c; + jsp->data.str.buff[jsp->data.str.buff_pos++] = chr; jsp->data.str.buff_total_pos++; /* Handle buffer "overflow" */ @@ -379,7 +378,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { * - For array or key types - following one is always string * - For object type - character is key */ - SEND_EVT(jsp, (t == LWJSON_STREAM_TYPE_KEY || t == LWJSON_STREAM_TYPE_ARRAY) + SEND_EVT(jsp, (type == LWJSON_STREAM_TYPE_KEY || type == LWJSON_STREAM_TYPE_ARRAY) ? LWJSON_STREAM_TYPE_STRING : LWJSON_STREAM_TYPE_KEY); jsp->data.str.buff_pos = 0; @@ -395,22 +394,22 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { */ case LWJSON_STREAM_STATE_PARSING_PRIMITIVE: { /* Any character except space, comma, or end of array/object are valid */ - if (!prv_is_space_char_ext(c) && c != ',' && c != ']' && c != '}') { + if (!prv_is_space_char_ext(chr) && chr != ',' && chr != ']' && chr != '}') { if (jsp->data.prim.buff_pos < sizeof(jsp->data.prim.buff) - 1) { - jsp->data.prim.buff[jsp->data.prim.buff_pos++] = c; + jsp->data.prim.buff[jsp->data.prim.buff_pos++] = chr; } } else { - lwjson_stream_type_t t = prv_stack_get_top(jsp); + lwjson_stream_type_t type = prv_stack_get_top(jsp); #if defined(LWJSON_DEV) - if (t == LWJSON_STREAM_TYPE_OBJECT) { + if (type == LWJSON_STREAM_TYPE_OBJECT) { /* TODO: Handle error - primitive cannot be just after object */ - } else if (t == LWJSON_STREAM_TYPE_KEY) { + } else if (type == LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG( jsp, "End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n", jsp->data.prim.buff); - } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + } else if (type == LWJSON_STREAM_TYPE_ARRAY) { LWJSON_DEBUG(jsp, "End of primitive parsing - an array string entry: \"%s\"\r\n", jsp->data.prim.buff); } @@ -438,9 +437,9 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { } else { LWJSON_DEBUG(jsp, "Invalid primitive type. Got: %s\r\n", jsp->data.prim.buff); } - if (t == LWJSON_STREAM_TYPE_KEY) { + if (type == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); - } else if (t == LWJSON_STREAM_TYPE_ARRAY) { + } else if (type == LWJSON_STREAM_TYPE_ARRAY) { jsp->stack[jsp->stack_pos - 1].meta.index++; } @@ -455,9 +454,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c) { break; } - /* TODO: Add other case statements */ default: break; } - jsp->prev_c = c; /* Save current c as previous for next round */ + jsp->prev_c = chr; /* Save current c as previous for next round */ return lwjsonSTREAMINPROG; } From f81b994efcd404e934ce69eb873dee9c3c6455bf Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 28 Nov 2023 18:45:11 +0100 Subject: [PATCH 12/28] Add include path --- library.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library.json b/library.json index 758bb8f..575b795 100644 --- a/library.json +++ b/library.json @@ -29,5 +29,8 @@ "build", "**/build" ] + }, + "build": { + "includeDir": "lwjson/src/include" } } \ No newline at end of file From a3318c7d5850a08b09d86a9d15d8b61169599e19 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 1 Dec 2023 17:11:02 +0100 Subject: [PATCH 13/28] Update docs --- docs/get-started/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index b0bfc41..2bc21a8 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -70,7 +70,7 @@ Configuration file ^^^^^^^^^^^^^^^^^^ Configuration file is used to overwrite default settings defined for the essential use case. -Library comes with template config file, which can be modified according to needs. +Library comes with template config file, which can be modified according to the application needs. and it should be copied (or simply renamed in-place) and named ``lwjson_opts.h`` .. note:: From b4130fcf82bdde15b647dd82a07dd3da4ea53ab7 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 8 Dec 2023 16:03:45 +0100 Subject: [PATCH 14/28] Improve docs and CMake configure_file --- CMakeLists.txt | 1 + docs/get-started/index.rst | 10 +++++++++- lwjson/CMakeLists.txt | 13 +++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07f877a..560a7af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ else() ) # Add subdir with lwjson and link to project + set(LWJSON_OPTS_DIR ${CMAKE_CURRENT_LIST_DIR}/dev) add_subdirectory(lwjson) target_link_libraries(${PROJECT_NAME} lwjson) target_link_libraries(${PROJECT_NAME} lwjson_debug) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index 2bc21a8..71df654 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -66,6 +66,10 @@ Next step is to add the library to the project, by means of source files to comp * Copy ``lwjson/src/include/lwjson/lwjson_opts_template.h`` to project folder and rename it to ``lwjson_opts.h`` * Build the project +.. tip:: + If you are using *CMake* build system, you can add the library to the project by adding the *library's folder* + directory with ``add_directory()`` CMake command, followed by linking the target with ``target_link_libraries()`` + Configuration file ^^^^^^^^^^^^^^^^^^ @@ -78,7 +82,11 @@ and it should be copied (or simply renamed in-place) and named ``lwjson_opts.h`` File must be renamed to ``lwjson_opts.h`` first and then copied to the project directory where compiler include paths have access to it by using ``#include "lwjson_opts.h"``. -List of configuration options are available in the :ref:`api_lwjson_opt` section. +.. tip:: + If you are using *CMake* build system, define the variable ``LWJSON_OPTS_DIR`` before adding library's directory to the *CMake* project. + Variable must set the output directory path. CMake will copy the template file there, and name it as required. + +Configuration options list is available available in the :ref:`api_lwjson_opt` section. If any option is about to be modified, it should be done in configuration file .. literalinclude:: ../../lwjson/src/include/lwjson/lwjson_opts_template.h diff --git a/lwjson/CMakeLists.txt b/lwjson/CMakeLists.txt index e4dd94f..d89c5cb 100644 --- a/lwjson/CMakeLists.txt +++ b/lwjson/CMakeLists.txt @@ -4,17 +4,17 @@ cmake_minimum_required(VERSION 3.22) set(lwjson_core_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_stream.c - ) +) # Debug sources set(lwjson_debug_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_debug.c - ) +) # Setup include directories set(lwjson_include_DIRS ${CMAKE_CURRENT_LIST_DIR}/src/include - ) +) # Register core library to the system add_library(lwjson INTERFACE) @@ -24,4 +24,9 @@ target_include_directories(lwjson INTERFACE ${lwjson_include_DIRS}) # Register lwjson debug module add_library(lwjson_debug INTERFACE) target_sources(lwjson_debug PUBLIC ${lwjson_debug_SRCS}) -target_include_directories(lwjson_debug INTERFACE ${lwjson_include_DIRS}) \ No newline at end of file +target_include_directories(lwjson_debug INTERFACE ${lwjson_include_DIRS}) + +# Create config file +if(DEFINED LWJSON_OPTS_DIR AND NOT EXISTS ${LWJSON_OPTS_DIR}/lwjson_opts.h) + configure_file(${CMAKE_CURRENT_LIST_DIR}/src/include/lwjson/lwjson_opts_template.h ${LWJSON_OPTS_DIR}/lwjson_opts.h COPYONLY) +endif() \ No newline at end of file From 29e34ff621ff843df5d5d1b04e95cba0a6070498 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 9 Dec 2023 11:10:19 +0100 Subject: [PATCH 15/28] Create .cmake file to allow higher flexibility of end user --- lwjson/CMakeLists.txt | 31 +------------------------------ lwjson/library.cmake | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 30 deletions(-) create mode 100644 lwjson/library.cmake diff --git a/lwjson/CMakeLists.txt b/lwjson/CMakeLists.txt index d89c5cb..f05f91d 100644 --- a/lwjson/CMakeLists.txt +++ b/lwjson/CMakeLists.txt @@ -1,32 +1,3 @@ cmake_minimum_required(VERSION 3.22) -# Setup generic source files -set(lwjson_core_SRCS - ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c - ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_stream.c -) - -# Debug sources -set(lwjson_debug_SRCS - ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_debug.c -) - -# Setup include directories -set(lwjson_include_DIRS - ${CMAKE_CURRENT_LIST_DIR}/src/include -) - -# Register core library to the system -add_library(lwjson INTERFACE) -target_sources(lwjson PUBLIC ${lwjson_core_SRCS}) -target_include_directories(lwjson INTERFACE ${lwjson_include_DIRS}) - -# Register lwjson debug module -add_library(lwjson_debug INTERFACE) -target_sources(lwjson_debug PUBLIC ${lwjson_debug_SRCS}) -target_include_directories(lwjson_debug INTERFACE ${lwjson_include_DIRS}) - -# Create config file -if(DEFINED LWJSON_OPTS_DIR AND NOT EXISTS ${LWJSON_OPTS_DIR}/lwjson_opts.h) - configure_file(${CMAKE_CURRENT_LIST_DIR}/src/include/lwjson/lwjson_opts_template.h ${LWJSON_OPTS_DIR}/lwjson_opts.h COPYONLY) -endif() \ No newline at end of file +include(${CMAKE_CURRENT_LIST_DIR}/library.cmake) \ No newline at end of file diff --git a/lwjson/library.cmake b/lwjson/library.cmake new file mode 100644 index 0000000..7c0941b --- /dev/null +++ b/lwjson/library.cmake @@ -0,0 +1,34 @@ +# Setup generic source files +set(lwjson_core_SRCS + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_stream.c +) + +# Debug sources +set(lwjson_debug_SRCS + ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson_debug.c +) + +# Setup include directories +set(lwjson_include_DIRS + ${CMAKE_CURRENT_LIST_DIR}/src/include +) + +# Register core library to the system +add_library(lwjson INTERFACE) +target_sources(lwjson PUBLIC ${lwjson_core_SRCS}) +target_include_directories(lwjson INTERFACE ${lwjson_include_DIRS}) +target_compile_options(lwjson PRIVATE ${LWJSON_COMPILE_OPTIONS}) +target_compile_definitions(lwjson PRIVATE ${LWJSON_COMPILE_DEFINITIONS}) + +# Register lwjson debug module +add_library(lwjson_debug INTERFACE) +target_sources(lwjson_debug PUBLIC ${lwjson_debug_SRCS}) +target_include_directories(lwjson_debug INTERFACE ${lwjson_include_DIRS}) +target_compile_options(lwjson_debug PRIVATE ${LWJSON_COMPILE_OPTIONS}) +target_compile_definitions(lwjson_debug PRIVATE ${LWJSON_COMPILE_DEFINITIONS}) + +# Create config file +if(DEFINED LWJSON_OPTS_DIR AND NOT EXISTS ${LWJSON_OPTS_DIR}/lwjson_opts.h) + configure_file(${CMAKE_CURRENT_LIST_DIR}/src/include/lwjson/lwjson_opts_template.h ${LWJSON_OPTS_DIR}/lwjson_opts.h COPYONLY) +endif() \ No newline at end of file From cbb991bce97ad5ffec739688c149ef3e80306e50 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 9 Dec 2023 16:49:22 +0100 Subject: [PATCH 16/28] Improve the cmake documentation --- docs/get-started/index.rst | 16 +++++++++++----- lwjson/library.cmake | 11 +++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index 71df654..205f8b9 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -58,7 +58,17 @@ Add library to project ^^^^^^^^^^^^^^^^^^^^^^ At this point it is assumed that you have successfully download library, either cloned it or from releases page. -Next step is to add the library to the project, by means of source files to compiler inputs and header files in search path +Next step is to add the library to the project, by means of source files to compiler inputs and header files in search path. + +*CMake* is the main supported build system. Package comes with the ``CMakeLists.txt`` and ``library.cmake`` files, both located in the ``lwjson`` directory: + +* ``CMakeLists.txt``: Is a wrapper and only includes ``library.cmake`` file. It is used if target application uses ``add_subdirectory`` and then uses ``target_link_libraries`` to include the library in the project +* ``library.cmake``: It is a fully configured set of variables. User must use ``include(path/to/library.cmake)`` to include the library and must manually add files/includes to the final target + +.. tip:: + Open ``library.cmake`` file and manually analyze all the possible variables you can set for full functionality. + +If you do not use the *CMake*, you can do the following: * Copy ``lwjson`` folder to your project, it contains library files * Add ``lwjson/src/include`` folder to `include path` of your toolchain. This is where `C/C++` compiler can find the files during compilation process. Usually using ``-I`` flag @@ -66,10 +76,6 @@ Next step is to add the library to the project, by means of source files to comp * Copy ``lwjson/src/include/lwjson/lwjson_opts_template.h`` to project folder and rename it to ``lwjson_opts.h`` * Build the project -.. tip:: - If you are using *CMake* build system, you can add the library to the project by adding the *library's folder* - directory with ``add_directory()`` CMake command, followed by linking the target with ``target_link_libraries()`` - Configuration file ^^^^^^^^^^^^^^^^^^ diff --git a/lwjson/library.cmake b/lwjson/library.cmake index 7c0941b..9e7bc25 100644 --- a/lwjson/library.cmake +++ b/lwjson/library.cmake @@ -1,3 +1,14 @@ +# +# This file provides set of variables for end user +# and also generates one (or more) libraries, that can be added to the project using target_link_libraries(...) +# +# Before this file is included to the root CMakeLists file (using include() function), user can set some variables: +# +# LWJSON_OPTS_DIR: If defined, it should set the folder path where options file shall be generated. +# LWJSON_COMPILE_OPTIONS: If defined, it provide compiler options for generated library. +# LWJSON_COMPILE_DEFINITIONS: If defined, it provides "-D" definitions to the library build +# + # Setup generic source files set(lwjson_core_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c From edf6b247a856c577fefb9433f221cbdfa7896c1c Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 9 Dec 2023 17:06:39 +0100 Subject: [PATCH 17/28] Add AUTHORS file --- AUTHORS | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 AUTHORS diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..99e09c5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Tilen Majerle +Tilen Majerle +erics +Eric Sidorov +erics +Tristen Pierson \ No newline at end of file From 8c2165384bd2733f495cae17fff050d50d24fec6 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Sat, 9 Dec 2023 17:13:46 +0100 Subject: [PATCH 18/28] Add authors in the docs file --- docs/authors/index.rst | 8 ++++++++ docs/index.rst | 1 + 2 files changed, 9 insertions(+) create mode 100644 docs/authors/index.rst diff --git a/docs/authors/index.rst b/docs/authors/index.rst new file mode 100644 index 0000000..4031a8c --- /dev/null +++ b/docs/authors/index.rst @@ -0,0 +1,8 @@ +.. _authors: + +Authors +======= + +List of authors and contributors to the library + +.. literalinclude:: ../../AUTHORS \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index bfb8c6f..41372a8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -73,6 +73,7 @@ Table of contents user-manual/index api-reference/index changelog/index + authors/index .. toctree:: :maxdepth: 2 From 12a9510c2f0101598d0fd9b0d98bd4284ea41653 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Thu, 4 Jan 2024 21:39:27 +0100 Subject: [PATCH 19/28] Update copyright to 2024 & AUTHORS file --- dev/lwjson_opts.h | 2 +- lwjson/src/include/lwjson/lwjson.h | 2 +- lwjson/src/include/lwjson/lwjson_opt.h | 2 +- lwjson/src/include/lwjson/lwjson_opts_template.h | 2 +- lwjson/src/lwjson/lwjson.c | 2 +- lwjson/src/lwjson/lwjson_debug.c | 2 +- lwjson/src/lwjson/lwjson_stream.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/dev/lwjson_opts.h b/dev/lwjson_opts.h index 0574a38..a7c1721 100644 --- a/dev/lwjson_opts.h +++ b/dev/lwjson_opts.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 98e5b48..c3b9ab3 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index 18d6e76..da5f7cc 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/include/lwjson/lwjson_opts_template.h b/lwjson/src/include/lwjson/lwjson_opts_template.h index 9f279a9..2971f0a 100644 --- a/lwjson/src/include/lwjson/lwjson_opts_template.h +++ b/lwjson/src/include/lwjson/lwjson_opts_template.h @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index 4e6155e..232ae59 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index 78b03f7..09680ff 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index e043527..a1322de 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -4,7 +4,7 @@ */ /* - * Copyright (c) 2023 Tilen MAJERLE + * Copyright (c) 2024 Tilen MAJERLE * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation From 42375641e4ff82466d57d7756f6504ee85f26e00 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 27 Feb 2024 21:20:23 +0100 Subject: [PATCH 20/28] Update license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 3702ca4..42c1b5c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Tilen MAJERLE +Copyright (c) 2024 Tilen MAJERLE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 5b83b110fc2ee731fd2cf3dc8b82cf60e1910ee6 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 6 Mar 2024 09:46:54 +0100 Subject: [PATCH 21/28] add helper functions for stream parsing --- CHANGELOG.md | 1 + lwjson/src/include/lwjson/lwjson.h | 51 ++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d89675..3220b68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Develop - Add clang-tidy +- Add helper functions for sequence check in stream parsing ## 1.6.1 diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index c3b9ab3..e735d3d 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -200,7 +200,8 @@ typedef struct lwjson_stream_parser { lwjson_stream_parser_callback_fn evt_fn; /*!< Event function for user */ - void *user_data; /*!< User data for callback function */ + void* user_data; /*!< User data for callback function */ + /* State */ union { struct { @@ -225,7 +226,7 @@ typedef struct lwjson_stream_parser { lwjsonr_t lwjson_stream_init(lwjson_stream_parser_t* jsp, lwjson_stream_parser_callback_fn evt_fn); lwjsonr_t lwjson_stream_set_user_data(lwjson_stream_parser_t* jsp, void* user_data); -void *lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp); +void* lwjson_stream_get_user_data(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_reset(lwjson_stream_parser_t* jsp); lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char c); @@ -324,6 +325,52 @@ lwjson_string_compare_n(const lwjson_token_t* token, const char* str, size_t len return 0; } +/** + * \name LWJSON_STREAM_SEQ + * \brief Helper functions for stack analysis in a callback function + * \note Useful exclusively for streaming functions + * \{ + */ + +/** + * \brief Check the sequence of JSON stack, starting from start_number index + * \note This applies only to one sequence element. Other macros, starting with + * `lwjson_stack_seq_X` (where X is the sequence length), provide + * more parameters for longer sequences. + * + * \param[in] jsp: LwJSON stream instance + * \param[in] start_num: Start number in the stack. Typically starts with `0`, but user may choose another + * number, if intention is to check partial sequence only + * \param[in] sp0: Stream stack type. Value of \ref lwjson_stream_type_t, but only last part of the enum. + * If user is interested in the \ref LWJSON_STREAM_TYPE_OBJECT, + * you should only write `OBJECT` as parameter. + * Idea behind is to make code smaller and easier to read, especially when + * using other sequence values with more parameters. + * \return `0` if sequence doesn't match, non-zero otherwise + */ +#define lwjson_stack_seq_1(jsp, start_num, sp0) ((jsp)->stack[(start_num)].type == LWJSON_STREAM_TYPE_##sp0) +#define lwjson_stack_seq_2(jsp, start_num, sp0, sp1) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_1((jsp), (start_num) + 1, sp1)) +#define lwjson_stack_seq_3(jsp, start_num, sp0, sp1, sp2) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_2((jsp), (start_num) + 1, sp1, sp2)) +#define lwjson_stack_seq_4(jsp, start_num, sp0, sp1, sp2, sp3) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_3((jsp), (start_num) + 1, sp1, sp2, sp3)) +#define lwjson_stack_seq_5(jsp, start_num, sp0, sp1, sp2, sp3, sp4) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) && lwjson_stack_seq_4((jsp), (start_num) + 1, sp1, sp2, sp3, sp4)) +#define lwjson_stack_seq_6(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \ + && lwjson_stack_seq_5((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5)) +#define lwjson_stack_seq_7(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5, sp6) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \ + && lwjson_stack_seq_6((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5, sp6)) +#define lwjson_stack_seq_8(jsp, start_num, sp0, sp1, sp2, sp3, sp4, sp5, sp6, sp7) \ + (lwjson_stack_seq_1((jsp), (start_num) + 0, sp0) \ + && lwjson_stack_seq_7((jsp), (start_num) + 1, sp1, sp2, sp3, sp4, sp5, sp6, sp7)) + +/** + * \} + */ + /** * \} */ From f235fbd5512a5676ef7aedff23a53c8481f881e3 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Wed, 6 Mar 2024 09:47:31 +0100 Subject: [PATCH 22/28] add option for trial run -> place for a user to "experiment" with the parsing --- CMakeLists.txt | 2 + dev/main.c | 9 ++- examples/example_stream.c | 2 +- examples/example_stream_with_user_data.c | 18 +++--- trial_env/trial_run.c | 79 ++++++++++++++++++++++++ trial_env/trial_run.json | 3 + 6 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 trial_env/trial_run.c create mode 100644 trial_env/trial_run.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 560a7af..a33fc82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ else() ${CMAKE_CURRENT_LIST_DIR}/examples/example_minimal.c ${CMAKE_CURRENT_LIST_DIR}/examples/example_traverse.c ${CMAKE_CURRENT_LIST_DIR}/examples/example_stream.c + ${CMAKE_CURRENT_LIST_DIR}/trial_env/trial_run.c + ) # Add key include paths diff --git a/dev/main.c b/dev/main.c index e293e6e..f411dec 100644 --- a/dev/main.c +++ b/dev/main.c @@ -1,8 +1,8 @@ #include #include #include -#include "windows.h" #include "lwjson/lwjson.h" +#include "windows.h" /* Classic parser */ static lwjson_token_t tokens[4096]; @@ -16,6 +16,9 @@ extern void example_minimal_run(void); extern void example_traverse_run(void); extern void example_stream_run(void); +/* Trial stream -> for a user to try its own code... */ +extern void trial_stream_run(void); + static void jsp_stream_callback(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type); int @@ -27,6 +30,10 @@ main() { const lwjson_token_t* tkn; (void)token_cnt; +#if 1 + trial_stream_run(); + return 0; +#endif #if 0 test_run(); example_minimal_run(); diff --git a/examples/example_stream.c b/examples/example_stream.c index 17ea749..c826892 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -12,7 +12,7 @@ static lwjson_stream_parser_t stream_parser; * \param jsp: JSON stream parser object * \param type: Event type */ -void +static void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { /* Get a value corresponsing to "k1" key */ if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ diff --git a/examples/example_stream_with_user_data.c b/examples/example_stream_with_user_data.c index 1f69d23..9b6b875 100644 --- a/examples/example_stream_with_user_data.c +++ b/examples/example_stream_with_user_data.c @@ -1,9 +1,10 @@ #include -#include "lwjson/lwjson.h" #include +#include "lwjson/lwjson.h" + typedef struct example_data_struct_t { uint8_t k1[10]; - char *k2; + char* k2; int k2_len; int k2_pos; } example_data_struct_t; @@ -19,7 +20,7 @@ static lwjson_stream_parser_t stream_parser; * \param jsp: JSON stream parser object * \param type: Event type */ -void +static void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { //Get the data struct from user data example_data_struct_t* data = lwjson_stream_get_user_data(jsp); @@ -29,15 +30,15 @@ prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ && strcmp(jsp->stack[1].meta.name, "k1") == 0) { printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); - strncpy((char *)data->k1, jsp->data.str.buff, sizeof(data->k1) - 1); + strncpy((char*)data->k1, jsp->data.str.buff, sizeof(data->k1) - 1); } if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ && strcmp(jsp->stack[1].meta.name, "k2") == 0) { printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); - if (jsp->stack_pos >= 3 - && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY && jsp->stack[2].meta.index < data->k2_len) { + if (jsp->stack_pos >= 3 && jsp->stack[2].type == LWJSON_STREAM_TYPE_ARRAY + && jsp->stack[2].meta.index < data->k2_len) { printf("Got array value '%s' index = %d \r\n", jsp->data.str.buff, jsp->stack[2].meta.index); data->k2[jsp->stack[2].meta.index] = (strncmp(jsp->data.str.buff, "true", 4) == 0); data->k2_pos = jsp->stack[2].meta.index + 1; @@ -73,12 +74,13 @@ example_stream_run(void) { } printf("Parsing completed\r\n"); printf("data: k1 = '%s'\r\n", data.k1); - for(int i = 0; i < data.k2_pos; i++) { + for (int i = 0; i < data.k2_pos; i++) { printf("data: k2[%d] = %d\r\n", i, data.k2[i]); } } -int main(void) { +int +main(void) { example_stream_run(); return 0; } \ No newline at end of file diff --git a/trial_env/trial_run.c b/trial_env/trial_run.c new file mode 100644 index 0000000..1c441b5 --- /dev/null +++ b/trial_env/trial_run.c @@ -0,0 +1,79 @@ +#include +#include "lwjson/lwjson.h" +#include "windows.h" + +/* LwJSON stream parser */ +static lwjson_stream_parser_t stream_parser; + +/** + * \brief Callback function for various events + * \param jsp: JSON stream parser object + * \param type: Event type + */ +static void +prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { + /* IO device part of the parsing goes here */ + if (jsp->stack_pos == 4 && lwjson_stack_seq_4(jsp, 0, OBJECT, KEY, OBJECT, KEY)) { + } else if (jsp->stack_pos == 7 && lwjson_stack_seq_7(jsp, 0, OBJECT, KEY, OBJECT, KEY, ARRAY, OBJECT, KEY)) { + } + + (void)type; + /* ... */ +} + +/* Parse JSON */ +void +trial_stream_run(void) { + lwjsonr_t res; + HANDLE f; + DWORD file_size; + char* json_text = NULL; + + printf("\r\n\r\nTrial parsing test stream\r\n"); + lwjson_stream_init(&stream_parser, prv_example_callback_func); + + f = CreateFile(TEXT("trial_env/trial_run.json"), + GENERIC_READ, // open for reading + 0, // do not share + NULL, // no security + OPEN_EXISTING, // existing file only + FILE_ATTRIBUTE_NORMAL, // normal file + NULL); // no attr. template + + if (f == INVALID_HANDLE_VALUE) { + printf("Could not open file..\r\n"); + goto exit; + } + if ((file_size = GetFileSize(f, NULL)) == INVALID_FILE_SIZE) { + printf("Invalid file size..\r\n"); + goto exit; + } else if (file_size == 0) { + printf("File is empty..\r\n"); + goto exit; + } + if ((json_text = calloc((size_t)(file_size + 1), sizeof(*json_text))) == NULL) { + printf("Could not allocate memory..\r\n"); + goto exit; + } + if (ReadFile(f, json_text, file_size, NULL, NULL) == 0) { + printf("Could not read full file..\r\n"); + goto exit; + } + + /* Demonstrate as stream inputs */ + for (const char* c = json_text; *c != '\0'; ++c) { + res = lwjson_stream_parse(&stream_parser, *c); + if (res == lwjsonSTREAMINPROG) { + } else if (res == lwjsonSTREAMWAITFIRSTCHAR) { + printf("Waiting first character\r\n"); + } else if (res == lwjsonSTREAMDONE) { + printf("Done\r\n"); + } else { + printf("Error\r\n"); + break; + } + } +exit: + free(json_text); + printf("Parsing completed\r\n"); +} diff --git a/trial_env/trial_run.json b/trial_env/trial_run.json new file mode 100644 index 0000000..756a8f2 --- /dev/null +++ b/trial_env/trial_run.json @@ -0,0 +1,3 @@ +{ + "json": "value" +} \ No newline at end of file From e063773edde530b543d193676c050a8a32661146 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 19 Mar 2024 21:34:24 +0100 Subject: [PATCH 23/28] Update library CMake system, to support automatic creation of options file, if user doesn't provide one --- CMakeLists.txt | 4 +--- docs/get-started/index.rst | 4 ++-- lwjson/library.cmake | 20 +++++++++++++++----- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a33fc82..28164e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,6 @@ else() ${CMAKE_CURRENT_LIST_DIR}/examples/example_traverse.c ${CMAKE_CURRENT_LIST_DIR}/examples/example_stream.c ${CMAKE_CURRENT_LIST_DIR}/trial_env/trial_run.c - ) # Add key include paths @@ -32,7 +31,6 @@ else() WIN32 _DEBUG CONSOLE - LWJSON_DEV ) # Compiler options @@ -43,7 +41,7 @@ else() ) # Add subdir with lwjson and link to project - set(LWJSON_OPTS_DIR ${CMAKE_CURRENT_LIST_DIR}/dev) + set(LWJSON_OPTS_FILE ${CMAKE_CURRENT_LIST_DIR}/dev/lwjson_opts.h) add_subdirectory(lwjson) target_link_libraries(${PROJECT_NAME} lwjson) target_link_libraries(${PROJECT_NAME} lwjson_debug) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index 205f8b9..b5cd452 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -89,8 +89,8 @@ and it should be copied (or simply renamed in-place) and named ``lwjson_opts.h`` include paths have access to it by using ``#include "lwjson_opts.h"``. .. tip:: - If you are using *CMake* build system, define the variable ``LWJSON_OPTS_DIR`` before adding library's directory to the *CMake* project. - Variable must set the output directory path. CMake will copy the template file there, and name it as required. + If you are using *CMake* build system, define the variable ``LWJSON_OPTS_FILE`` before adding library's directory to the *CMake* project. + Variable must contain the path to the user options file. If not provided, one will be generated in the build directory. Configuration options list is available available in the :ref:`api_lwjson_opt` section. If any option is about to be modified, it should be done in configuration file diff --git a/lwjson/library.cmake b/lwjson/library.cmake index 9e7bc25..3cc8319 100644 --- a/lwjson/library.cmake +++ b/lwjson/library.cmake @@ -1,14 +1,19 @@ # +# LIB_PREFIX: LWJSON +# # This file provides set of variables for end user # and also generates one (or more) libraries, that can be added to the project using target_link_libraries(...) # # Before this file is included to the root CMakeLists file (using include() function), user can set some variables: # -# LWJSON_OPTS_DIR: If defined, it should set the folder path where options file shall be generated. +# LWJSON_OPTS_FILE: If defined, it is the path to the user options file. If not defined, one will be generated for you automatically # LWJSON_COMPILE_OPTIONS: If defined, it provide compiler options for generated library. # LWJSON_COMPILE_DEFINITIONS: If defined, it provides "-D" definitions to the library build # +# Custom include directory +set(LWJSON_CUSTOM_INC_DIR ${CMAKE_CURRENT_BINARY_DIR}/lib_inc) + # Setup generic source files set(lwjson_core_SRCS ${CMAKE_CURRENT_LIST_DIR}/src/lwjson/lwjson.c @@ -23,6 +28,7 @@ set(lwjson_debug_SRCS # Setup include directories set(lwjson_include_DIRS ${CMAKE_CURRENT_LIST_DIR}/src/include + ${LWJSON_CUSTOM_INC_DIR} ) # Register core library to the system @@ -39,7 +45,11 @@ target_include_directories(lwjson_debug INTERFACE ${lwjson_include_DIRS}) target_compile_options(lwjson_debug PRIVATE ${LWJSON_COMPILE_OPTIONS}) target_compile_definitions(lwjson_debug PRIVATE ${LWJSON_COMPILE_DEFINITIONS}) -# Create config file -if(DEFINED LWJSON_OPTS_DIR AND NOT EXISTS ${LWJSON_OPTS_DIR}/lwjson_opts.h) - configure_file(${CMAKE_CURRENT_LIST_DIR}/src/include/lwjson/lwjson_opts_template.h ${LWJSON_OPTS_DIR}/lwjson_opts.h COPYONLY) -endif() \ No newline at end of file +# Create config file if user didn't provide one info himself +if(NOT LWJSON_OPTS_FILE) + message(STATUS "Using default lwjson_opts.h file") + set(LWJSON_OPTS_FILE ${CMAKE_CURRENT_LIST_DIR}/src/include/lwjson/lwjson_opts_template.h) +else() + message(STATUS "Using custom lwjson_opts.h file from ${LWJSON_OPTS_FILE}") +endif() +configure_file(${LWJSON_OPTS_FILE} ${LWJSON_CUSTOM_INC_DIR}/lwjson_opts.h COPYONLY) From 87bc8dd95937befff13a72b323d818acf1f932ef Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 19 Mar 2024 21:41:47 +0100 Subject: [PATCH 24/28] Improve documentation --- docs/get-started/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/get-started/index.rst b/docs/get-started/index.rst index b5cd452..aa5c1d9 100644 --- a/docs/get-started/index.rst +++ b/docs/get-started/index.rst @@ -90,7 +90,7 @@ and it should be copied (or simply renamed in-place) and named ``lwjson_opts.h`` .. tip:: If you are using *CMake* build system, define the variable ``LWJSON_OPTS_FILE`` before adding library's directory to the *CMake* project. - Variable must contain the path to the user options file. If not provided, one will be generated in the build directory. + Variable must contain the path to the user options file. If not provided and to avoid build error, one will be generated in the build directory. Configuration options list is available available in the :ref:`api_lwjson_opt` section. If any option is about to be modified, it should be done in configuration file From 60e5cde8fcbf0786d0ed36d53d6cfd746ab9bc13 Mon Sep 17 00:00:00 2001 From: Josef Salda Date: Tue, 2 Apr 2024 14:08:41 +0200 Subject: [PATCH 25/28] The streaming parser now requires the presence of ',' and ':' characters. If during parsing it finds a character that should not be in the string, it does not ignore it. --- lwjson/src/include/lwjson/lwjson.h | 2 + lwjson/src/lwjson/lwjson_stream.c | 103 ++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index e735d3d..4d56a0d 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -177,6 +177,8 @@ typedef enum { LWJSON_STREAM_STATE_PARSING, /*!< In parsing of the first char state - detecting next character state */ LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */ LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */ + LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END, /*!< Expecting ',', '}' or ']' */ + LWJSON_STREAM_STATE_EXPECTING_COLON, /*!< Expecting ':' */ } lwjson_stream_state_t; /* Forward declaration */ diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index a1322de..2287c26 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -197,7 +197,7 @@ lwjsonr_t lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { /* Get first character first */ if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR && chr != '{' && chr != '[') { - return lwjsonSTREAMWAITFIRSTCHAR; + return prv_is_space_char_ext(chr) ? lwjsonSTREAMWAITFIRSTCHAR : lwjsonERRJSON; } start_over: @@ -209,19 +209,20 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { * that is used to indicate start of JSON stream */ case LWJSON_STREAM_STATE_WAITINGFIRSTCHAR: + case LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END: case LWJSON_STREAM_STATE_PARSING: { - /* Determine start of object or an array */ - if (chr == '{' || chr == '[') { - /* Reset stack pointer if this character came from waiting for first character */ - if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR) { - jsp->stack_pos = 0; - } - if (!prv_stack_push(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { - LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); - return lwjsonERRMEM; + /* Ignore whitespace chars */ + if (prv_is_space_char_ext(chr)) { + break; + + /* Determine value separator */ + } else if ( chr == ',') { + if (jsp->parse_state == LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END) { + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + } else { + LWJSON_DEBUG(jsp, "ERROR - ',' can only follow value\r\n"); + return lwjsonERRJSON; } - jsp->parse_state = LWJSON_STREAM_STATE_PARSING; - SEND_EVT(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); /* Determine end of object or an array */ } else if (chr == '}' || chr == ']') { @@ -268,6 +269,32 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { return lwjsonSTREAMDONE; } + jsp->parse_state = LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END; + + /* If comma or end was expected, then it is already error here */ + } else if (jsp->parse_state == LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END) { + LWJSON_DEBUG(jsp, "ERROR - ',', '}' or ']' was expected\r\n"); + return lwjsonERRJSON; + + /* Determine start of object or an array */ + } else if (chr == '{' || chr == '[') { + if (chr == '{' && prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_OBJECT) { + /* Key must be before value */ + LWJSON_DEBUG(jsp, "ERROR - key mas be befor value (object)\r\n"); + return lwjsonERRJSON; + } + + /* Reset stack pointer if this character came from waiting for first character */ + if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR) { + jsp->stack_pos = 0; + } + if (!prv_stack_push(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { + LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); + return lwjsonERRMEM; + } + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + SEND_EVT(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); + /* Determine start of string - can be key or regular string (in array or after key) */ } else if (chr == '"') { #if defined(LWJSON_DEV) @@ -284,30 +311,41 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; LWJSON_MEMSET(&jsp->data.str, 0x00, sizeof(jsp->data.str)); - /* Check for end of key character */ - } else if (chr == ':') { - lwjson_stream_type_t type = prv_stack_get_top(jsp); - - /* - * Color can only be followed by key on the stack - * - * It is clear JSON error if this is not the case - */ - if (type != LWJSON_STREAM_TYPE_KEY) { - LWJSON_DEBUG(jsp, "Error - wrong ':' character\r\n"); - return lwjsonERRJSON; - } /* Check if this is start of number or "true", "false" or "null" */ } else if (chr == '-' || (chr >= '0' && chr <= '9') || chr == 't' || chr == 'f' || chr == 'n') { + if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_OBJECT) { + LWJSON_DEBUG(jsp, "ERROR - key must be before value (primitive)\r\n"); + /* Key must be before value */ + return lwjsonERRJSON; + } + LWJSON_DEBUG(jsp, "Start of primitive parsing parsing - %s, First char: %c\r\n", (chr == '-' || (chr >= '0' && chr <= '9')) ? "number" : "true,false,null", chr); jsp->parse_state = LWJSON_STREAM_STATE_PARSING_PRIMITIVE; LWJSON_MEMSET(&jsp->data.prim, 0x00, sizeof(jsp->data.prim)); jsp->data.prim.buff[jsp->data.prim.buff_pos++] = chr; + + /* Wrong char */ + } else { + LWJSON_DEBUG(jsp, "ERROR - wrong char %c\r\n", chr); + return lwjsonERRJSON; } break; } + /* Check for end of key character i.e. name separator ':' */ + case LWJSON_STREAM_STATE_EXPECTING_COLON: + /* Ignore whitespace chars */ + if (prv_is_space_char_ext(chr)) { + break; + } else if (chr == ':') { + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + } else { + LWJSON_DEBUG(jsp, "ERROR - expecting ':'\r\n"); + return lwjsonERRJSON; + } + break; + /* * Parse any type of string in a sequence * @@ -343,6 +381,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { * When top of stack is a key - string is a value for a key - notify user and pop the value for key * When top of stack is an array - string is one type - notify user and don't do anything */ + jsp->parse_state = LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END; if (type == LWJSON_STREAM_TYPE_OBJECT) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_KEY); if (prv_stack_push(jsp, LWJSON_STREAM_TYPE_KEY)) { @@ -356,6 +395,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { LWJSON_DEBUG(jsp, "Cannot push key to stack\r\n"); return lwjsonERRMEM; } + jsp->parse_state = LWJSON_STREAM_STATE_EXPECTING_COLON; } else if (type == LWJSON_STREAM_TYPE_KEY) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); prv_stack_pop(jsp); @@ -364,7 +404,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_STRING); jsp->stack[jsp->stack_pos - 1].meta.index++; } - jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { /* TODO: Check other backslash elements */ jsp->data.str.buff[jsp->data.str.buff_pos++] = chr; @@ -397,14 +436,15 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { if (!prv_is_space_char_ext(chr) && chr != ',' && chr != ']' && chr != '}') { if (jsp->data.prim.buff_pos < sizeof(jsp->data.prim.buff) - 1) { jsp->data.prim.buff[jsp->data.prim.buff_pos++] = chr; + } else { + LWJSON_DEBUG(jsp, "Buffer overflow for primitive\r\n"); + return lwjsonERRJSON; } } else { lwjson_stream_type_t type = prv_stack_get_top(jsp); #if defined(LWJSON_DEV) - if (type == LWJSON_STREAM_TYPE_OBJECT) { - /* TODO: Handle error - primitive cannot be just after object */ - } else if (type == LWJSON_STREAM_TYPE_KEY) { + if (type == LWJSON_STREAM_TYPE_KEY) { LWJSON_DEBUG( jsp, "End of primitive parsing - string value associated to previous key in an object: \"%s\"\r\n", @@ -436,6 +476,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { SEND_EVT(jsp, LWJSON_STREAM_TYPE_NUMBER); } else { LWJSON_DEBUG(jsp, "Invalid primitive type. Got: %s\r\n", jsp->data.prim.buff); + return lwjsonERRJSON; } if (type == LWJSON_STREAM_TYPE_KEY) { prv_stack_pop(jsp); @@ -445,10 +486,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { /* * Received character is not part of the primitive and must be processed again - * - * Set state to default state and start from beginning */ - jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + jsp->parse_state = LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END; goto start_over; } break; From d8be96aeb789a30072c2b4292841dafecf8e8fa8 Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Tue, 2 Apr 2024 22:35:57 +0200 Subject: [PATCH 26/28] Discard invalid JSON --- AUTHORS | 7 ++++--- CHANGELOG.md | 1 + examples/example_stream.c | 6 ++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 99e09c5..9d3f670 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,6 +1,7 @@ Tilen Majerle Tilen Majerle -erics -Eric Sidorov +Tristen Pierson erics -Tristen Pierson \ No newline at end of file +Eric Sidorov +erics +Josef Salda \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3220b68..a085717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Add clang-tidy - Add helper functions for sequence check in stream parsing +- Add support to discard invalid JSON stream ## 1.6.1 diff --git a/examples/example_stream.c b/examples/example_stream.c index c826892..b3fad3b 100644 --- a/examples/example_stream.c +++ b/examples/example_stream.c @@ -15,10 +15,8 @@ static lwjson_stream_parser_t stream_parser; static void prv_example_callback_func(lwjson_stream_parser_t* jsp, lwjson_stream_type_t type) { /* Get a value corresponsing to "k1" key */ - if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ - && jsp->stack[0].type == LWJSON_STREAM_TYPE_OBJECT /* First must be object */ - && jsp->stack[1].type == LWJSON_STREAM_TYPE_KEY /* We need key to be before */ - && strcmp(jsp->stack[1].meta.name, "k1") == 0) { + if (jsp->stack_pos >= 2 /* Number of stack entries must be high */ + && lwjson_stack_seq_2(jsp, 0, OBJECT, KEY) && strcmp(jsp->stack[1].meta.name, "k1") == 0) { printf("Got key '%s' with value '%s'\r\n", jsp->stack[1].meta.name, jsp->data.str.buff); } (void)type; From 300e1e7a5d643c54f25edce49a0732bd49e1ae3b Mon Sep 17 00:00:00 2001 From: Josef Salda Date: Fri, 17 May 2024 15:12:25 +0200 Subject: [PATCH 27/28] Fixed {[ parentheses issue and reset after parsing. --- lwjson/src/lwjson/lwjson_stream.c | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index 2287c26..bc1dc96 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -266,6 +266,7 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { /* If that is the end of JSON */ if (jsp->stack_pos == 0) { + lwjson_stream_reset(jsp); return lwjsonSTREAMDONE; } @@ -276,25 +277,6 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { LWJSON_DEBUG(jsp, "ERROR - ',', '}' or ']' was expected\r\n"); return lwjsonERRJSON; - /* Determine start of object or an array */ - } else if (chr == '{' || chr == '[') { - if (chr == '{' && prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_OBJECT) { - /* Key must be before value */ - LWJSON_DEBUG(jsp, "ERROR - key mas be befor value (object)\r\n"); - return lwjsonERRJSON; - } - - /* Reset stack pointer if this character came from waiting for first character */ - if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR) { - jsp->stack_pos = 0; - } - if (!prv_stack_push(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { - LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); - return lwjsonERRMEM; - } - jsp->parse_state = LWJSON_STREAM_STATE_PARSING; - SEND_EVT(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); - /* Determine start of string - can be key or regular string (in array or after key) */ } else if (chr == '"') { #if defined(LWJSON_DEV) @@ -311,6 +293,24 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { jsp->parse_state = LWJSON_STREAM_STATE_PARSING_STRING; LWJSON_MEMSET(&jsp->data.str, 0x00, sizeof(jsp->data.str)); + } else if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_OBJECT) { + /* Key must be before value */ + LWJSON_DEBUG(jsp, "ERROR - key must be before value\r\n"); + return lwjsonERRJSON; + + /* Determine start of object or an array */ + } else if (chr == '{' || chr == '[') { + /* Reset stack pointer if this character came from waiting for first character */ + if (jsp->parse_state == LWJSON_STREAM_STATE_WAITINGFIRSTCHAR) { + jsp->stack_pos = 0; + } + if (!prv_stack_push(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY)) { + LWJSON_DEBUG(jsp, "Cannot push object/array to stack\r\n"); + return lwjsonERRMEM; + } + jsp->parse_state = LWJSON_STREAM_STATE_PARSING; + SEND_EVT(jsp, chr == '{' ? LWJSON_STREAM_TYPE_OBJECT : LWJSON_STREAM_TYPE_ARRAY); + /* Check if this is start of number or "true", "false" or "null" */ } else if (chr == '-' || (chr >= '0' && chr <= '9') || chr == 't' || chr == 'f' || chr == 'n') { if (prv_stack_get_top(jsp) == LWJSON_STREAM_TYPE_OBJECT) { From 46dc5f21e31879eaa9ebabc3b43f9c15d4623c3f Mon Sep 17 00:00:00 2001 From: Tilen Majerle Date: Fri, 17 May 2024 16:25:07 +0200 Subject: [PATCH 28/28] Add version 1.7.0 --- CHANGELOG.md | 3 + dev/lwjson_opts.h | 2 +- dev/main.c | 6 +- library.json | 2 +- lwjson/src/include/lwjson/lwjson.h | 4 +- lwjson/src/include/lwjson/lwjson_opt.h | 2 +- .../src/include/lwjson/lwjson_opts_template.h | 2 +- lwjson/src/lwjson/lwjson.c | 2 +- lwjson/src/lwjson/lwjson_debug.c | 2 +- lwjson/src/lwjson/lwjson_stream.c | 10 +- test/test.c | 259 +++++++++--------- 11 files changed, 152 insertions(+), 142 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a085717..c8c9f15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,12 @@ ## Develop +## 1.7.0 + - Add clang-tidy - Add helper functions for sequence check in stream parsing - Add support to discard invalid JSON stream +- Fixed some invalid JSON parsing in the streaming module ## 1.6.1 diff --git a/dev/lwjson_opts.h b/dev/lwjson_opts.h index a7c1721..b5ba723 100644 --- a/dev/lwjson_opts.h +++ b/dev/lwjson_opts.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #ifndef LWJSON_OPTS_HDR_H #define LWJSON_OPTS_HDR_H diff --git a/dev/main.c b/dev/main.c index f411dec..78f6bc9 100644 --- a/dev/main.c +++ b/dev/main.c @@ -30,15 +30,17 @@ main() { const lwjson_token_t* tkn; (void)token_cnt; -#if 1 +#if 0 trial_stream_run(); return 0; #endif -#if 0 +#if 1 test_run(); +#if 1 example_minimal_run(); example_traverse_run(); example_stream_run(); +#endif return 0; #endif diff --git a/library.json b/library.json index 575b795..db93cfc 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "LwJSON", - "version": "1.6.1", + "version": "1.7.0", "description": "Lightweight JSON parser for embedded systems with support for inline comments", "keywords": "json, javascript, lightweight, parser, stm32, manager, library, comment, object, notation, object notation", "repository": { diff --git a/lwjson/src/include/lwjson/lwjson.h b/lwjson/src/include/lwjson/lwjson.h index 4d56a0d..6a384c9 100644 --- a/lwjson/src/include/lwjson/lwjson.h +++ b/lwjson/src/include/lwjson/lwjson.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #ifndef LWJSON_HDR_H #define LWJSON_HDR_H @@ -178,7 +178,7 @@ typedef enum { LWJSON_STREAM_STATE_PARSING_STRING, /*!< Parse string primitive */ LWJSON_STREAM_STATE_PARSING_PRIMITIVE, /*!< Parse any primitive that is non-string, either "true", "false", "null" or a number */ LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END, /*!< Expecting ',', '}' or ']' */ - LWJSON_STREAM_STATE_EXPECTING_COLON, /*!< Expecting ':' */ + LWJSON_STREAM_STATE_EXPECTING_COLON, /*!< Expecting ':' */ } lwjson_stream_state_t; /* Forward declaration */ diff --git a/lwjson/src/include/lwjson/lwjson_opt.h b/lwjson/src/include/lwjson/lwjson_opt.h index da5f7cc..ae92127 100644 --- a/lwjson/src/include/lwjson/lwjson_opt.h +++ b/lwjson/src/include/lwjson/lwjson_opt.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #ifndef LWJSON_OPT_HDR_H #define LWJSON_OPT_HDR_H diff --git a/lwjson/src/include/lwjson/lwjson_opts_template.h b/lwjson/src/include/lwjson/lwjson_opts_template.h index 2971f0a..e4d6ac8 100644 --- a/lwjson/src/include/lwjson/lwjson_opts_template.h +++ b/lwjson/src/include/lwjson/lwjson_opts_template.h @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #ifndef LWJSON_OPTS_HDR_H #define LWJSON_OPTS_HDR_H diff --git a/lwjson/src/lwjson/lwjson.c b/lwjson/src/lwjson/lwjson.c index 232ae59..994d4fa 100644 --- a/lwjson/src/lwjson/lwjson.c +++ b/lwjson/src/lwjson/lwjson.c @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #include #include "lwjson/lwjson.h" diff --git a/lwjson/src/lwjson/lwjson_debug.c b/lwjson/src/lwjson/lwjson_debug.c index 09680ff..d74ef9a 100644 --- a/lwjson/src/lwjson/lwjson_debug.c +++ b/lwjson/src/lwjson/lwjson_debug.c @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #include #include diff --git a/lwjson/src/lwjson/lwjson_stream.c b/lwjson/src/lwjson/lwjson_stream.c index bc1dc96..95509b3 100644 --- a/lwjson/src/lwjson/lwjson_stream.c +++ b/lwjson/src/lwjson/lwjson_stream.c @@ -29,7 +29,7 @@ * This file is part of LwJSON - Lightweight JSON format parser. * * Author: Tilen MAJERLE - * Version: v1.6.1 + * Version: v1.7.0 */ #include #include "lwjson/lwjson.h" @@ -215,8 +215,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { if (prv_is_space_char_ext(chr)) { break; - /* Determine value separator */ - } else if ( chr == ',') { + /* Determine value separator */ + } else if (chr == ',') { if (jsp->parse_state == LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END) { jsp->parse_state = LWJSON_STREAM_STATE_PARSING; } else { @@ -274,8 +274,8 @@ lwjson_stream_parse(lwjson_stream_parser_t* jsp, char chr) { /* If comma or end was expected, then it is already error here */ } else if (jsp->parse_state == LWJSON_STREAM_STATE_EXPECTING_COMMA_OR_END) { - LWJSON_DEBUG(jsp, "ERROR - ',', '}' or ']' was expected\r\n"); - return lwjsonERRJSON; + LWJSON_DEBUG(jsp, "ERROR - ',', '}' or ']' was expected\r\n"); + return lwjsonERRJSON; /* Determine start of string - can be key or regular string (in array or after key) */ } else if (chr == '"') { diff --git a/test/test.c b/test/test.c index c8eb562..b70b5c3 100644 --- a/test/test.c +++ b/test/test.c @@ -5,8 +5,8 @@ * \brief Path and type parse check */ typedef struct { - const char* path; /*!< Path in parsed JSON */ - lwjson_type_t type; /*!< expected data type in JSON */ + const char* path; /*!< Path in parsed JSON */ + lwjson_type_t type; /*!< expected data type in JSON */ } test_path_type_t; /* LwJSON instance and tokens */ @@ -20,8 +20,20 @@ test_json_parse(void) { printf("JSON parse test\r\n"); -#define RUN_TEST(exp_res, json_str) if (lwjson_parse(&lwjson, (json_str)) == (exp_res)) { ++test_passed; } else { ++test_failed; printf("Test failed for input %s on line %d\r\n", json_str, __LINE__); } -#define RUN_TEST_EX(exp_res, json_str, len) if (lwjson_parse_ex(&lwjson, (json_str), (len)) == (exp_res)) { ++test_passed; } else { ++test_failed; printf("Test failed for input %s on line %d\r\n", json_str, __LINE__); } +#define RUN_TEST(exp_res, json_str) \ + if (lwjson_parse(&lwjson, (json_str)) == (exp_res)) { \ + ++test_passed; \ + } else { \ + ++test_failed; \ + printf("Test failed for input %s on line %d\r\n", json_str, __LINE__); \ + } +#define RUN_TEST_EX(exp_res, json_str, len) \ + if (lwjson_parse_ex(&lwjson, (json_str), (len)) == (exp_res)) { \ + ++test_passed; \ + } else { \ + ++test_failed; \ + printf("Test failed for input %s on line %d\r\n", json_str, __LINE__); \ + } /* Run JSON parse tests that must succeed */ RUN_TEST(lwjsonOK, "{}"); @@ -58,6 +70,8 @@ test_json_parse(void) { RUN_TEST(lwjsonERRJSON, "[[,[]]"); RUN_TEST(lwjsonERRJSON, "[,[]]"); RUN_TEST(lwjsonERRJSON, "[[],[,{}]"); + RUN_TEST(lwjsonERRJSON, "{[0,1,2]}"); + RUN_TEST(lwjsonERRJSON, "{1,2}"); /* Check specials */ RUN_TEST(lwjsonOK, "{\"k\":\"\\t\"}"); @@ -80,26 +94,26 @@ test_json_parse(void) { /* Run JSON tests to fail */ RUN_TEST(lwjsonERRPAR, ""); - RUN_TEST(lwjsonERRJSON, "{[]}"); /* Array without key inside object */ - RUN_TEST(lwjsonERRJSON, "{\"k\":False}"); /* False value must be all lowercase */ - RUN_TEST(lwjsonERRJSON, "{\"k\":True}"); /* True value must be all lowercase */ - RUN_TEST(lwjsonERRJSON, "{\"k\":nUll}"); /* Null value must be all lowercase */ - RUN_TEST(lwjsonERRJSON, "{\"k\"1}"); /* Missing separator */ - RUN_TEST(lwjsonERRJSON, "{\"k\"1}"); /* Missing separator */ - RUN_TEST(lwjsonERRJSON, "{k:1}"); /* Property name must be string */ - RUN_TEST(lwjsonERRJSON, "{k:0.}"); /* Wrong number format */ + RUN_TEST(lwjsonERRJSON, "{[]}"); /* Array without key inside object */ + RUN_TEST(lwjsonERRJSON, "{\"k\":False}"); /* False value must be all lowercase */ + RUN_TEST(lwjsonERRJSON, "{\"k\":True}"); /* True value must be all lowercase */ + RUN_TEST(lwjsonERRJSON, "{\"k\":nUll}"); /* Null value must be all lowercase */ + RUN_TEST(lwjsonERRJSON, "{\"k\"1}"); /* Missing separator */ + RUN_TEST(lwjsonERRJSON, "{\"k\"1}"); /* Missing separator */ + RUN_TEST(lwjsonERRJSON, "{k:1}"); /* Property name must be string */ + RUN_TEST(lwjsonERRJSON, "{k:0.}"); /* Wrong number format */ /* Tests with custom len */ RUN_TEST_EX(lwjsonOK, "[1,2,3,4]abc", 9); /* Limit input len to JSON-only */ RUN_TEST_EX(lwjsonERR, "[1,2,3,4]abc", 10); /* Too long input for JSON string.. */ - RUN_TEST_EX(lwjsonOK, "[1,2,3,4]", 15); /* String ends earlier than what is input data len indicating = OK if JSON is valid */ + RUN_TEST_EX(lwjsonOK, "[1,2,3,4]", + 15); /* String ends earlier than what is input data len indicating = OK if JSON is valid */ #undef RUN_TEST #undef RUN_TEST_EX /* Print results */ - printf("JSON parse test result pass/fail: %d/%d\r\n\r\n", - (int)test_passed, (int)test_failed); + printf("JSON parse test result pass/fail: %d/%d\r\n\r\n", (int)test_passed, (int)test_failed); } /* Test number of tokens necessary for parsing */ @@ -109,19 +123,20 @@ test_parse_token_count(void) { printf("---\r\nTest JSON token count..\r\n"); -#define RUN_TEST(exp_token_count, json_str) do { \ - if (lwjson_parse(&lwjson, (json_str)) == lwjsonOK) { \ - if ((lwjson.next_free_token_pos + 1) == exp_token_count) { \ - ++test_passed; \ - } else { \ - ++test_failed; \ - printf("Test failed for JSON token count on input %s\r\n", (json_str));\ - } \ - lwjson_free(&lwjson); \ - } else { \ - ++test_failed; \ - printf("Test failed for JSON parse on input %s\r\n", (json_str));\ - } \ +#define RUN_TEST(exp_token_count, json_str) \ + do { \ + if (lwjson_parse(&lwjson, (json_str)) == lwjsonOK) { \ + if ((lwjson.next_free_token_pos + 1) == exp_token_count) { \ + ++test_passed; \ + } else { \ + ++test_failed; \ + printf("Test failed for JSON token count on input %s\r\n", (json_str)); \ + } \ + lwjson_free(&lwjson); \ + } else { \ + ++test_failed; \ + printf("Test failed for JSON parse on input %s\r\n", (json_str)); \ + } \ } while (0) /* Run token count tests */ @@ -138,8 +153,7 @@ test_parse_token_count(void) { #undef RUN_TEST /* Print results */ - printf("JSON token count test result pass/fail: %d/%d\r\n\r\n", - (int)test_passed, (int)test_failed); + printf("JSON token count test result pass/fail: %d/%d\r\n\r\n", (int)test_passed, (int)test_failed); } /* Test JSON data types */ @@ -150,50 +164,50 @@ test_json_data_types(void) { /* Input JSON string */ const char* json_complete = "" - "{" - " \"int\": {" - " \"num1\": 1234," - " \"num2\": -1234," - " \"num3\": 0" - " }," + "{" + " \"int\": {" + " \"num1\": 1234," + " \"num2\": -1234," + " \"num3\": 0" + " }," #if LWJSON_CFG_COMMENTS - " /* This is my comment... */" + " /* This is my comment... */" #endif /* LWJSON_CFG_COMMENTS */ - " \"real\": {" - " \"num1\":123.4," - " \"num2\":-123.4," - " \"num3\":123E3," - " \"num4\":123e4," - " \"num5\":-123E3," - " \"num6\":-123e4," - " \"num7\":123E-3," - " \"num8\":123e-4," - " \"num9\":-123E-3," - " \"num10\":-123e-4," - " \"num11\":123.12E3," - " \"num12\":123.1e4," - " \"num13\":-123.0E3," - " \"num14\":-123.1e4," - " \"num15\":123.1E-3," - " \"num16\":123.1235e-4," - " \"num17\":-123.324342E-3," - " \"num18\":-123.3232e-4," - " }," - " \"obj\": {" - " \"obj1\":{}," - " \"obj2\":[]," - " \"obj3\":{" - " \"key1\":[]," - " \"key2\":\"string\"," - " }," - " }," - " \"bool\": {" - " \"true\":true," - " \"false\":false" - " }," - " \"null\":null," - " \"array\":[]," - "}"; + " \"real\": {" + " \"num1\":123.4," + " \"num2\":-123.4," + " \"num3\":123E3," + " \"num4\":123e4," + " \"num5\":-123E3," + " \"num6\":-123e4," + " \"num7\":123E-3," + " \"num8\":123e-4," + " \"num9\":-123E-3," + " \"num10\":-123e-4," + " \"num11\":123.12E3," + " \"num12\":123.1e4," + " \"num13\":-123.0E3," + " \"num14\":-123.1e4," + " \"num15\":123.1E-3," + " \"num16\":123.1235e-4," + " \"num17\":-123.324342E-3," + " \"num18\":-123.3232e-4," + " }," + " \"obj\": {" + " \"obj1\":{}," + " \"obj2\":[]," + " \"obj3\":{" + " \"key1\":[]," + " \"key2\":\"string\"," + " }," + " }," + " \"bool\": {" + " \"true\":true," + " \"false\":false" + " }," + " \"null\":null," + " \"array\":[]," + "}"; /* JSON paths */ const test_path_type_t paths_types[] = { @@ -245,7 +259,7 @@ test_json_data_types(void) { }; printf("---\r\nTest JSON data types..\r\n"); - + /* First parse JSON */ if (lwjson_parse(&lwjson, json_complete) != lwjsonOK) { printf("Could not parse LwJSON data types..\r\n"); @@ -272,8 +286,7 @@ test_json_data_types(void) { lwjson_free(&lwjson); /* Print results */ - printf("Data type test result pass/fail: %d/%d\r\n\r\n", - (int)test_passed, (int)test_failed); + printf("Data type test result pass/fail: %d/%d\r\n\r\n", (int)test_passed, (int)test_failed); } /* Test find function */ @@ -305,87 +318,80 @@ test_find_function(void) { "; printf("---\r\nTest JSON find..\r\n"); - + /* First parse JSON */ if (lwjson_parse(&lwjson, json_str) != lwjsonOK) { printf("Could not parse JSON string..\r\n"); return; } - #define RUN_TEST(c) if ((c)) { ++test_passed; } else { ++test_failed; printf("Test failed on line %d\r\n", __LINE__); } +#define RUN_TEST(c) \ + if ((c)) { \ + ++test_passed; \ + } else { \ + ++test_failed; \ + printf("Test failed on line %d\r\n", __LINE__); \ + } /* Run all tests */ - RUN_TEST((token = lwjson_find(&lwjson, "my_arr")) != NULL - && token->type == LWJSON_TYPE_ARRAY); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr")) != NULL && token->type == LWJSON_TYPE_ARRAY); RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#")) == NULL); RUN_TEST((token = lwjson_find(&lwjson, "my_arr.")) == NULL); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#0")) != NULL - && token->type == LWJSON_TYPE_OBJECT); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#1")) != NULL - && token->type == LWJSON_TYPE_OBJECT); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#2")) != NULL - && token->type == LWJSON_TYPE_OBJECT); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#0.str")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "first_entry", 11) == 0); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#1.str")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "second_entry", 12) == 0); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#2.str")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "third_entry", 11) == 0); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3")) != NULL - && token->type == LWJSON_TYPE_ARRAY); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3.#1")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "def", 3) == 0); - RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3.#0")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "abc", 3) == 0); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#0")) != NULL && token->type == LWJSON_TYPE_OBJECT); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#1")) != NULL && token->type == LWJSON_TYPE_OBJECT); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#2")) != NULL && token->type == LWJSON_TYPE_OBJECT); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#0.str")) != NULL && token->type == LWJSON_TYPE_STRING + && strncmp(token->u.str.token_value, "first_entry", 11) == 0); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#1.str")) != NULL && token->type == LWJSON_TYPE_STRING + && strncmp(token->u.str.token_value, "second_entry", 12) == 0); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#2.str")) != NULL && token->type == LWJSON_TYPE_STRING + && strncmp(token->u.str.token_value, "third_entry", 11) == 0); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3")) != NULL && token->type == LWJSON_TYPE_ARRAY); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3.#1")) != NULL && token->type == LWJSON_TYPE_STRING + && strncmp(token->u.str.token_value, "def", 3) == 0); + RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3.#0")) != NULL && token->type == LWJSON_TYPE_STRING + && strncmp(token->u.str.token_value, "abc", 3) == 0); RUN_TEST((token = lwjson_find(&lwjson, "my_arr.#3.#")) == NULL); /* Use EX version to search for tokens */ - RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj")) != NULL /* First search for my_obj in root JSON */ - && (token = lwjson_find_ex(&lwjson, token, "key_true")) != NULL /* Use token for relative search and search for key_true only */ - && token->type == LWJSON_TYPE_TRUE); + RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj")) != NULL /* First search for my_obj in root JSON */ + && (token = lwjson_find_ex(&lwjson, token, "key_true")) + != NULL /* Use token for relative search and search for key_true only */ + && token->type == LWJSON_TYPE_TRUE); /* Deep search */ /* Search for first match in any array */ RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.arr.#.#.my_key")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "my_text", 7) == 0); + && token->type == LWJSON_TYPE_STRING && strncmp(token->u.str.token_value, "my_text", 7) == 0); /* Search for match in specific array keys */ RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.arr.#2.#0.my_key")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "my_text", 7) == 0); + && token->type == LWJSON_TYPE_STRING && strncmp(token->u.str.token_value, "my_text", 7) == 0); /* Search for match in specific array keys = must return NULL, no index = 1 in second array */ RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.arr.#2.#1.my_key")) == NULL); /* Use partial searches */ RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj")) != NULL - && (token = lwjson_find_ex(&lwjson, token, "arr")) != NULL - && (token = lwjson_find_ex(&lwjson, token, "#2")) != NULL - && (token = lwjson_find_ex(&lwjson, token, "#0")) != NULL - && (token = lwjson_find_ex(&lwjson, token, "my_key")) != NULL - && token->type == LWJSON_TYPE_STRING - && strncmp(token->u.str.token_value, "my_text", 7) == 0); + && (token = lwjson_find_ex(&lwjson, token, "arr")) != NULL + && (token = lwjson_find_ex(&lwjson, token, "#2")) != NULL + && (token = lwjson_find_ex(&lwjson, token, "#0")) != NULL + && (token = lwjson_find_ex(&lwjson, token, "my_key")) != NULL && token->type == LWJSON_TYPE_STRING + && strncmp(token->u.str.token_value, "my_text", 7) == 0); /* Search for match in specific array keys and check for string length field */ - RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.ustr")) != NULL - && token->type == LWJSON_TYPE_STRING - && lwjson_get_val_string_length(token) == 11 - && strncmp(token->u.str.token_value, "\\t\\u1234abc", 11) == 0); + RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.ustr")) != NULL && token->type == LWJSON_TYPE_STRING + && lwjson_get_val_string_length(token) == 11 + && strncmp(token->u.str.token_value, "\\t\\u1234abc", 11) == 0); /* Check string compare */ RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.arr.#.#.my_key")) != NULL - && lwjson_string_compare(token, "my_text")); + && lwjson_string_compare(token, "my_text")); RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.arr.#.#.my_key")) != NULL - && lwjson_string_compare_n(token, "my_text", 3)); + && lwjson_string_compare_n(token, "my_text", 3)); RUN_TEST((token = lwjson_find_ex(&lwjson, NULL, "my_obj.arr.#.#.my_key")) != NULL - && !lwjson_string_compare_n(token, "my_stext", 4)); /* Must be a fail */ + && !lwjson_string_compare_n(token, "my_stext", 4)); /* Must be a fail */ #undef RUN_TEST @@ -393,8 +399,7 @@ test_find_function(void) { lwjson_free(&lwjson); /* Print results */ - printf("Find function test result pass/fail: %d/%d\r\n\r\n", - (int)test_passed, (int)test_failed); + printf("Find function test result pass/fail: %d/%d\r\n\r\n", (int)test_passed, (int)test_failed); } /** @@ -404,9 +409,9 @@ void test_run(void) { /* Init LwJSON */ lwjson_init(&lwjson, tokens, LWJSON_ARRAYSIZE(tokens)); - - /* Test JSON parse */ - test_json_parse(); + + /* Test JSON parse */ + test_json_parse(); /* Test find function */ test_find_function();