From 2dcda90c7b9b21fa9f21b6a86dc24786fd963cb5 Mon Sep 17 00:00:00 2001 From: Patrick Pelissier Date: Tue, 9 Mar 2021 22:12:16 +0100 Subject: [PATCH] Add JSON serial over string --- m-serial-json.h | 611 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) diff --git a/m-serial-json.h b/m-serial-json.h index 8d53d6a3..8335e6c2 100644 --- a/m-serial-json.h +++ b/m-serial-json.h @@ -30,6 +30,10 @@ M_BEGIN_PROTECTED_CODE +/********************************************************************************/ +/*************************** FILE / WRITE / JSON ********************************/ +/********************************************************************************/ + /* Write the boolean 'data' into the serial stream 'serial'. Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ static inline m_serial_return_code_t @@ -277,6 +281,11 @@ typedef m_serial_write_t m_serial_json_write_t; + +/********************************************************************************/ +/*************************** FILE / READ / JSON ********************************/ +/********************************************************************************/ + /* Helper function to skip a space */ static inline int m_serial_json_read_skip (FILE *f) @@ -503,6 +512,7 @@ m_serial_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, FILE *f = (FILE*) serial->data[0].p; int final1 = -1, final2 = -1; + // TODO: Accept 'null' as empty variant? int nn = m_core_fscanf(f, " {%n }%n", &final1, &final2); (void) nn ; // Unused. Check is done through final (more robust). // Test how much the parsing succeed. @@ -576,6 +586,607 @@ typedef m_serial_read_t m_serial_json_read_t; #define M_OPL_m_serial_json_read_t() \ (INIT_WITH(m_serial_json_read_init), CLEAR(m_serial_json_read_clear), TYPE(m_serial_json_read_t) ) + + +/********************************************************************************/ +/************************** STRING / WRITE / JSON *******************************/ +/********************************************************************************/ + +/* Write the boolean 'data' into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_boolean(m_serial_write_t serial, const bool data) +{ + struct string_s *f = (struct string_s *)serial->data[0].p; + int n = string_cat_printf(f, "%s", data ? "true" : "false"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the integer 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_integer(m_serial_write_t serial,const long long data, const size_t size_of_type) +{ + (void) size_of_type; // Ignored + struct string_s *f = (struct string_s *)serial->data[0].p; + int n = string_cat_printf(f, "%lld", data); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the float 'data' of 'size_of_type' bytes into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_float(m_serial_write_t serial, const long double data, const size_t size_of_type) +{ + (void) size_of_type; // Ignored + struct string_s *f = (struct string_s *)serial->data[0].p; + int n = string_cat_printf(f, "%Lf", data); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Write the null-terminated string 'data'into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_string(m_serial_write_t serial, const char data[], size_t length) +{ + M_ASSERT_SLOW(length == strlen(data) ); + struct string_s *f = (struct string_s *)serial->data[0].p; + M_ASSERT(f != NULL && data != NULL); + /* HACK: Build dummy string to reuse string_get_str */ + string_t v2; + uintptr_t ptr = (uintptr_t) data; + v2->u.heap.size = length; + v2->u.heap.alloc = length + 1; + v2->ptr = (char*)ptr; + string_get_str(f, v2, true); + return M_SERIAL_OK_DONE; +} + +/* Start writing an array of 'number_of_elements' objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the array has no data, + or the number of elements of the array is unkown. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_array_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; // argument not used + (void) number_of_elements; // Ignored + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, '['); + return M_SERIAL_OK_CONTINUE; +} + +/* Write an array separator between elements of an array into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_array_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, ','); + return M_SERIAL_OK_CONTINUE; +} + +/* End the writing of an array into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_array_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, ']'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a map of 'number_of_elements' pairs of objects into the serial stream 'serial'. + If 'number_of_elements' is 0, then either the map has no data, + or the number of elements is unkown. + Initialize 'local' so that it can be used to serialize the map + (local is an unique serialization object of the map). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_map_start(m_serial_local_t local, m_serial_write_t serial, const size_t number_of_elements) +{ + (void) local; // argument not used + (void) number_of_elements; // Ignored + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, '{'); + return M_SERIAL_OK_CONTINUE; +} + +/* Write a value separator between element of the same pair of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_map_value(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, ':'); + return M_SERIAL_OK_CONTINUE; +} + +/* Write a map separator between elements of a map into the serial stream 'serial' if needed. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_map_next(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, ','); + return M_SERIAL_OK_CONTINUE; +} + +/* End the writing of a map into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_map_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, '}'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a tuple into the serial stream 'serial'. + Initialize 'local' so that it can serial the tuple + (local is an unique serialization object of the tuple). + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_tuple_start(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, '{'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing the field named field_name[index] of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_tuple_id(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) max; // Ignored + struct string_s *f = (struct string_s *)serial->data[0].p; + int n = string_cat_printf(f, "%c\"%s\":", index == 0 ? ' ' : ',', field_name[index]); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* End the write of a tuple into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_tuple_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, '}'); + return M_SERIAL_OK_CONTINUE; +} + +/* Start writing a variant into the serial stream 'serial'. + If index <= 0, the variant is empty. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise + Otherwise, the field 'field_name[index]' will be filled. + Return M_SERIAL_OK_CONTINUE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_variant_start(m_serial_local_t local, m_serial_write_t serial, const char *const field_name[], const int max, const int index) +{ + (void) local; // argument not used + (void) max; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + int n; + if (index >= 0) { + M_ASSERT (index < max); + n = string_cat_printf(f, "{\"%s\":", field_name[index]); + return n > 0 ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); + } else { + n = string_cat_printf(f, "{}"); + return n > 0 ? M_SERIAL_OK_DONE : m_core_serial_fail(); + } +} + +/* End Writing a variant into the serial stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_write_variant_end(m_serial_local_t local, m_serial_write_t serial) +{ + (void) local; // argument not used + struct string_s *f = (struct string_s *)serial->data[0].p; + string_push_back(f, '}'); + return M_SERIAL_OK_CONTINUE; +} + +/* The internal exported interface of m_serial_write_json. */ +static const m_serial_write_interface_t m_serial_str_json_write_interface = { + m_serial_str_json_write_boolean, + m_serial_str_json_write_integer, + m_serial_str_json_write_float, + m_serial_str_json_write_string, + m_serial_str_json_write_array_start, + m_serial_str_json_write_array_next, + m_serial_str_json_write_array_end, + m_serial_str_json_write_map_start, + m_serial_str_json_write_map_value, + m_serial_str_json_write_map_next, + m_serial_str_json_write_map_end, + m_serial_str_json_write_tuple_start, + m_serial_str_json_write_tuple_id, + m_serial_str_json_write_tuple_end, + m_serial_str_json_write_variant_start, + m_serial_str_json_write_variant_end +}; + +/* Initialize the JSON serial object for writing any object to JSON format in the given FILE */ +static inline void m_serial_str_json_write_init(m_serial_write_t serial, string_t s) +{ + serial->m_interface = &m_serial_str_json_write_interface; + serial->data[0].p = M_ASSIGN_CAST(struct string_s *, s); +} + +/* CLear the JSON serial object for writing*/ +static inline void m_serial_str_json_write_clear(m_serial_write_t serial) +{ + (void) serial; // Nothing to do +} + +/* Define a synonym to the JSON serializer with a proper OPLIST */ +typedef m_serial_write_t m_serial_str_json_write_t; +#define M_OPL_m_serial_str_json_write_t() \ + (INIT_WITH(m_serial_str_json_write_init), CLEAR(m_serial_str_json_write_clear), TYPE(m_serial_str_json_write_t) ) + + + +/********************************************************************************/ +/************************** STRING / READ / JSON *******************************/ +/********************************************************************************/ + +/* Helper function to read a character and advance the stream */ +static inline char +m_serial_str_json_getc(const char **p) +{ + char c = **p; + // Once it reaches the end of string character (== 0), stop advancing the stream. + *p = *p + (c != 0); + return c; +} + +/* Helper function to skip space(s) */ +static inline void +m_serial_str_json_skip (const char **p) +{ + while (isspace (**p)) { + (*p)++; + } +} + +/* Helper function to read a string with characters not present in reject */ +static inline void +m_serial_str_json_gets(size_t alloc, char field[], const char reject[], const char **s) +{ + M_ASSERT (alloc > 0); + size_t length = strcspn(*s, reject); + if (length >= alloc-1) + length = alloc - 1; + memcpy(field, *s, length); + field[length] = 0; + *s += length; +} + +/* Read from the stream 'serial' a boolean. + Set '*b' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_boolean(m_serial_read_t serial, bool *b){ + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + if (c == 't') { + *b = true; + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 'r')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 'u')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 'e')) return m_core_serial_fail(); + return M_SERIAL_OK_DONE; + } else if (M_LIKELY(c == 'f')) { + *b = false; + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 'a')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 'l')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 's')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != 'e')) return m_core_serial_fail(); + return M_SERIAL_OK_DONE; + } else { + return m_core_serial_fail(); + } +} + +/* Read from the stream 'serial' an integer that can be represented with 'size_of_type' bytes. + Set '*i' with the integer value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_integer(m_serial_read_t serial, long long *i, const size_t size_of_type){ + (void) size_of_type; // Ignored + const char **f = &serial->data[0].c; + char *e; + *i = strtoll(*f, &e, 10); + bool b = e != *f; + serial->data[0].c = (const char*) e; + return b ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a float that can be represented with 'size_of_type' bytes. + Set '*r' with the boolean value if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_float(m_serial_read_t serial, long double *r, const size_t size_of_type){ + (void) size_of_type; // Ignored + const char **f = &serial->data[0].c; + char *e; + *r = strtold(*f, &e); + bool b = e != *f; + serial->data[0].c = (const char*) e; + return b ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Read from the stream 'serial' a string. + Set 's' with the string if succeeds + Return M_SERIAL_OK_DONE if it succeeds, M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_string(m_serial_read_t serial, struct string_s *s){ + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + const char *e; + bool b = string_parse_str(s, *f, &e); + serial->data[0].c = e; + return b ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' an array. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends (the array is empty), + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_array_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != '[')) { + return m_core_serial_fail(); + } + *num = 0; // Don't know the size of the array. + m_serial_str_json_skip(f); + if (M_UNLIKELY (**f == ']')) { + c = m_serial_str_json_getc(f); + return M_SERIAL_OK_DONE; + } else { + return M_SERIAL_OK_CONTINUE; + } +} + +/* Continue reading from the stream 'serial' an array. + Return M_SERIAL_OK_CONTINUE if it succeeds and the array continue, + M_SERIAL_OK_DONE if it succeeds and the array ends, + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_array_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + return c == ',' ? M_SERIAL_OK_CONTINUE : c == ']' ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading from the stream 'serial' a map. + Set '*num' with the number of elements, or 0 if it is not known. + Initialize 'local' so that it can be used to serialize the array + (local is an unique serialization object of the array). + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_OK_DONE if it succeeds and the map ends (the map is empty), + m_core_serial_fail() otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_map_start(m_serial_local_t local, m_serial_read_t serial, size_t *num) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + if (M_UNLIKELY(c != '{')) { + return m_core_serial_fail(); + } + *num = 0; // Don't know the size of the map + m_serial_str_json_skip(f); + if (M_UNLIKELY (**f == '}')) { + c = m_serial_str_json_getc(f); + return M_SERIAL_OK_DONE; + } else { + return M_SERIAL_OK_CONTINUE; + } +} + +/* Continue reading from the stream 'serial' the value separator + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_map_value(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + return c ==':' ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Continue reading from the stream 'serial' a map. + Return M_SERIAL_OK_CONTINUE if it succeeds and the map continue, + M_SERIAL_OK_DONE if it succeeds and the map ends, + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_map_next(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + return c == ',' ? M_SERIAL_OK_CONTINUE : c == '}' ? M_SERIAL_OK_DONE : m_core_serial_fail(); +} + +/* Start reading a tuple from the stream 'serial'. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_tuple_start(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + return c == '{' ? M_SERIAL_OK_CONTINUE : m_core_serial_fail(); +} + +/* Continue reading a tuple from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the tuple continues, + Return M_SERIAL_OK_DONE if it succeeds and the tuple ends, + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_tuple_id(m_serial_local_t local, m_serial_read_t serial, const char *const field_name [], const int max, int *id) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + char c = m_serial_str_json_getc(f); + if (c == 0) return m_core_serial_fail(); + if (c == '}') return M_SERIAL_OK_DONE; + if (c == ',') { + // If first call of read_tuple_id, it is a failure + if (M_UNLIKELY (*id == -1)) return m_core_serial_fail(); + m_serial_str_json_skip(f); + c = m_serial_str_json_getc(f); + } + + /* Read the field in the JSON */ + char field[M_USE_IDENTIFIER_ALLOC+1]; + if (M_UNLIKELY(c != '"')) return m_core_serial_fail(); + m_serial_str_json_gets(M_USE_IDENTIFIER_ALLOC+1, field, " \t\n\"", f); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY (c != '"')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY (c != ':')) return m_core_serial_fail(); + + /* Search for field in field_name */ + for(int n = 0; n < max; n++) { + if (strcmp(field, field_name[n]) == 0) { + *id = n; + return M_SERIAL_OK_CONTINUE; + } + } + return m_core_serial_fail(); +} + +/* Start reading a variant from the stream 'serial'. + Set '*id' with the corresponding index of the table 'field_name[max]' + associated to the parsed field in the stream. + Return M_SERIAL_OK_CONTINUE if it succeeds and the variant continues, + Return M_SERIAL_OK_DONE if it succeeds and the variant ends(variant is empty), + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_variant_start(m_serial_local_t local, m_serial_read_t serial, const char *const field_name[], const int max, int*id) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + int c = m_serial_str_json_getc(f); + if (M_UNLIKELY (c != '{')) + // TODO: Accept 'null' as empty variant? + return m_core_serial_fail(); + m_serial_str_json_skip(f); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY (c == '}')) return M_SERIAL_OK_DONE; // Empty variant + + /* Read the field in the JSON */ + char field[M_USE_IDENTIFIER_ALLOC+1]; + if (M_UNLIKELY(c != '"')) return m_core_serial_fail(); + m_serial_str_json_gets(M_USE_IDENTIFIER_ALLOC+1, field, " \t\n\"", f); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY (c != '"')) return m_core_serial_fail(); + c = m_serial_str_json_getc(f); + if (M_UNLIKELY (c != ':')) return m_core_serial_fail(); + + /* Linear Search for field in field_name */ + for(int n = 0; n < max; n++) { + if (strcmp(field, field_name[n]) == 0) { + *id = n; + return M_SERIAL_OK_CONTINUE; + } + } + // Field not found + return m_core_serial_fail(); +} + +/* End reading a variant from the stream 'serial'. + Return M_SERIAL_OK_DONE if it succeeds and the variant ends, + M_SERIAL_FAIL otherwise */ +static inline m_serial_return_code_t +m_serial_str_json_read_variant_end(m_serial_local_t local, m_serial_read_t serial) +{ + (void) local; // argument not used + const char **f = &serial->data[0].c; + m_serial_str_json_skip(f); + int c = m_serial_str_json_getc(f); + return (M_UNLIKELY (c != '}')) ? m_core_serial_fail() : M_SERIAL_OK_DONE; +} + +static const m_serial_read_interface_t m_serial_str_json_read_interface = { + m_serial_str_json_read_boolean, + m_serial_str_json_read_integer, + m_serial_str_json_read_float, + m_serial_str_json_read_string, + m_serial_str_json_read_array_start, + m_serial_str_json_read_array_next, + m_serial_str_json_read_map_start, + m_serial_str_json_read_map_value, + m_serial_str_json_read_map_next, + m_serial_str_json_read_tuple_start, + m_serial_str_json_read_tuple_id, + m_serial_str_json_read_variant_start, + m_serial_str_json_read_variant_end +}; + +/* Initialize the JSON serial object for reading any object from JSON format in the given const string */ +static inline void m_serial_str_json_read_init(m_serial_read_t serial, const char str[]) +{ + serial->m_interface = &m_serial_str_json_read_interface; + serial->data[0].c = str; +} + +/* Clear the JSON serial object for reading from the FILE */ +static inline const char *m_serial_str_json_read_clear(m_serial_read_t serial) +{ + // Nothing to clear. Return pointer to the last data parsed. + return serial->data[0].c; +} + +/* Define a synonym of m_serial_read_t + to the JSON serializer with its proper OPLIST */ +typedef m_serial_read_t m_serial_str_json_read_t; +#define M_OPL_m_serial_str_json_read_t() \ + (INIT_WITH(m_serial_str_json_read_init), CLEAR(m_serial_str_json_read_clear), TYPE(m_serial_str_json_read_t) ) + + M_END_PROTECTED_CODE #endif