From dae1cfb08470e6c6b57166609ffe90dd91844121 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:16:56 +0000 Subject: [PATCH] Implement json_table function in C This commit introduces a C implementation of a `json_table`-like function that processes a JSON string and transforms it into a tabular data structure. Key features: - Parses JSON using the cJSON library. - Implements a basic JSONPath engine for selecting row data and column data. Supports `$.object.child`, `$[index]`, and `$.array[*]`. - Defines data structures for table, rows, and column definitions. - Converts JSON values to C types (int, double, string), with JSON nulls being converted to 0/0.0 for numeric types and NULL for strings. - Includes a demonstration program (`main.c`) with various examples. - Provides a Makefile for building the demonstration program. --- Makefile | 20 + cJSON.c | 3191 +++++++++++++++++++++++++++++++++++++++++++++++ cJSON.h | 308 +++++ cJSON.o | Bin 0 -> 84952 bytes json_path.c | 168 +++ json_path.h | 48 + json_path.o | Bin 0 -> 11576 bytes json_table.c | 363 ++++++ json_table.h | 72 ++ json_table.o | Bin 0 -> 22152 bytes json_table_demo | Bin 0 -> 95384 bytes main.c | 188 +++ main.o | Bin 0 -> 15840 bytes 13 files changed, 4358 insertions(+) create mode 100644 Makefile create mode 100644 cJSON.c create mode 100644 cJSON.h create mode 100644 cJSON.o create mode 100644 json_path.c create mode 100644 json_path.h create mode 100644 json_path.o create mode 100644 json_table.c create mode 100644 json_table.h create mode 100644 json_table.o create mode 100755 json_table_demo create mode 100644 main.c create mode 100644 main.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..e356744385 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +CC = gcc +CFLAGS = -Wall -g -I. # -I. to find local headers like cJSON.h, json_table.h +LDFLAGS = -lm # Link math library for cJSON if needed (e.g. for NAN, sqrt) + +SRCS = cJSON.c json_path.c json_table.c main.c +OBJS = $(SRCS:.c=.o) +TARGET = json_table_demo + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $(TARGET) $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJS) $(TARGET) diff --git a/cJSON.c b/cJSON.c new file mode 100644 index 0000000000..ca824f0e7f --- /dev/null +++ b/cJSON.c @@ -0,0 +1,3191 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 18) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + item->valuestring = NULL; + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + item->string = NULL; + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char *number_c_string; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + size_t number_string_length = 0; + cJSON_bool has_decimal_point = false; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_string_length++; + break; + + case '.': + number_string_length++; + has_decimal_point = true; + break; + + default: + goto loop_end; + } + } +loop_end: + /* malloc for temporary buffer, add 1 for '\0' */ + number_c_string = (unsigned char *) input_buffer->hooks.allocate(number_string_length + 1); + if (number_c_string == NULL) + { + return false; /* allocation failure */ + } + + memcpy(number_c_string, buffer_at_offset(input_buffer), number_string_length); + number_c_string[number_string_length] = '\0'; + + if (has_decimal_point) + { + for (i = 0; i < number_string_length; i++) + { + if (number_c_string[i] == '.') + { + /* replace '.' with the decimal point of the current locale (for strtod) */ + number_c_string[i] = decimal_point; + } + } + } + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + /* free the temporary buffer */ + input_buffer->hooks.deallocate(number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +/* Note: when passing a NULL valuestring, cJSON_SetValuestring treats this as an error and return NULL */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + size_t v1_len; + size_t v2_len; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + /* return NULL if the object is corrupted or valuestring is NULL */ + if (object->valuestring == NULL || valuestring == NULL) + { + return NULL; + } + + v1_len = strlen(valuestring); + v2_len = strlen(object->valuestring); + + if (v1_len <= v2_len) + { + /* strcpy does not handle overlapping string: [X1, X2] [Y1, Y2] => X2 < Y1 or Y2 < X1 */ + if (!( valuestring + v1_len < object->valuestring || object->valuestring + v2_len < valuestring )) + { + return NULL; + } + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + output = NULL; + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + buffer->buffer = NULL; + } + + if (printed != NULL) + { + hooks->deallocate(printed); + printed = NULL; + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + p.buffer = NULL; + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + if (cannot_access_at_index(input_buffer, 1)) + { + goto fail; /* nothing comes after the comma */ + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL) || (item != parent->child && item->prev == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0 || newitem == NULL) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + if (after_inserted != array->child && after_inserted->prev == NULL) { + /* return false if after_inserted is a corrupted array item */ + return false; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse); + +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + return cJSON_Duplicate_rec(item, 0, recurse ); +} + +cJSON * cJSON_Duplicate_rec(const cJSON *item, size_t depth, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + if(depth >= CJSON_CIRCULAR_LIMIT) { + goto fail; + } + newchild = cJSON_Duplicate_rec(child, depth + 1, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); + object = NULL; +} diff --git a/cJSON.h b/cJSON.h new file mode 100644 index 0000000000..fd3737b7eb --- /dev/null +++ b/cJSON.h @@ -0,0 +1,308 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 18 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* Limits the length of circular references can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_CIRCULAR_LIMIT +#define CJSON_CIRCULAR_LIMIT 10000 +#endif + + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. + */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/cJSON.o b/cJSON.o new file mode 100644 index 0000000000000000000000000000000000000000..7237866fadc4f8da86795f91b66230a01a16298d GIT binary patch literal 84952 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 ZPx;;BSDgOP-u5M +#include +#include +#include // For isdigit +#include // For true/false + +// Helper to free JsonPathResult +void json_path_result_free(JsonPathResult* result) { + if (result) { + free(result->items); // Frees the array of pointers + result->items = NULL; + result->count = 0; + } +} + +// Internal helper to duplicate a string, as strdup is not standard C90 +// and we want to manage memory explicitly. +static char* jp_strdup(const char* s) { + if (!s) return NULL; + size_t len = strlen(s) + 1; + char* new_s = (char*)malloc(len); + if (!new_s) return NULL; + memcpy(new_s, s, len); + return new_s; +} + +// Internal helper to add a cJSON item to a JsonPathResult +static JsonPathResult append_item_to_result(JsonPathResult result, cJSON* item) { + if (!item) return result; // Don't add NULL items if that's the resolution + + cJSON** new_items = (cJSON**)realloc(result.items, (result.count + 1) * sizeof(cJSON*)); + if (!new_items) { + // Realloc failed, free existing and return empty + free(result.items); + return (JsonPathResult){NULL, 0, false}; + } + result.items = new_items; + result.items[result.count] = item; + result.count++; + return result; +} + + +JsonPathResult json_path_evaluate(cJSON* current_node, const char* path_expr) { + JsonPathResult result = {NULL, 0, false}; + if (!current_node || !path_expr) { + return result; + } + + char* path_copy = jp_strdup(path_expr); + if (!path_copy) { + return result; // Memory allocation failure + } + + char* current_segment = path_copy; + cJSON* current_json_item = current_node; + result = append_item_to_result(result, current_json_item); // Start with the current node + + if (strcmp(current_segment, "$") == 0) { + // Path is just "$", refers to the root_node itself + free(path_copy); + // Result already contains current_node, so just return + return result; + } + + // If path starts with "$.", move past it. + if (strncmp(current_segment, "$.", 2) == 0) { + current_segment += 2; + } else if (current_segment[0] == '$') { // Path like "$[0]" or "$['key']" (though we don't support quoted keys yet) + current_segment += 1; + } + // If path does not start with '$', it's assumed to be relative from current_node + // (this behavior is for when json_path_evaluate is called recursively for column paths) + + char* next_delimiter; + while (current_segment && *current_segment != '\0' && result.count > 0) { + JsonPathResult current_iteration_result = {NULL, 0, false}; + result.is_array_wildcard_result = false; // Reset for this segment + + for (size_t i = 0; i < result.count; ++i) { + current_json_item = result.items[i]; + if (!current_json_item) continue; + + if (*current_segment == '[') { // Array access: [index] or [*] + current_segment++; // Skip '[' + next_delimiter = strchr(current_segment, ']'); + if (!next_delimiter) { + json_path_result_free(&result); + json_path_result_free(¤t_iteration_result); + free(path_copy); + return (JsonPathResult){NULL, 0, false}; // Invalid array segment + } + *next_delimiter = '\0'; // Terminate segment + + if (strcmp(current_segment, "*") == 0) { // Wildcard [*] + if (cJSON_IsArray(current_json_item)) { + result.is_array_wildcard_result = true; + cJSON* array_child = current_json_item->child; + while (array_child) { + current_iteration_result = append_item_to_result(current_iteration_result, array_child); + array_child = array_child->next; + } + } else { + // Wildcard on non-array, effectively finds nothing for this path item + // Continue to next item in 'result.items' if any + } + } else { // Specific index [index] + char* endptr; + long index = strtol(current_segment, &endptr, 10); + if (*endptr != '\0' || !cJSON_IsArray(current_json_item)) { + // Invalid index or not an array + // Continue to next item in 'result.items' if any + continue; + } + cJSON* indexed_item = cJSON_GetArrayItem(current_json_item, (int)index); + if (indexed_item) { + current_iteration_result = append_item_to_result(current_iteration_result, indexed_item); + } + } + current_segment = next_delimiter + 1; + } else { // Object access: .key + if (*current_segment == '.') current_segment++; // Skip leading '.' if present + + next_delimiter = strchr(current_segment, '.'); + char* next_bracket = strchr(current_segment, '['); + if (next_bracket && (!next_delimiter || next_bracket < next_delimiter)) { + next_delimiter = next_bracket; + } + + char temp_char = 0; + if (next_delimiter) { + temp_char = *next_delimiter; + *next_delimiter = '\0'; + } + + if (cJSON_IsObject(current_json_item)) { + cJSON* object_child = cJSON_GetObjectItemCaseSensitive(current_json_item, current_segment); + if (object_child) { + current_iteration_result = append_item_to_result(current_iteration_result, object_child); + } + } else { + // Trying to access key on non-object + // Continue to next item in 'result.items' if any + } + + if (next_delimiter) { + *next_delimiter = temp_char; // Restore char + current_segment = next_delimiter; + } else { + current_segment = NULL; // End of path + } + } + } // End for loop over result.items + + json_path_result_free(&result); // Free previous result's item array + result = current_iteration_result; // Assign new result + + if (result.count == 0 && current_segment && *current_segment != '\0') { + // If at any point we have no items but still have path segments, it's a miss. + break; + } + + } // End while loop over path segments + + free(path_copy); + return result; +} diff --git a/json_path.h b/json_path.h new file mode 100644 index 0000000000..998f9b7ae2 --- /dev/null +++ b/json_path.h @@ -0,0 +1,48 @@ +#ifndef JSON_PATH_H +#define JSON_PATH_H + +#include "cJSON.h" // Needs cJSON structures +#include // For size_t + +// Structure to hold the result of a JSONPath evaluation. +// It might be a single cJSON item or an array of cJSON items (e.g., from [*]). +typedef struct { + cJSON** items; // Array of pointers to cJSON items. + // If the path resolves to a single item, this will have one element. + // If the path resolves to multiple items (e.g. wildcard), this will have multiple. + // The items are BORROWED from the main parsed cJSON structure, not copies. + size_t count; // Number of items in the 'items' array. + cJSON_bool is_array_wildcard_result; // Flag to indicate if the result came from a [*] operation on an array +} JsonPathResult; + +// Evaluates a JSONPath expression against a cJSON object. +// +// Parameters: +// root_node: The cJSON object (or array) to evaluate the path against. +// path_expr: The JSONPath expression string. +// Supported: +// - `$` (root) +// - `.child_name` (object member access) +// - `[index]` (array element access by specific index) +// - `[*]` (array wildcard - returns all elements of an array) +// +// Returns: +// A JsonPathResult structure. The caller is responsible for freeing +// the 'items' array within this structure using `json_path_result_free()`. +// The cJSON items themselves are part of the main document and should not be freed here. +// If an error occurs (e.g., invalid path, item not found for a specific part of the path), +// `items` will be NULL and `count` will be 0. +// +// Notes: +// - This is a simplified implementation. It does not support deep scanning (..), +// script expressions, filters, or more complex path features. +// - Object member names and array indices are processed sequentially. +// - If a segment of the path does not find an item (e.g., non-existent key or out-of-bounds index), +// the evaluation stops and returns an empty result, unless it's a wildcard `[*]`. +JsonPathResult json_path_evaluate(cJSON* current_node, const char* path_expr); + +// Frees the memory allocated for the JsonPathResult structure, specifically the 'items' array. +// Does NOT free the cJSON items themselves. +void json_path_result_free(JsonPathResult* result); + +#endif // JSON_PATH_H diff --git a/json_path.o b/json_path.o new file mode 100644 index 0000000000000000000000000000000000000000..f6e0f9c3b008b730522aa822550263d1e4707e94 GIT binary patch literal 11576 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 literal 0 HcmV?d00001 diff --git a/json_table.c b/json_table.c new file mode 100644 index 0000000000..25afe23139 --- /dev/null +++ b/json_table.c @@ -0,0 +1,363 @@ +#include "json_table.h" +#include "cJSON.h" +#include "json_path.h" // For JsonPathResult and json_path_evaluate + +#include +#include +#include + +// --- Forward declarations for internal helper functions --- +static size_t calculate_total_row_data_size(JsonTableColumnDef* column_defs, size_t num_columns); +static void populate_column_offsets_and_sizes(JsonTableColumnDef* column_defs, size_t num_columns); +static JsonTableError process_single_row_item( + cJSON* row_json_item, + JsonTable* table_to_populate, + char** error_message +); +static char* jt_strdup(const char* s); // Simple strdup, as it's not C90 standard + +// --- Type Conversion Utilities (Placeholders for now) --- +static JsonTableError cjson_to_int(cJSON* item, int* out_val, char** error_message) { + if (item == NULL || cJSON_IsNull(item)) { // Handle explicit JSON null + *out_val = 0; // Default to 0 for null numbers + return JT_SUCCESS; + } + if (!cJSON_IsNumber(item)) { + if (error_message) *error_message = jt_strdup("Type error: Expected a number for INT conversion."); + return JT_ERROR_INVALID_TYPE; + } + *out_val = item->valueint; + return JT_SUCCESS; +} + +static JsonTableError cjson_to_double(cJSON* item, double* out_val, char** error_message) { + if (item == NULL || cJSON_IsNull(item)) { // Handle explicit JSON null + *out_val = 0.0; // Default to 0.0 for null numbers + return JT_SUCCESS; + } + if (!cJSON_IsNumber(item)) { + if (error_message) *error_message = jt_strdup("Type error: Expected a number for DOUBLE conversion."); + return JT_ERROR_INVALID_TYPE; + } + *out_val = item->valuedouble; + return JT_SUCCESS; +} + +static JsonTableError cjson_to_string(cJSON* item, char** out_val, char** error_message) { + if (!cJSON_IsString(item)) { + // Allow NULL for strings if item is NULL or not a string + if (item == NULL || cJSON_IsNull(item)) { + *out_val = NULL; // Represent JSON null as C NULL for strings + return JT_SUCCESS; + } + *error_message = jt_strdup("Type error: Expected a string for STRING conversion."); + return JT_ERROR_INVALID_TYPE; + } + *out_val = jt_strdup(item->valuestring); + if (!*out_val && item->valuestring) { // Check if strdup failed but original string existed + *error_message = jt_strdup("Memory allocation failed for string copy."); + return JT_ERROR_MEMORY_ALLOCATION; + } + return JT_SUCCESS; +} + +// --- Helper Functions --- +static char* jt_strdup(const char* s) { + if (!s) return NULL; + size_t len = strlen(s) + 1; + char* new_s = (char*)malloc(len); + if (!new_s) return NULL; + memcpy(new_s, s, len); + return new_s; +} + + +const char* json_table_error_string(JsonTableError error_code) { + switch (error_code) { + case JT_SUCCESS: return "Success"; + case JT_ERROR_PARSE_JSON: return "JSON Parsing Error"; + case JT_ERROR_INVALID_PATH: return "Invalid JSONPath Expression"; + case JT_ERROR_INVALID_TYPE: return "Data Type Mismatch/Conversion Error"; + case JT_ERROR_MEMORY_ALLOCATION: return "Memory Allocation Error"; + case JT_ERROR_COLUMN_PROCESSING: return "Error Processing Column Data"; + case JT_ERROR_UNKNOWN: return "Unknown Error"; + default: return "Undefined Error Code"; + } +} + +static size_t get_data_type_size(JsonTableDataType type) { + switch (type) { + case JT_INT: return sizeof(int); + case JT_DOUBLE: return sizeof(double); + case JT_STRING: return sizeof(char*); + default: return 0; // Should not happen if types are validated + } +} + +static void populate_column_offsets_and_sizes(JsonTableColumnDef* column_defs, size_t num_columns) { + size_t current_offset = 0; + for (size_t i = 0; i < num_columns; ++i) { + column_defs[i].size = get_data_type_size(column_defs[i].type); + column_defs[i].offset = current_offset; + current_offset += column_defs[i].size; + // Add padding here if alignment becomes an issue for certain architectures/types + } +} + +static size_t calculate_total_row_data_size(JsonTableColumnDef* column_defs, size_t num_columns) { + size_t total_size = 0; + for (size_t i = 0; i < num_columns; ++i) { + total_size += get_data_type_size(column_defs[i].type); + // Add padding here if necessary + } + return total_size; +} + + +// --- Core `json_table_process` Logic --- +JsonTableError json_table_process( + const char* json_string, + const char* row_path_expr, + JsonTableColumnDef* column_defs_input, // User provided column definitions + size_t num_columns, + JsonTable** result_table_ptr, + char** error_message) { + + if (!json_string || !row_path_expr || !column_defs_input || num_columns == 0 || !result_table_ptr) { + if (error_message) *error_message = jt_strdup("Invalid arguments to json_table_process."); + return JT_ERROR_UNKNOWN; // Or a more specific error for invalid args + } + + if (error_message) *error_message = NULL; // Initialize error message + + JsonTableError err_code = JT_SUCCESS; + cJSON* root_json = NULL; + JsonTable* table = NULL; + JsonPathResult row_items_result = {NULL, 0, 0}; // Use 0 for cJSON_bool false + + // 1. Parse the input JSON string + root_json = cJSON_Parse(json_string); + if (!root_json) { + if (error_message) { + const char* parse_end = cJSON_GetErrorPtr(); + if (parse_end) { + // Basic error message, could be more sophisticated + char temp_err_msg[256]; + snprintf(temp_err_msg, sizeof(temp_err_msg), "JSON parse error near: %.*s", 30, parse_end); + *error_message = jt_strdup(temp_err_msg); + } else { + *error_message = jt_strdup("JSON parse error (unknown location)."); + } + } + err_code = JT_ERROR_PARSE_JSON; + goto cleanup; + } + + // 2. Allocate and initialize the result table structure + table = (JsonTable*)calloc(1, sizeof(JsonTable)); + if (!table) { + if (error_message) *error_message = jt_strdup("Memory allocation failed for JsonTable."); + err_code = JT_ERROR_MEMORY_ALLOCATION; + goto cleanup; + } + *result_table_ptr = table; + + // Copy column definitions and calculate offsets/sizes + table->columnDefs = (JsonTableColumnDef*)malloc(num_columns * sizeof(JsonTableColumnDef)); + if (!table->columnDefs) { + if (error_message) *error_message = jt_strdup("Memory allocation failed for column definitions copy."); + err_code = JT_ERROR_MEMORY_ALLOCATION; + goto cleanup; + } + memcpy(table->columnDefs, column_defs_input, num_columns * sizeof(JsonTableColumnDef)); + table->columnCount = num_columns; + populate_column_offsets_and_sizes(table->columnDefs, table->columnCount); + table->totalRowDataSize = calculate_total_row_data_size(table->columnDefs, table->columnCount); + + + // 3. Identify Row Items using the row_path_expr + row_items_result = json_path_evaluate(root_json, row_path_expr); + if (row_items_result.count == 0 && strcmp(row_path_expr, "$") != 0) { // If path is "$" and it's not an array, it might be a single object row + // If path is just "$", and root is an object, treat it as a single row. + if (strcmp(row_path_expr, "$") == 0 && cJSON_IsObject(root_json)) { + // Special case: root object is the single row item. + // json_path_evaluate for "$" currently returns the root itself. + // We need to re-package it as if it was found by a path. + json_path_result_free(&row_items_result); // Free the previous result + row_items_result.items = (cJSON**)malloc(sizeof(cJSON*)); + if (!row_items_result.items) { + if (error_message) *error_message = jt_strdup("Memory allocation failed for single row item."); + err_code = JT_ERROR_MEMORY_ALLOCATION; + goto cleanup; + } + row_items_result.items[0] = root_json; + row_items_result.count = 1; + row_items_result.is_array_wildcard_result = 0; // Use 0 for cJSON_bool false + } else { + // No items found for rows, could be an empty result or invalid path + // This is not necessarily an error, could just be an empty table. + // If error_message is not NULL, we might want to indicate path evaluation failure. + // For now, assume it's an empty table result. + goto cleanup; // Successfully created an empty table + } + } + + + // 4. Process Each Row Item + table->rowCapacity = row_items_result.count > 0 ? row_items_result.count : 10; // Initial capacity + table->rows = (JsonTableRow*)malloc(table->rowCapacity * sizeof(JsonTableRow)); + if (!table->rows) { + if (error_message) *error_message = jt_strdup("Memory allocation failed for table rows."); + err_code = JT_ERROR_MEMORY_ALLOCATION; + goto cleanup; + } + + for (size_t i = 0; i < row_items_result.count; ++i) { + cJSON* current_row_json_item = row_items_result.items[i]; + if (!current_row_json_item) continue; // Should not happen if json_path_evaluate is correct + + if (table->rowCount >= table->rowCapacity) { + // Resize rows array + size_t new_capacity = table->rowCapacity * 2; + JsonTableRow* new_rows = (JsonTableRow*)realloc(table->rows, new_capacity * sizeof(JsonTableRow)); + if (!new_rows) { + if (error_message) *error_message = jt_strdup("Memory reallocation failed for table rows."); + err_code = JT_ERROR_MEMORY_ALLOCATION; + goto cleanup; + } + table->rows = new_rows; + table->rowCapacity = new_capacity; + } + + // Initialize data for the current row to NULL or zero + table->rows[table->rowCount].data = calloc(1, table->totalRowDataSize); + if (!table->rows[table->rowCount].data) { + if (error_message) *error_message = jt_strdup("Memory allocation failed for row data buffer."); + err_code = JT_ERROR_MEMORY_ALLOCATION; + goto cleanup; + } + + err_code = process_single_row_item(current_row_json_item, table, error_message); + if (err_code != JT_SUCCESS) { + // Error message should already be set by process_single_row_item + // We might need to free the partially allocated row data if an error occurs here + free(table->rows[table->rowCount].data); + table->rows[table->rowCount].data = NULL; + goto cleanup; + } + // Only increment rowCount if process_single_row_item was successful + // table->rowCount is incremented inside process_single_row_item upon success + } + +cleanup: + if (root_json) { + cJSON_Delete(root_json); + root_json = NULL; + } + json_path_result_free(&row_items_result); + + if (err_code != JT_SUCCESS && table) { + json_table_free(table); // Free partially constructed table on error + *result_table_ptr = NULL; // Ensure caller doesn't use partially freed table + } + + return err_code; +} + +static JsonTableError process_single_row_item( + cJSON* row_json_item, + JsonTable* table, // table_to_populate + char** error_message) { + + JsonTableError col_err = JT_SUCCESS; + void* current_row_data_buffer = table->rows[table->rowCount].data; + + for (size_t j = 0; j < table->columnCount; ++j) { + JsonTableColumnDef* col_def = &(table->columnDefs[j]); + JsonPathResult col_val_result = json_path_evaluate(row_json_item, col_def->jsonPath); + cJSON* val_item = NULL; + + if (col_val_result.count > 0) { + val_item = col_val_result.items[0]; // Take the first item if path resolves to multiple (should ideally be specific) + } + // If val_item is NULL here, it means the path didn't resolve or resolved to null. + // Type conversion functions should handle NULL val_item gracefully (e.g., for strings or nullable types). + + char* conv_error_msg = NULL; + switch (col_def->type) { + case JT_INT: + col_err = cjson_to_int(val_item, (int*)((char*)current_row_data_buffer + col_def->offset), &conv_error_msg); + break; + case JT_DOUBLE: + col_err = cjson_to_double(val_item, (double*)((char*)current_row_data_buffer + col_def->offset), &conv_error_msg); + break; + case JT_STRING: + col_err = cjson_to_string(val_item, (char**)((char*)current_row_data_buffer + col_def->offset), &conv_error_msg); + break; + default: + if (error_message && !*error_message) *error_message = jt_strdup("Unsupported column data type."); + col_err = JT_ERROR_INVALID_TYPE; + } + + json_path_result_free(&col_val_result); // Free result from column path evaluation + + if (col_err != JT_SUCCESS) { + if (error_message && !*error_message && conv_error_msg) { + char temp_err_msg[512]; + snprintf(temp_err_msg, sizeof(temp_err_msg), "Error processing column '%s': %s", col_def->columnName ? col_def->columnName : "[Unnamed]", conv_error_msg); + *error_message = jt_strdup(temp_err_msg); + free(conv_error_msg); // Free the message from the conversion function + } else if (error_message && !*error_message) { + char temp_err_msg[256]; + snprintf(temp_err_msg, sizeof(temp_err_msg), "Error processing column '%s'.", col_def->columnName ? col_def->columnName : "[Unnamed]"); + *error_message = jt_strdup(temp_err_msg); + } else if (conv_error_msg) { // error_message might already be set, or user didn't want one + free(conv_error_msg); + } + return JT_ERROR_COLUMN_PROCESSING; // Return immediately on first column error + } + } + table->rowCount++; // Increment only after all columns for the current row are processed successfully + return JT_SUCCESS; +} + + +// --- Cleanup Function --- +void json_table_free(JsonTable* table) { + if (!table) { + return; + } + + // Free data within each row + if (table->rows) { + for (size_t i = 0; i < table->rowCount; ++i) { + if (table->rows[i].data) { + // Free individual string members if any + for (size_t j = 0; j < table->columnCount; ++j) { + if (table->columnDefs[j].type == JT_STRING) { + char** str_ptr = (char**)((char*)table->rows[i].data + table->columnDefs[j].offset); + if (*str_ptr) { + free(*str_ptr); + *str_ptr = NULL; + } + } + } + free(table->rows[i].data); + table->rows[i].data = NULL; + } + } + free(table->rows); + table->rows = NULL; + } + + // Free column definitions copy + if (table->columnDefs) { + // If column names or paths were dynamically allocated FOR the table struct, free them here. + // Assuming they are const char* pointing to user's memory or literals for now. + free(table->columnDefs); + table->columnDefs = NULL; + } + + // Free the table structure itself + free(table); +} diff --git a/json_table.h b/json_table.h new file mode 100644 index 0000000000..15a98997f7 --- /dev/null +++ b/json_table.h @@ -0,0 +1,72 @@ +#ifndef JSON_TABLE_H +#define JSON_TABLE_H + +#include // For size_t + +// Forward declaration for cJSON +struct cJSON; + +// Error codes for json_table functions +typedef enum { + JT_SUCCESS = 0, + JT_ERROR_PARSE_JSON, + JT_ERROR_INVALID_PATH, + JT_ERROR_INVALID_TYPE, + JT_ERROR_MEMORY_ALLOCATION, + JT_ERROR_COLUMN_PROCESSING, + JT_ERROR_UNKNOWN +} JsonTableError; + +// Supported C data types for table columns +typedef enum { + JT_INT, + JT_DOUBLE, + JT_STRING, + // JT_BOOLEAN, // Add more types as needed + // JT_NULL, +} JsonTableDataType; + +// Definition for a column in the output table +typedef struct { + const char* columnName; // Name of the column (for reference/debugging) + const char* jsonPath; // JSONPath expression to extract the value for this column, relative to a row item + JsonTableDataType type; // Target C data type for this column + size_t offset; // Offset within a JsonTableRow's data buffer where this column's value is stored + size_t size; // Size of the data type (e.g., sizeof(int), sizeof(char*)) +} JsonTableColumnDef; + +// Represents a single row in the output table +// The 'data' buffer is a contiguous block of memory holding all column values for this row. +// Values are accessed by casting (base_address + offset) to the appropriate type pointer. +typedef struct { + void* data; // Buffer holding all column data for this row +} JsonTableRow; + +// Represents the entire output table +typedef struct { + JsonTableRow* rows; // Array of rows + size_t rowCount; // Number of rows in the table + size_t rowCapacity; // Current allocated capacity for rows array + + JsonTableColumnDef* columnDefs; // Copy of the column definitions used to create this table + size_t columnCount; // Number of columns + size_t totalRowDataSize; // Total size in bytes for the data buffer of a single row (sum of all column sizes) +} JsonTable; + +// Function to create and process the JSON data into a table +JsonTableError json_table_process( + const char* json_string, // Input JSON data as a C string + const char* row_path_expr, // JSONPath expression to identify the array of objects/elements that represent rows + JsonTableColumnDef* column_defs, // Array of column definitions + size_t num_columns, // Number of columns in column_defs + JsonTable** result_table, // Pointer to store the resulting table + char** error_message // Pointer to store an error message string if an error occurs +); + +// Function to free all memory allocated for a JsonTable +void json_table_free(JsonTable* table); + +// Helper function to get a string representation of an error code +const char* json_table_error_string(JsonTableError error_code); + +#endif // JSON_TABLE_H diff --git a/json_table.o b/json_table.o new file mode 100644 index 0000000000000000000000000000000000000000..72ba1e066ada7f7a9f6463e6c158333ea8bcdb58 GIT binary patch literal 22152 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 literal 0 HcmV?d00001 diff --git a/json_table_demo b/json_table_demo new file mode 100755 index 0000000000000000000000000000000000000000..cdad0111a6d7c564a951632a4c20cf22b89bf119 GIT binary patch literal 95384 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

!!Zhyvf9` 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 literal 0 HcmV?d00001 diff --git a/main.c b/main.c new file mode 100644 index 0000000000..7563378e9b --- /dev/null +++ b/main.c @@ -0,0 +1,188 @@ +#include +#include +#include "json_table.h" + +// Helper function to print the extracted table +void print_json_table(JsonTable* table) { + if (!table) { + printf("Table is NULL.\n"); + return; + } + + printf("Table has %zu rows and %zu columns.\n", table->rowCount, table->columnCount); + + // Print header + for (size_t j = 0; j < table->columnCount; ++j) { + printf("%-20s | ", table->columnDefs[j].columnName ? table->columnDefs[j].columnName : "N/A"); + } + printf("\n"); + for (size_t j = 0; j < table->columnCount; ++j) { + for(int k=0; k<20; ++k) printf("-"); + printf("---"); + } + printf("\n"); + + // Print rows + for (size_t i = 0; i < table->rowCount; ++i) { + for (size_t j = 0; j < table->columnCount; ++j) { + void* cell_data_ptr = (char*)table->rows[i].data + table->columnDefs[j].offset; + switch (table->columnDefs[j].type) { + case JT_INT: + printf("%-20d | ", *(int*)cell_data_ptr); + break; + case JT_DOUBLE: + printf("%-20.2f | ", *(double*)cell_data_ptr); + break; + case JT_STRING: + { + char* str_val = *(char**)cell_data_ptr; + printf("%-20s | ", str_val ? str_val : "NULL"); + } + break; + default: + printf("%-20s | ", "UNKN_TYPE"); + } + } + printf("\n"); + } + printf("\n"); +} + +int main() { + // Example 1: Simple array of objects + const char* json_string_1 = "{\n" + " \"storeName\": \"My Tech Store\",\n" + " \"products\": [\n" + " { \"id\": 1, \"name\": \"Laptop\", \"price\": 1200.50, \"specs\": { \"cpu\": \"i7\", \"ram\": 16 } },\n" + " { \"id\": 2, \"name\": \"Mouse\", \"price\": 25.99, \"specs\": { \"dpi\": 1200 } },\n" + " { \"id\": 3, \"name\": \"Keyboard\", \"price\": 75.00, \"tags\": [\"mechanical\", \"rgb\"] },\n" + " { \"id\": 4, \"name\": \"Monitor\", \"price\": 300.75, \"specs\": null }\n" + " ]\n" + "}"; + + printf("--- Example 1: Extracting products ---\n"); + JsonTableColumnDef cols1[] = { + {"ID", "$.id", JT_INT}, + {"Product Name", "$.name", JT_STRING}, + {"Price", "$.price", JT_DOUBLE}, + {"CPU", "$.specs.cpu", JT_STRING} // This will be NULL for items without it + }; + size_t num_cols1 = sizeof(cols1) / sizeof(cols1[0]); + + JsonTable* table1 = NULL; + char* error_msg1 = NULL; + + JsonTableError err1 = json_table_process(json_string_1, "$.products[*]", cols1, num_cols1, &table1, &error_msg1); + + if (err1 == JT_SUCCESS) { + printf("Successfully processed JSON for Example 1.\n"); + print_json_table(table1); + } else { + printf("Error in Example 1 (code %d): %s\n", err1, json_table_error_string(err1)); + if (error_msg1) { + printf("Details: %s\n", error_msg1); + } + } + if (error_msg1) free(error_msg1); + json_table_free(table1); + printf("-------------------------------------\n\n"); + + + // Example 2: JSON is a single object, to be treated as one row + const char* json_string_2 = "{ \"name\": \"Alice\", \"age\": 30, \"city\": \"New York\" }"; + + printf("--- Example 2: Single object as a row ---\n"); + JsonTableColumnDef cols2[] = { + {"Person Name", "$.name", JT_STRING}, + {"Age", "$.age", JT_INT}, + {"City", "$.city", JT_STRING}, + {"Country", "$.country", JT_STRING} // Non-existent field + }; + size_t num_cols2 = sizeof(cols2) / sizeof(cols2[0]); + JsonTable* table2 = NULL; + char* error_msg2 = NULL; + + // Using "$" as row path to indicate the root object is the row. + JsonTableError err2 = json_table_process(json_string_2, "$", cols2, num_cols2, &table2, &error_msg2); + + if (err2 == JT_SUCCESS) { + printf("Successfully processed JSON for Example 2.\n"); + print_json_table(table2); + } else { + printf("Error in Example 2 (code %d): %s\n", err2, json_table_error_string(err2)); + if (error_msg2) { + printf("Details: %s\n", error_msg2); + } + } + if (error_msg2) free(error_msg2); + json_table_free(table2); + printf("-------------------------------------\n\n"); + + + // Example 3: Malformed JSON + printf("--- Example 3: Malformed JSON ---\n"); + const char* json_string_3 = "{ \"name\": \"Bob\", \"age\": 40, city: \"London\" }"; // "city" key not quoted + JsonTable* table3 = NULL; + char* error_msg3 = NULL; + + JsonTableError err3 = json_table_process(json_string_3, "$", cols2, num_cols2, &table3, &error_msg3); + if (err3 == JT_SUCCESS) { + print_json_table(table3); + } else { + printf("Error in Example 3 (code %d): %s\n", err3, json_table_error_string(err3)); + if (error_msg3) { + printf("Details: %s\n", error_msg3); + } + } + if (error_msg3) free(error_msg3); + json_table_free(table3); // Safe to call even if table3 is NULL + printf("-------------------------------------\n\n"); + + // Example 4: Path to non-array for wildcard + printf("--- Example 4: Wildcard on non-array path ---\n"); + const char* json_string_4 = "{ \"data\": { \"value\": 123 } }"; + JsonTableColumnDef cols4[] = { {"Value", "$.value", JT_INT} }; + JsonTable* table4 = NULL; + char* error_msg4 = NULL; + JsonTableError err4 = json_table_process(json_string_4, "$.data[*]", cols4, 1, &table4, &error_msg4); + if (err4 == JT_SUCCESS) { + printf("Successfully processed JSON for Example 4 (expecting empty table).\n"); + print_json_table(table4); // Should be an empty table + } else { + printf("Error in Example 4 (code %d): %s\n", err4, json_table_error_string(err4)); + if (error_msg4) { + printf("Details: %s\n", error_msg4); + } + } + if (error_msg4) free(error_msg4); + json_table_free(table4); + printf("-------------------------------------\n\n"); + + + // Example 5: Array of simple types (not objects) + const char* json_string_5 = "[10, 20, 30, null, 40]"; + printf("--- Example 5: Array of simple types ---\n"); + JsonTableColumnDef cols5[] = { + {"Number", "$", JT_INT} // Path "$" here refers to the array element itself + }; + JsonTable* table5 = NULL; + char* error_msg5 = NULL; + // For an array of simple types, each element is a row. + // The column path `$` means "this element". + JsonTableError err5 = json_table_process(json_string_5, "$[*]", cols5, 1, &table5, &error_msg5); + if (err5 == JT_SUCCESS) { + printf("Successfully processed JSON for Example 5.\n"); + print_json_table(table5); + } else { + printf("Error in Example 5 (code %d): %s\n", err5, json_table_error_string(err5)); + if (error_msg5) { + printf("Details: %s\n", error_msg5); // Expect type error for 'null' if JT_INT is strict + } + } + if (error_msg5) free(error_msg5); + json_table_free(table5); + printf("-------------------------------------\n\n"); + + + return 0; +} diff --git a/main.o b/main.o new file mode 100644 index 0000000000000000000000000000000000000000..346ce339838b49e77f1872d286a89bd996305961 GIT binary patch literal 15840 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 Ro@Ueclm9=pnWCZV|1XYY>>2<7 literal 0 HcmV?d00001