/*
- Copyright (c) 2009 Dave Gamble
+ 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
/* cJSON */
/* JSON parser in C. */
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
-#include "cJSON.h"
-/* Determine the number of bits that an integer has using the preprocessor */
-#if INT_MAX == 32767
- /* 16 bits */
- #define INTEGER_SIZE 0x0010
-#elif INT_MAX == 2147483647
- /* 32 bits */
- #define INTEGER_SIZE 0x0100
-#elif INT_MAX == 9223372036854775807
- /* 64 bits */
- #define INTEGER_SIZE 0x1000
-#else
- #error "Failed to determine the size of an integer"
+#ifdef __GNUC__
+#pragma GCC visibility pop
#endif
-static const char *global_ep = NULL;
+#include "cJSON.h"
+
+/* define our own boolean type */
+#define true ((cJSON_bool)1)
+#define false ((cJSON_bool)0)
+
+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);
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 4) || (CJSON_VERSION_PATCH != 5)
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
-const char *cJSON_GetErrorPtr(void)
+CJSON_PUBLIC(const char*) cJSON_Version(void)
{
- return global_ep;
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+ return version;
}
/* case insensitive strcmp */
-static int cJSON_strcasecmp(const char *s1, const char *s2)
+static int cJSON_strcasecmp(const unsigned char *string1, const unsigned char *string2)
{
- if (!s1)
+ if (string1 == NULL)
{
- return (s1 == s2) ? 0 : 1; /* both NULL? */
+ if (string2 == NULL)
+ {
+ /* both NULL */
+ return 0;
+ }
+
+ return 1;
}
- if (!s2)
+
+ if (string2 == NULL)
{
return 1;
}
- for(; tolower(*(const unsigned char *)s1) == tolower(*(const unsigned char *)s2); ++s1, ++s2)
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
{
- if (*s1 == '\0')
+ if (*string1 == '\0')
{
return 0;
}
}
- return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+ return tolower(string1[0]) - tolower(string2[0]);
}
-static void *(*cJSON_malloc)(size_t sz) = malloc;
-static void (*cJSON_free)(void *ptr) = free;
+typedef struct internal_hooks
+{
+ void *(*allocate)(size_t size);
+ void (*deallocate)(void *pointer);
+ void *(*reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+static internal_hooks global_hooks = { malloc, free, realloc };
-static char* cJSON_strdup(const char* str)
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
{
- size_t len = 0;
- char *copy = NULL;
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
- len = strlen(str) + 1;
- if (!(copy = (char*)cJSON_malloc(len)))
+ length = strlen((const char*)string) + sizeof("");
+ if (!(copy = (unsigned char*)hooks->allocate(length)))
{
return NULL;
}
- memcpy(copy, str, len);
+ memcpy(copy, string, length);
return copy;
}
-void cJSON_InitHooks(cJSON_Hooks* hooks)
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
{
- if (!hooks)
+ if (hooks == NULL)
{
/* Reset hooks */
- cJSON_malloc = malloc;
- cJSON_free = free;
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
return;
}
- cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
- cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
+ 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(void)
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
- cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
if (node)
{
memset(node, '\0', sizeof(cJSON));
}
/* Delete a cJSON structure. */
-void cJSON_Delete(cJSON *c)
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
{
cJSON *next = NULL;
- while (c)
+ while (item != NULL)
{
- next = c->next;
- if (!(c->type & cJSON_IsReference) && c->child)
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
{
- cJSON_Delete(c->child);
+ cJSON_Delete(item->child);
}
- if (!(c->type & cJSON_IsReference) && c->valuestring)
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
{
- cJSON_free(c->valuestring);
+ global_hooks.deallocate(item->valuestring);
}
- if (!(c->type & cJSON_StringIsConst) && c->string)
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
- cJSON_free(c->string);
+ global_hooks.deallocate(item->string);
}
- cJSON_free(c);
- c = next;
+ global_hooks.deallocate(item);
+ item = next;
}
}
+typedef struct
+{
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+} 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))
+#define cannot_read(buffer, size) (!can_read(buffer, size))
+/* 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 const char *parse_number(cJSON *item, const char *num)
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{
- double n = 0;
- double sign = 1;
- double scale = 0;
- int subscale = 0;
- int signsubscale = 1;
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char number_c_string[64];
+ size_t i = 0;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and zero terminate the string
+ * because the number in the input buffer is not necessariliy zero terminated
+ * and strtod only works with zero terminated strings */
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+ {
+ switch (buffer_at_offset(input_buffer)[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ case '.':
+ break;
- /* Has sign? */
- if (*num == '-')
- {
- sign = -1;
- num++;
+ default:
+ goto loop_end;
+ }
+
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
}
- /* is zero */
- if (*num == '0')
+ loop_end:
+ number_c_string[i] = '\0';
+
+
+ number = strtod((const char*)number_c_string, (char**)&after_end);
+ if (number_c_string == after_end)
{
- num++;
+ return false; /* parse_error */
}
- /* Number? */
- if ((*num >= '1') && (*num <= '9'))
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= INT_MAX)
{
- do
- {
- n = (n * 10.0) + (*num++ - '0');
- }
- while ((*num >= '0') && (*num<='9'));
+ item->valueint = INT_MAX;
}
- /* Fractional part? */
- if ((*num == '.') && (num[1] >= '0') && (num[1] <= '9'))
+ else if (number <= INT_MIN)
{
- num++;
- do
- {
- n = (n *10.0) + (*num++ - '0');
- scale--;
- } while ((*num >= '0') && (*num <= '9'));
+ item->valueint = INT_MIN;
}
- /* Exponent? */
- if ((*num == 'e') || (*num == 'E'))
+ else
{
- num++;
- /* With sign? */
- if (*num == '+')
- {
- num++;
- }
- else if (*num == '-')
- {
- signsubscale = -1;
- num++;
- }
- /* Number? */
- while ((*num>='0') && (*num<='9'))
- {
- subscale = (subscale * 10) + (*num++ - '0');
- }
+ item->valueint = (int)number;
}
- /* number = +/- number.fraction * 10^+/- exponent */
- n = sign * n * pow(10.0, (scale + subscale * signsubscale));
-
- item->valuedouble = n;
- item->valueint = (int)n;
item->type = cJSON_Number;
- return num;
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ return true;
}
-/* calculate the next largest power of 2 */
-static int pow2gt (int x)
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
{
- --x;
-
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
-#if INTEGER_SIZE & 0x1110 /* at least 16 bit */
- x |= x >> 8;
-#endif
-#if INTEGER_SIZE & 0x1100 /* at least 32 bit */
- x |= x >> 16;
-#endif
-#if INT_SIZE & 0x1000 /* 64 bit */
- x |= x >> 32;
-#endif
+ if (number >= INT_MAX)
+ {
+ object->valueint = INT_MAX;
+ }
+ else if (number <= INT_MIN)
+ {
+ object->valueint = INT_MIN;
+ }
+ else
+ {
+ object->valueint = (int)number;
+ }
- return x + 1;
+ return object->valuedouble = number;
}
typedef struct
{
- char *buffer;
- int length;
- int offset;
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ cJSON_bool noalloc;
} printbuffer;
/* realloc printbuffer if necessary to have at least "needed" bytes more */
-static char* ensure(printbuffer *p, int needed)
+static unsigned char* ensure(printbuffer * const p, size_t needed, const internal_hooks * const hooks)
{
- char *newbuffer = NULL;
- int newsize = 0;
- if (!p || !p->buffer)
+ 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;
+
+ needed += p->offset + 1;
if (needed <= p->length)
{
return p->buffer + p->offset;
}
- newsize = pow2gt(needed);
- newbuffer = (char*)cJSON_malloc(newsize);
- if (!newbuffer)
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (newsize > (INT_MAX / 2))
+ {
+ /* overflow of int, use INT_MAX if possible */
+ if (needed <= INT_MAX)
+ {
+ newsize = INT_MAX;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
{
- cJSON_free(p->buffer);
- p->length = 0;
- p->buffer = NULL;
+ newsize = needed * 2;
+ }
- return NULL;
+ if (hooks->reallocate != NULL)
+ {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char*)hooks->reallocate(p->buffer, newsize);
}
- if (newbuffer)
+ else
{
- memcpy(newbuffer, p->buffer, p->length);
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char*)hooks->allocate(newsize);
+ if (!newbuffer)
+ {
+ hooks->deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ if (newbuffer)
+ {
+ memcpy(newbuffer, p->buffer, p->offset + 1);
+ }
+ hooks->deallocate(p->buffer);
}
- cJSON_free(p->buffer);
p->length = newsize;
p->buffer = newbuffer;
return newbuffer + p->offset;
}
-/* calculate the new length of the string in a printbuffer */
-static int update(const printbuffer *p)
+/* 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);
+}
+
+/* Removes trailing zeroes from the end of a printed number */
+static cJSON_bool trim_trailing_zeroes(printbuffer * const buffer)
{
- char *str = NULL;
- if (!p || !p->buffer)
+ size_t offset = 0;
+ unsigned char *content = NULL;
+
+ if ((buffer == NULL) || (buffer->buffer == NULL) || (buffer->offset < 1))
+ {
+ return false;
+ }
+
+ offset = buffer->offset - 1;
+ content = buffer->buffer;
+
+ while ((offset > 0) && (content[offset] == '0'))
+ {
+ offset--;
+ }
+ if ((offset > 0) && (content[offset] == '.'))
{
- return 0;
+ offset--;
}
- str = p->buffer + p->offset;
- return p->offset + strlen(str);
+ offset++;
+ content[offset] = '\0';
+
+ buffer->offset = offset;
+
+ return true;
}
/* Render the number nicely from the given item into a string. */
-static char *print_number(const cJSON *item, printbuffer *p)
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer, const internal_hooks * const hooks)
{
- char *str = NULL;
+ unsigned char *output_pointer = NULL;
double d = item->valuedouble;
- /* special case for 0. */
- if (d == 0)
+ int length = 0;
+ cJSON_bool trim_zeroes = true; /* should at the end be removed? */
+
+ if (output_buffer == NULL)
{
- if (p)
- {
- str = ensure(p, 2);
- }
- else
- {
- str = (char*)cJSON_malloc(2);
- }
- if (str)
- {
- strcpy(str,"0");
- }
+ return false;
}
- /* value is an int */
- else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN))
+
+ /* This is a nice tradeoff. */
+ output_pointer = ensure(output_buffer, 64, hooks);
+ if (output_pointer == NULL)
{
- if (p)
- {
- str = ensure(p, 21);
- }
- else
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if ((d * 0) != 0)
+ {
+ length = sprintf((char*)output_pointer, "null");
+ }
+ else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
+ {
+ /* integer */
+ length = sprintf((char*)output_pointer, "%.0f", d);
+ trim_zeroes = false; /* don't remove zeroes for "big integers" */
+ }
+ else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
+ {
+ length = sprintf((char*)output_pointer, "%e", d);
+ trim_zeroes = false; /* don't remove zeroes in engineering notation */
+ }
+ else
+ {
+ length = sprintf((char*)output_pointer, "%f", d);
+ }
+
+ /* sprintf failed */
+ if (length < 0)
+ {
+ return false;
+ }
+
+ output_buffer->offset += (size_t)length;
+
+ if (trim_zeroes)
+ {
+ return trim_trailing_zeroes(output_buffer);
+ }
+
+ 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'))
{
- /* 2^64+1 can be represented in 21 chars. */
- str = (char*)cJSON_malloc(21);
+ h += (unsigned int) input[i] - '0';
}
- if (str)
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
{
- sprintf(str, "%d", item->valueint);
+ h += (unsigned int) 10 + input[i] - 'A';
}
- }
- /* value is a floating point number */
- else
- {
- if (p)
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
{
- /* This is a nice tradeoff. */
- str = ensure(p, 64);
+ h += (unsigned int) 10 + input[i] - 'a';
}
- else
+ else /* invalid */
{
- /* This is a nice tradeoff. */
- str=(char*)cJSON_malloc(64);
+ return 0;
}
- if (str)
+
+ if (i < 3)
{
- /* This checks for NaN and Infinity */
- if ((d * 0) != 0)
- {
- sprintf(str, "null");
- }
- else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
- {
- sprintf(str, "%.0f", d);
- }
- else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
- {
- sprintf(str, "%e", d);
- }
- else
- {
- sprintf(str, "%f", d);
- }
+ /* shift left to make place for the next nibble */
+ h = h << 4;
}
}
- return str;
+
+ return h;
}
-/* parse 4 digit hexadecimal number */
-static unsigned parse_hex4(const char *str)
+/* 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)
{
- unsigned h = 0;
- /* first digit */
- if ((*str >= '0') && (*str <= '9'))
- {
- h += (*str) - '0';
- }
- else if ((*str >= 'A') && (*str <= 'F'))
- {
- h += 10 + (*str) - 'A';
- }
- else if ((*str >= 'a') && (*str <= 'f'))
- {
- h += 10 + (*str) - 'a';
- }
- else /* invalid */
+ 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)
{
- return 0;
+ /* input ends unexpectedly */
+ goto fail;
}
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
- /* second digit */
- h = h << 4;
- str++;
- if ((*str >= '0') && (*str <= '9'))
- {
- h += (*str) - '0';
- }
- else if ((*str >= 'A') && (*str <= 'F'))
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
{
- h += 10 + (*str) - 'A';
+ goto fail;
}
- else if ((*str >= 'a') && (*str <= 'f'))
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
{
- h += 10 + (*str) - 'a';
+ 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 /* invalid */
+ else
{
- return 0;
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
}
- /* third digit */
- h = h << 4;
- str++;
- if ((*str >= '0') && (*str <= '9'))
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80)
{
- h += (*str) - '0';
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
}
- else if ((*str >= 'A') && (*str <= 'F'))
+ else if (codepoint < 0x800)
{
- h += 10 + (*str) - 'A';
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
}
- else if ((*str >= 'a') && (*str <= 'f'))
+ else if (codepoint < 0x10000)
{
- h += 10 + (*str) - 'a';
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
}
- else /* invalid */
+ else if (codepoint <= 0x10FFFF)
{
- return 0;
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
}
-
- /* fourth digit */
- h = h << 4;
- str++;
- if ((*str >= '0') && (*str <= '9'))
+ else
{
- h += (*str) - '0';
+ /* invalid unicode codepoint */
+ goto fail;
}
- else if ((*str >= 'A') && (*str <= 'F'))
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
{
- h += 10 + (*str) - 'A';
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
}
- else if ((*str >= 'a') && (*str <= 'f'))
+ /* encode first byte */
+ if (utf8_length > 1)
{
- h += 10 + (*str) - 'a';
+ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
}
- else /* invalid */
+ else
{
- return 0;
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
}
- return h;
-}
+ *output_pointer += utf8_length;
-/* first bytes of UTF8 encoding for a given length in bytes */
-static const unsigned char firstByteMark[7] =
-{
- 0x00, /* should never happen */
- 0x00, /* 0xxxxxxx */
- 0xC0, /* 110xxxxx */
- 0xE0, /* 1110xxxx */
- 0xF0, /* 11110xxx */
- 0xF8,
- 0xFC
-};
+ return sequence_length;
+
+fail:
+ return 0;
+}
-/* Parse the input text into an unescaped cstring, and populate item. */
-static const char *parse_string(cJSON *item, const char *str, const char **ep)
+/* 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 internal_hooks * const hooks)
{
- const char *ptr = str + 1;
- const char *end_ptr =str + 1;
- char *ptr2 = NULL;
- char *out = NULL;
- int len = 0;
- unsigned uc = 0;
- unsigned uc2 = 0;
+ 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 (*str != '\"')
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"')
{
- *ep = str;
- return NULL;
+ goto fail;
}
- while ((*end_ptr != '\"') && *end_ptr && ++len)
{
- if (*end_ptr++ == '\\')
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while ((*input_end != '\"') && ((size_t)(input_end - input_buffer->content) < input_buffer->length))
{
- if (*end_ptr == '\0')
+ /* is escape sequence */
+ if (input_end[0] == '\\')
{
- /* prevent buffer overflow when last input character is a backslash */
- return NULL;
+ 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++;
}
- /* Skip escaped quotes. */
- end_ptr++;
+ input_end++;
+ }
+ if (*input_end != '\"')
+ {
+ goto fail; /* string ended unexpectedly */
}
- }
- /* This is at most how long we need for the string, roughly. */
- out = (char*)cJSON_malloc(len + 1);
- if (!out)
- {
- return NULL;
+ /* 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*)hooks->allocate(allocation_length + sizeof(""));
+ if (output == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
}
- item->valuestring = out; /* assign here so out will be deleted during cJSON_Delete() later */
- item->type = cJSON_String;
- ptr = str + 1;
- ptr2 = out;
+ output_pointer = output;
/* loop through the string literal */
- while (ptr < end_ptr)
+ while (input_pointer < input_end)
{
- if (*ptr != '\\')
+ if (*input_pointer != '\\')
{
- *ptr2++ = *ptr++;
+ *output_pointer++ = *input_pointer++;
}
/* escape sequence */
else
{
- ptr++;
- switch (*ptr)
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1)
+ {
+ goto fail;
+ }
+
+ switch (input_pointer[1])
{
case 'b':
- *ptr2++ = '\b';
+ *output_pointer++ = '\b';
break;
case 'f':
- *ptr2++ = '\f';
+ *output_pointer++ = '\f';
break;
case 'n':
- *ptr2++ = '\n';
+ *output_pointer++ = '\n';
break;
case 'r':
- *ptr2++ = '\r';
+ *output_pointer++ = '\r';
break;
case 't':
- *ptr2++ = '\t';
+ *output_pointer++ = '\t';
break;
case '\"':
case '\\':
case '/':
- *ptr2++ = *ptr;
+ *output_pointer++ = input_pointer[1];
break;
- case 'u':
- /* transcode utf16 to utf8. See RFC2781 and RFC3629. */
- uc = parse_hex4(ptr + 1); /* get the unicode char. */
- ptr += 4;
- if (ptr >= end_ptr)
- {
- /* invalid */
- *ep = str;
- return NULL;
- }
- /* check for invalid. */
- if (((uc >= 0xDC00) && (uc <= 0xDFFF)) || (uc == 0))
- {
- *ep = str;
- return NULL;
- }
-
- /* UTF16 surrogate pairs. */
- if ((uc >= 0xD800) && (uc<=0xDBFF))
- {
- if ((ptr + 6) > end_ptr)
- {
- /* invalid */
- *ep = str;
- return NULL;
- }
- if ((ptr[1] != '\\') || (ptr[2] != 'u'))
- {
- /* missing second-half of surrogate. */
- *ep = str;
- return NULL;
- }
- uc2 = parse_hex4(ptr + 3);
- ptr += 6; /* \uXXXX */
- if ((uc2 < 0xDC00) || (uc2 > 0xDFFF))
- {
- /* invalid second-half of surrogate. */
- *ep = str;
- return NULL;
- }
- /* calculate unicode codepoint from the surrogate pair */
- uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
- }
- /* encode as UTF8
- * takes at maximum 4 bytes to encode:
- * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
- len = 4;
- if (uc < 0x80)
- {
- /* normal ascii, encoding 0xxxxxxx */
- len = 1;
- }
- else if (uc < 0x800)
- {
- /* two bytes, encoding 110xxxxx 10xxxxxx */
- len = 2;
- }
- else if (uc < 0x10000)
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0)
{
- /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
- len = 3;
+ /* failed to convert UTF16-literal to UTF-8 */
+ goto fail;
}
- ptr2 += len;
-
- switch (len) {
- case 4:
- /* 10xxxxxx */
- *--ptr2 = ((uc | 0x80) & 0xBF);
- uc >>= 6;
- case 3:
- /* 10xxxxxx */
- *--ptr2 = ((uc | 0x80) & 0xBF);
- uc >>= 6;
- case 2:
- /* 10xxxxxx */
- *--ptr2 = ((uc | 0x80) & 0xBF);
- uc >>= 6;
- case 1:
- /* depending on the length in bytes this determines the
- * encoding ofthe first UTF8 byte */
- *--ptr2 = (uc | firstByteMark[len]);
- }
- ptr2 += len;
break;
+
default:
- *ep = str;
- return NULL;
+ goto fail;
}
- ptr++;
+ input_pointer += sequence_length;
}
}
- *ptr2 = '\0';
- if (*ptr == '\"')
+
+ /* 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)
+ {
+ hooks->deallocate(output);
+ }
+
+ if (input_pointer != NULL)
{
- ptr++;
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
}
- return ptr;
+ return false;
}
/* Render the cstring provided to an escaped version that can be printed. */
-static char *print_string_ptr(const char *str, printbuffer *p)
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer, const internal_hooks * const hooks)
{
- const char *ptr = NULL;
- char *ptr2 = NULL;
- char *out = NULL;
- int len = 0;
- int flag = 0;
- unsigned char token = '\0';
+ 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 (!str)
+ if (input == NULL)
{
- if (p)
+ output = ensure(output_buffer, sizeof("\"\""), hooks);
+ if (output == NULL)
{
- out = ensure(p, 3);
+ return false;
}
- else
- {
- out = (char*)cJSON_malloc(3);
- }
- if (!out)
- {
- return NULL;
- }
- strcpy(out, "\"\"");
+ strcpy((char*)output, "\"\"");
- return out;
+ return true;
}
/* set "flag" to 1 if something needs to be escaped */
- for (ptr = str; *ptr; ptr++)
+ 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("\"\""), hooks);
+ if (output == NULL)
{
- flag |= (((*ptr > 0) && (*ptr < 32)) /* unprintable characters */
- || (*ptr == '\"') /* double quote */
- || (*ptr == '\\')) /* backslash */
- ? 1
- : 0;
+ return false;
}
+
/* no characters have to be escaped */
- if (!flag)
+ if (escape_characters == 0)
{
- len = ptr - str;
- if (p)
- {
- out = ensure(p, len + 3);
- }
- else
- {
- out = (char*)cJSON_malloc(len + 3);
- }
- if (!out)
- {
- return NULL;
- }
-
- ptr2 = out;
- *ptr2++ = '\"';
- strcpy(ptr2, str);
- ptr2[len] = '\"';
- ptr2[len + 1] = '\0';
-
- return out;
- }
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
- ptr = str;
- /* calculate additional space that is needed for escaping */
- while ((token = *ptr) && ++len)
- {
- if (strchr("\"\\\b\f\n\r\t", token))
- {
- len++; /* +1 for the backslash */
- }
- else if (token < 32)
- {
- len += 5; /* +5 for \uXXXX */
- }
- ptr++;
+ return true;
}
- if (p)
- {
- out = ensure(p, len + 3);
- }
- else
- {
- out = (char*)cJSON_malloc(len + 3);
- }
- if (!out)
- {
- return NULL;
- }
-
- ptr2 = out;
- ptr = str;
- *ptr2++ = '\"';
+ output[0] = '\"';
+ output_pointer = output + 1;
/* copy the string */
- while (*ptr)
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
{
- if (((unsigned char)*ptr > 31) && (*ptr != '\"') && (*ptr != '\\'))
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
{
/* normal character, copy */
- *ptr2++ = *ptr++;
+ *output_pointer = *input_pointer;
}
else
{
/* character needs to be escaped */
- *ptr2++ = '\\';
- switch (token = *ptr++)
+ *output_pointer++ = '\\';
+ switch (*input_pointer)
{
case '\\':
- *ptr2++ = '\\';
+ *output_pointer = '\\';
break;
case '\"':
- *ptr2++ = '\"';
+ *output_pointer = '\"';
break;
case '\b':
- *ptr2++ = 'b';
+ *output_pointer = 'b';
break;
case '\f':
- *ptr2++ = 'f';
+ *output_pointer = 'f';
break;
case '\n':
- *ptr2++ = 'n';
+ *output_pointer = 'n';
break;
case '\r':
- *ptr2++ = 'r';
+ *output_pointer = 'r';
break;
case '\t':
- *ptr2++ = 't';
+ *output_pointer = 't';
break;
default:
/* escape and print as unicode codepoint */
- sprintf(ptr2, "u%04x", token);
- ptr2 += 5;
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
break;
}
}
}
- *ptr2++ = '\"';
- *ptr2++ = '\0';
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
- return out;
+ return true;
}
/* Invoke print_string_ptr (which is useful) on an item. */
-static char *print_string(const cJSON *item, printbuffer *p)
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p, const internal_hooks * const hooks)
{
- return print_string_ptr(item->valuestring, p);
+ return print_string_ptr((unsigned char*)item->valuestring, p, hooks);
}
/* Predeclare these prototypes. */
-static const char *parse_value(cJSON *item, const char *value, const char **ep);
-static char *print_value(const cJSON *item, int depth, int fmt, printbuffer *p);
-static const char *parse_array(cJSON *item, const char *value, const char **ep);
-static char *print_array(const cJSON *item, int depth, int fmt, printbuffer *p);
-static const char *parse_object(cJSON *item, const char *value, const char **ep);
-static char *print_object(const cJSON *item, int depth, int fmt, printbuffer *p);
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks);
+static cJSON_bool print_value(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks);
+static cJSON_bool print_array(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks);
+static cJSON_bool print_object(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks);
/* Utility to jump whitespace and cr/lf */
-static const char *skip(const char *in)
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
{
- while (in && *in && ((unsigned char)*in<=32))
+ if ((buffer == NULL) || (buffer->content == NULL))
+ {
+ return NULL;
+ }
+
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
{
- in++;
+ buffer->offset++;
}
- return in;
+ if (buffer->offset == buffer->length)
+ {
+ buffer->offset--;
+ }
+
+ return buffer;
}
/* Parse an object - create a new root, and populate. */
-cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated)
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{
- const char *end = NULL;
- /* use global error pointer if no specific one was given */
- const char **ep = return_parse_end ? return_parse_end : &global_ep;
- cJSON *c = cJSON_New_Item();
- *ep = NULL;
- if (!c) /* memory fail */
+ parse_buffer buffer = { 0, 0, 0 };
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL)
{
- return NULL;
+ goto fail;
}
- end = parse_value(c, skip(value), ep);
- if (!end)
+ buffer.content = (const unsigned char*)value;
+ buffer.length = strlen((const char*)value) + sizeof("");
+ buffer.offset = 0;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(&buffer), &global_hooks))
{
/* parse failure. ep is set. */
- cJSON_Delete(c);
- return NULL;
+ goto fail;
}
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated)
{
- end = skip(end);
- if (*end)
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
{
- cJSON_Delete(c);
- *ep = end;
- return NULL;
+ goto fail;
}
}
if (return_parse_end)
{
- *return_parse_end = end;
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
}
- return c;
+ 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;
+ }
+ else
+ {
+ global_error = local_error;
+ }
+ }
+
+ return NULL;
}
/* Default options for cJSON_Parse */
-cJSON *cJSON_Parse(const char *value)
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
{
return cJSON_ParseWithOpts(value, 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)
+{
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char*) hooks->allocate(256);
+ if (buffer->buffer == NULL)
+ {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, 0, format, buffer, hooks))
+ {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL)
+ {
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length);
+ buffer->buffer = NULL;
+ if (printed == NULL) {
+ goto fail;
+ }
+ }
+ else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL)
+ {
+ hooks->deallocate(buffer->buffer);
+ }
+
+ if (printed != NULL)
+ {
+ hooks->deallocate(printed);
+ }
+
+ return NULL;
+}
+
/* Render a cJSON item/entity/structure to text. */
-char *cJSON_Print(const cJSON *item)
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
{
- return print_value(item, 0, 1, 0);
+ return (char*)print(item, true, &global_hooks);
}
-char *cJSON_PrintUnformatted(const cJSON *item)
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
{
- return print_value(item, 0, 0, 0);
+ return (char*)print(item, false, &global_hooks);
}
-char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt)
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
{
printbuffer p;
- p.buffer = (char*)cJSON_malloc(prebuffer);
+
+ if (prebuffer < 0)
+ {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
if (!p.buffer)
{
return NULL;
}
- p.length = prebuffer;
+
+ p.length = (size_t)prebuffer;
p.offset = 0;
+ p.noalloc = false;
- return print_value(item, 0, fmt, &p);
+ if (!print_value(item, 0, fmt, &p, &global_hooks))
+ {
+ return NULL;
+ }
+
+ return (char*)p.buffer;
}
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
+{
+ printbuffer p;
+
+ if (len < 0)
+ {
+ return false;
+ }
+
+ p.buffer = (unsigned char*)buf;
+ p.length = (size_t)len;
+ p.offset = 0;
+ p.noalloc = true;
+ return print_value(item, 0, fmt, &p, &global_hooks);
+}
/* Parser core - when encountering text, process appropriately. */
-static const char *parse_value(cJSON *item, const char *value, const char **ep)
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks)
{
- if (!value)
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
{
- /* Fail on null. */
- return NULL;
+ return false; /* no input */
}
/* parse the different types of values */
- if (!strncmp(value, "null", 4))
+ /* null */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
{
item->type = cJSON_NULL;
- return value + 4;
+ input_buffer->offset += 4;
+ return true;
}
- if (!strncmp(value, "false", 5))
+ /* false */
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
{
item->type = cJSON_False;
- return value + 5;
+ input_buffer->offset += 5;
+ return true;
}
- if (!strncmp(value, "true", 4))
+ /* 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;
- return value + 4;
+ input_buffer->offset += 4;
+ return true;
}
- if (*value == '\"')
+ /* string */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
{
- return parse_string(item, value, ep);
+ return parse_string(item, input_buffer, hooks);
}
- if ((*value == '-') || ((*value >= '0') && (*value <= '9')))
+ /* 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, value);
+ return parse_number(item, input_buffer);
}
- if (*value == '[')
+ /* array */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
{
- return parse_array(item, value, ep);
+ return parse_array(item, input_buffer, hooks);
}
- if (*value == '{')
+ /* object */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
{
- return parse_object(item, value, ep);
+ return parse_object(item, input_buffer, hooks);
}
- /* failure. */
- *ep = value;
- return NULL;
+
+ return false;
}
/* Render a value to text. */
-static char *print_value(const cJSON *item, int depth, int fmt, printbuffer *p)
+static cJSON_bool print_value(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks)
{
- char *out = NULL;
+ unsigned char *output = NULL;
- if (!item)
+ if ((item == NULL) || (output_buffer == NULL))
{
- return NULL;
+ return false;
}
- if (p)
+
+ switch ((item->type) & 0xFF)
{
- switch ((item->type) & 0xFF)
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5, hooks);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6, hooks);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5, hooks);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer, hooks);
+
+ case cJSON_Raw:
{
- case cJSON_NULL:
- out = ensure(p, 5);
- if (out)
- {
- strcpy(out, "null");
- }
- break;
- case cJSON_False:
- out = ensure(p, 6);
- if (out)
- {
- strcpy(out, "false");
- }
- break;
- case cJSON_True:
- out = ensure(p, 5);
- if (out)
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
+ {
+ if (!output_buffer->noalloc)
{
- strcpy(out, "true");
+ hooks->deallocate(output_buffer->buffer);
}
- break;
- case cJSON_Number:
- out = print_number(item, p);
- break;
- case cJSON_String:
- out = print_string(item, p);
- break;
- case cJSON_Array:
- out = print_array(item, depth, fmt, p);
- break;
- case cJSON_Object:
- out = print_object(item, depth, fmt, p);
- break;
- }
- }
- else
- {
- switch ((item->type) & 0xFF)
- {
- case cJSON_NULL:
- out = cJSON_strdup("null");
- break;
- case cJSON_False:
- out = cJSON_strdup("false");
- break;
- case cJSON_True:
- out = cJSON_strdup("true");
- break;
- case cJSON_Number:
- out = print_number(item, 0);
- break;
- case cJSON_String:
- out = print_string(item, 0);
- break;
- case cJSON_Array:
- out = print_array(item, depth, fmt, 0);
- break;
- case cJSON_Object:
- out = print_object(item, depth, fmt, 0);
- break;
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length, hooks);
+ if (output == NULL)
+ {
+ return false;
+ }
+ memcpy(output, item->valuestring, raw_length);
+ return true;
}
- }
- return out;
+ case cJSON_String:
+ return print_string(item, output_buffer, hooks);
+
+ case cJSON_Array:
+ return print_array(item, depth, format, output_buffer, hooks);
+
+ case cJSON_Object:
+ return print_object(item, depth, format, output_buffer, hooks);
+
+ default:
+ return false;
+ }
}
/* Build an array from input text. */
-static const char *parse_array(cJSON *item,const char *value,const char **ep)
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks)
{
- cJSON *child = NULL;
- if (*value != '[')
- {
- /* not an array! */
- *ep = value;
- return NULL;
- }
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
- item->type = cJSON_Array;
- value = skip(value + 1);
- if (*value == ']')
+ if (buffer_at_offset(input_buffer)[0] != '[')
{
- /* empty array. */
- return value + 1;
+ /* not an array */
+ goto fail;
}
- item->child = child = cJSON_New_Item();
- if (!item->child)
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
{
- /* memory fail */
- return NULL;
+ /* empty array */
+ goto success;
}
- /* skip any spacing, get the value. */
- value = skip(parse_value(child, skip(value), ep));
- if (!value)
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
{
- return NULL;
+ 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 */
- while (*value == ',')
+ do
{
- cJSON *new_item = NULL;
- if (!(new_item = cJSON_New_Item()))
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(hooks);
+ if (new_item == NULL)
{
- /* memory fail */
- return NULL;
+ goto fail; /* allocation failure */
}
- /* add new item to end of the linked list */
- child->next = new_item;
- new_item->prev = child;
- child = new_item;
- /* go to the next comma */
- value = skip(parse_value(child, skip(value + 1), ep));
- if (!value)
+ /* attach next item to list */
+ if (head == NULL)
{
- /* memory fail */
- return 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, hooks))
+ {
+ 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 (*value == ']')
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
{
- /* end of array */
- return value + 1;
+ goto fail; /* expected end of array */
}
- /* malformed. */
- *ep = value;
+success:
+ item->type = cJSON_Array;
+ item->child = head;
- return NULL;
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
}
/* Render an array to text */
-static char *print_array(const cJSON *item, int depth, int fmt, printbuffer *p)
-{
- char **entries;
- char *out = NULL;
- char *ptr = NULL;
- char *ret = NULL;
- int len = 5;
- cJSON *child = item->child;
- int numentries = 0;
- int i = 0;
- int fail = 0;
- size_t tmplen = 0;
-
- /* How many entries in the array? */
- while (child)
- {
- numentries++;
- child = child->next;
- }
+static cJSON_bool print_array(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
- /* Explicitly handle numentries == 0 */
- if (!numentries)
+ if (output_buffer == NULL)
{
- if (p)
- {
- out = ensure(p, 3);
- }
- else
- {
- out = (char*)cJSON_malloc(3);
- }
- if (out)
- {
- strcpy(out,"[]");
- }
-
- return out;
+ return false;
}
- if (p)
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1, hooks);
+ if (output_pointer == NULL)
{
- /* Compose the output array. */
- /* opening square bracket */
- i = p->offset;
- ptr = ensure(p, 1);
- if (!ptr)
- {
- return NULL;
- }
- *ptr = '[';
- p->offset++;
-
- child = item->child;
- while (child && !fail)
- {
- print_value(child, depth + 1, fmt, p);
- p->offset = update(p);
- if (child->next)
- {
- len = fmt ? 2 : 1;
- ptr = ensure(p, len + 1);
- if (!ptr)
- {
- return NULL;
- }
- *ptr++ = ',';
- if(fmt)
- {
- *ptr++ = ' ';
- }
- *ptr = '\0';
- p->offset += len;
- }
- child = child->next;
- }
- ptr = ensure(p, 2);
- if (!ptr)
- {
- return NULL;
- }
- *ptr++ = ']';
- *ptr = '\0';
- out = (p->buffer) + i;
+ return false;
}
- else
- {
- /* Allocate an array to hold the pointers to all printed values */
- entries = (char**)cJSON_malloc(numentries * sizeof(char*));
- if (!entries)
- {
- return NULL;
- }
- memset(entries, '\0', numentries * sizeof(char*));
- /* Retrieve all the results: */
- child = item->child;
- while (child && !fail)
- {
- ret = print_value(child, depth + 1, fmt, 0);
- entries[i++] = ret;
- if (ret)
- {
- len += strlen(ret) + 2 + (fmt ? 1 : 0);
- }
- else
- {
- fail = 1;
- }
- child = child->next;
- }
+ *output_pointer = '[';
+ output_buffer->offset++;
- /* If we didn't fail, try to malloc the output string */
- if (!fail)
- {
- out = (char*)cJSON_malloc(len);
- }
- /* If that fails, we fail. */
- if (!out)
+ while (current_element != NULL)
+ {
+ if (!print_value(current_element, depth + 1, format, output_buffer, hooks))
{
- fail = 1;
+ return false;
}
-
- /* Handle failure. */
- if (fail)
+ update_offset(output_buffer);
+ if (current_element->next)
{
- /* free all the entries in the array */
- for (i = 0; i < numentries; i++)
+ length = (size_t) (format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1, hooks);
+ if (output_pointer == NULL)
{
- if (entries[i])
- {
- cJSON_free(entries[i]);
- }
+ return false;
}
- cJSON_free(entries);
- return NULL;
- }
-
- /* Compose the output array. */
- *out='[';
- ptr = out + 1;
- *ptr = '\0';
- for (i = 0; i < numentries; i++)
- {
- tmplen = strlen(entries[i]);
- memcpy(ptr, entries[i], tmplen);
- ptr += tmplen;
- if (i != (numentries - 1))
+ *output_pointer++ = ',';
+ if(format)
{
- *ptr++ = ',';
- if(fmt)
- {
- *ptr++ = ' ';
- }
- *ptr = '\0';
+ *output_pointer++ = ' ';
}
- cJSON_free(entries[i]);
+ *output_pointer = '\0';
+ output_buffer->offset += length;
}
- cJSON_free(entries);
- *ptr++ = ']';
- *ptr++ = '\0';
+ current_element = current_element->next;
}
- return out;
+ output_pointer = ensure(output_buffer, 2, hooks);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+
+ return true;
}
/* Build an object from the text. */
-static const char *parse_object(cJSON *item, const char *value, const char **ep)
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer, const internal_hooks * const hooks)
{
- cJSON *child = NULL;
- if (*value != '{')
- {
- /* not an object! */
- *ep = value;
- return NULL;
- }
-
- item->type = cJSON_Object;
- value = skip(value + 1);
- if (*value == '}')
- {
- /* empty object. */
- return value + 1;
- }
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
- child = cJSON_New_Item();
- item->child = child;
- if (!item->child)
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
{
- return NULL;
- }
- /* parse first key */
- value = skip(parse_string(child, skip(value), ep));
- if (!value)
- {
- return NULL;
+ goto fail; /* not an object */
}
- /* use string as key, not value */
- child->string = child->valuestring;
- child->valuestring = NULL;
- if (*value != ':')
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
{
- /* invalid object. */
- *ep = value;
- return NULL;
- }
- /* skip any spacing, get the value. */
- value = skip(parse_value(child, skip(value + 1), ep));
- if (!value)
- {
- return NULL;
+ goto success; /* empty object */
}
- while (*value == ',')
- {
- cJSON *new_item = NULL;
- if (!(new_item = cJSON_New_Item()))
- {
- /* memory fail */
- return NULL;
- }
- /* add to linked list */
- child->next = new_item;
- new_item->prev = child;
-
- child = new_item;
- value = skip(parse_string(child, skip(value + 1), ep));
- if (!value)
- {
- return NULL;
- }
-
- /* use string as key, not value */
- child->string = child->valuestring;
- child->valuestring = NULL;
-
- if (*value != ':')
- {
- /* invalid object. */
- *ep = value;
- return NULL;
- }
- /* skip any spacing, get the value. */
- value = skip(parse_value(child, skip(value + 1), ep));
- if (!value)
- {
- return NULL;
- }
- }
- /* end of object */
- if (*value == '}')
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
{
- return value + 1;
+ input_buffer->offset--;
+ goto fail;
}
- /* malformed */
- *ep = value;
- return NULL;
-}
-
-/* Render an object to text. */
-static char *print_object(const cJSON *item, int depth, int fmt, printbuffer *p)
-{
- char **entries = NULL;
- char **names = NULL;
- char *out = NULL;
- char *ptr = NULL;
- char *ret = NULL;
- char *str = NULL;
- int len = 7;
- int i = 0;
- int j = 0;
- cJSON *child = item->child;
- int numentries = 0;
- int fail = 0;
- size_t tmplen = 0;
-
- /* Count the number of entries. */
- while (child)
- {
- numentries++;
- child = child->next;
- }
-
- /* Explicitly handle empty object case */
- if (!numentries)
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
{
- if (p)
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(hooks);
+ if (new_item == NULL)
{
- out = ensure(p, fmt ? depth + 4 : 3);
+ goto fail; /* allocation failure */
}
- else
+
+ /* attach next item to list */
+ if (head == NULL)
{
- out = (char*)cJSON_malloc(fmt ? depth + 4 : 3);
+ /* start the linked list */
+ current_item = head = new_item;
}
- if (!out)
+ else
{
- return NULL;
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
}
- ptr = out;
- *ptr++ = '{';
- if (fmt) {
- *ptr++ = '\n';
- for (i = 0; i < depth; i++)
- {
- *ptr++ = '\t';
- }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer, hooks))
+ {
+ goto fail; /* faile to parse name */
}
- *ptr++ = '}';
- *ptr++ = '\0';
+ buffer_skip_whitespace(input_buffer);
- return out;
- }
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
- if (p)
- {
- /* Compose the output: */
- i = p->offset;
- len = fmt ? 2 : 1; /* fmt: {\n */
- ptr = ensure(p, len + 1);
- if (!ptr)
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
{
- return NULL;
+ goto fail; /* invalid object */
}
- *ptr++ = '{';
- if (fmt)
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer, hooks))
{
- *ptr++ = '\n';
+ goto fail; /* failed to parse value */
}
- *ptr = '\0';
- p->offset += len;
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
- child = item->child;
- depth++;
- while (child)
- {
- if (fmt)
- {
- ptr = ensure(p, depth);
- if (!ptr)
- {
- return NULL;
- }
- for (j = 0; j < depth; j++)
- {
- *ptr++ = '\t';
- }
- p->offset += depth;
- }
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+ {
+ goto fail; /* expected end of object */
+ }
- /* print key */
- print_string_ptr(child->string, p);
- p->offset = update(p);
+success:
+ item->type = cJSON_Object;
+ item->child = head;
- len = fmt ? 2 : 1;
- ptr = ensure(p, len);
- if (!ptr)
- {
- return NULL;
- }
- *ptr++ = ':';
- if (fmt)
- {
- *ptr++ = '\t';
- }
- p->offset+=len;
+ input_buffer->offset++;
+ return true;
- /* print value */
- print_value(child, depth, fmt, p);
- p->offset = update(p);
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
- /* print comma if not last */
- len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
- ptr = ensure(p, len + 1);
- if (!ptr)
- {
- return NULL;
- }
- if (child->next)
- {
- *ptr++ = ',';
- }
+ return false;
+}
- if (fmt)
- {
- *ptr++ = '\n';
- }
- *ptr = '\0';
- p->offset += len;
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, const size_t depth, const cJSON_bool format, printbuffer * const output_buffer, const internal_hooks * const hooks)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
- child = child->next;
- }
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
- ptr = ensure(p, fmt ? (depth + 1) : 2);
- if (!ptr)
- {
- return NULL;
- }
- if (fmt)
- {
- for (i = 0; i < (depth - 1); i++)
- {
- *ptr++ = '\t';
- }
- }
- *ptr++ = '}';
- *ptr = '\0';
- out = (p->buffer) + i;
+ /* Compose the output: */
+ length = (size_t) (format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1, hooks);
+ if (output_pointer == NULL)
+ {
+ return false;
}
- else
+
+ *output_pointer++ = '{';
+ if (format)
{
- /* Allocate space for the names and the objects */
- entries = (char**)cJSON_malloc(numentries * sizeof(char*));
- if (!entries)
- {
- return NULL;
- }
- names = (char**)cJSON_malloc(numentries * sizeof(char*));
- if (!names)
- {
- cJSON_free(entries);
- return NULL;
- }
- memset(entries, '\0', sizeof(char*) * numentries);
- memset(names, '\0', sizeof(char*) * numentries);
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
- /* Collect all the results into our arrays: */
- child = item->child;
- depth++;
- if (fmt)
- {
- len += depth;
- }
- while (child && !fail)
+ while (current_item)
+ {
+ if (format)
{
- names[i] = str = print_string_ptr(child->string, 0); /* print key */
- entries[i++] = ret = print_value(child, depth, fmt, 0);
- if (str && ret)
+ size_t i;
+ output_pointer = ensure(output_buffer, depth + 1, hooks);
+ if (output_pointer == NULL)
{
- len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0);
+ return false;
}
- else
+ for (i = 0; i < depth + 1; i++)
{
- fail = 1;
+ *output_pointer++ = '\t';
}
- child = child->next;
+ output_buffer->offset += depth + 1;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer, hooks))
+ {
+ return false;
}
+ update_offset(output_buffer);
- /* Try to allocate the output string */
- if (!fail)
+ length = (size_t) (format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length, hooks);
+ if (output_pointer == NULL)
{
- out = (char*)cJSON_malloc(len);
+ return false;
}
- if (!out)
+ *output_pointer++ = ':';
+ if (format)
{
- fail = 1;
+ *output_pointer++ = '\t';
}
+ output_buffer->offset += length;
- /* Handle failure */
- if (fail)
+ /* print value */
+ if (!print_value(current_item, depth + 1, format, output_buffer, hooks))
{
- /* free all the printed keys and values */
- for (i = 0; i < numentries; i++)
- {
- if (names[i])
- {
- cJSON_free(names[i]);
- }
- if (entries[i])
- {
- cJSON_free(entries[i]);
- }
- }
- cJSON_free(names);
- cJSON_free(entries);
- return NULL;
+ return false;
}
+ update_offset(output_buffer);
- /* Compose the output: */
- *out = '{';
- ptr = out + 1;
- if (fmt)
+ /* print comma if not last */
+ length = (size_t) ((format ? 1 : 0) + (current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1, hooks);
+ if (output_pointer == NULL)
{
- *ptr++ = '\n';
+ return false;
}
- *ptr = '\0';
- for (i = 0; i < numentries; i++)
+ if (current_item->next)
{
- if (fmt)
- {
- for (j = 0; j < depth; j++)
- {
- *ptr++='\t';
- }
- }
- tmplen = strlen(names[i]);
- memcpy(ptr, names[i], tmplen);
- ptr += tmplen;
- *ptr++ = ':';
- if (fmt)
- {
- *ptr++ = '\t';
- }
- strcpy(ptr, entries[i]);
- ptr += strlen(entries[i]);
- if (i != (numentries - 1))
- {
- *ptr++ = ',';
- }
- if (fmt)
- {
- *ptr++ = '\n';
- }
- *ptr = '\0';
- cJSON_free(names[i]);
- cJSON_free(entries[i]);
+ *output_pointer++ = ',';
}
- cJSON_free(names);
- cJSON_free(entries);
- if (fmt)
+ if (format)
{
- for (i = 0; i < (depth - 1); i++)
- {
- *ptr++ = '\t';
- }
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(output_buffer, format ? (depth + 2) : 2, hooks);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (format)
+ {
+ size_t i;
+ for (i = 0; i < (depth); i++)
+ {
+ *output_pointer++ = '\t';
}
- *ptr++ = '}';
- *ptr++ = '\0';
}
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
- return out;
+ return true;
}
/* Get Array size/item / object item. */
-int cJSON_GetArraySize(const cJSON *array)
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
{
cJSON *c = array->child;
- int i = 0;
+ size_t i = 0;
while(c)
{
i++;
c = c->next;
}
- return i;
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)i;
}
-cJSON *cJSON_GetArrayItem(const cJSON *array, int item)
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item)
{
cJSON *c = array ? array->child : NULL;
while (c && item > 0)
return c;
}
-cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string)
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string)
{
cJSON *c = object ? object->child : NULL;
- while (c && cJSON_strcasecmp(c->string, string))
+ while (c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string))
{
c = c->next;
}
return c;
}
-int cJSON_HasObjectItem(const cJSON *object,const char *string)
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (string == NULL))
+ {
+ return NULL;
+ }
+
+ current_element = object->child;
+ while ((current_element != NULL) && (strcmp(string, current_element->string) != 0))
+ {
+ current_element = current_element->next;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
{
return cJSON_GetObjectItem(object, string) ? 1 : 0;
}
}
/* Utility for handling references. */
-static cJSON *create_reference(const cJSON *item)
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
{
- cJSON *ref = cJSON_New_Item();
+ cJSON *ref = cJSON_New_Item(hooks);
if (!ref)
{
return NULL;
}
/* Add item to array/object. */
-void cJSON_AddItemToArray(cJSON *array, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
- cJSON *c = array->child;
- if (!item)
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL))
{
return;
}
- if (!c)
+
+ child = array->child;
+
+ if (child == NULL)
{
/* list is empty, start new one */
array->child = item;
else
{
/* append to the end */
- while (c->next)
+ while (child->next)
{
- c = c->next;
+ child = child->next;
}
- suffix_object(c, item);
+ suffix_object(child, item);
}
}
-void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
- if (!item)
- {
- return;
- }
-
- /* free old key and set new one */
- if (item->string)
- {
- cJSON_free(item->string);
- }
- item->string = cJSON_strdup(string);
-
- cJSON_AddItemToArray(object,item);
+ /* call cJSON_AddItemToObjectCS for code reuse */
+ cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item);
+ /* remove cJSON_StringIsConst flag */
+ item->type &= ~cJSON_StringIsConst;
}
+#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+
/* Add an item to an object with constant string as key */
-void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
if (!item)
{
}
if (!(item->type & cJSON_StringIsConst) && item->string)
{
- cJSON_free(item->string);
+ global_hooks.deallocate(item->string);
}
item->string = (char*)string;
item->type |= cJSON_StringIsConst;
cJSON_AddItemToArray(object, item);
}
+#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic pop
+#endif
-void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
{
- cJSON_AddItemToArray(array, create_reference(item));
+ cJSON_AddItemToArray(array, create_reference(item, &global_hooks));
}
-void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
{
- cJSON_AddItemToObject(object, string, create_reference(item));
+ cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks));
}
-cJSON *cJSON_DetachItemFromArray(cJSON *array, int which)
+static cJSON *DetachItemFromArray(cJSON *array, size_t which)
{
cJSON *c = array->child;
while (c && (which > 0))
return c;
}
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0)
+ {
+ return NULL;
+ }
+
+ return DetachItemFromArray(array, (size_t)which);
+}
-void cJSON_DeleteItemFromArray(cJSON *array, int which)
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
{
cJSON_Delete(cJSON_DetachItemFromArray(array, which));
}
-cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string)
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
{
- int i = 0;
+ size_t i = 0;
cJSON *c = object->child;
- while (c && cJSON_strcasecmp(c->string,string))
+ while (c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string))
{
i++;
c = c->next;
}
if (c)
{
- return cJSON_DetachItemFromArray(object, i);
+ return DetachItemFromArray(object, i);
}
return NULL;
}
-void cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
{
cJSON_Delete(cJSON_DetachItemFromObject(object, string));
}
/* Replace array/object items with new ones. */
-void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
{
cJSON *c = array->child;
while (c && (which > 0))
}
}
-void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+static void ReplaceItemInArray(cJSON *array, size_t which, cJSON *newitem)
{
cJSON *c = array->child;
while (c && (which > 0))
c->next = c->prev = NULL;
cJSON_Delete(c);
}
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0)
+ {
+ return;
+ }
+
+ ReplaceItemInArray(array, (size_t)which, newitem);
+}
-void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
{
- int i = 0;
+ size_t i = 0;
cJSON *c = object->child;
- while(c && cJSON_strcasecmp(c->string, string))
+ while(c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string))
{
i++;
c = c->next;
/* free the old string if not const */
if (!(newitem->type & cJSON_StringIsConst) && newitem->string)
{
- cJSON_free(newitem->string);
+ global_hooks.deallocate(newitem->string);
}
- newitem->string = cJSON_strdup(string);
- cJSON_ReplaceItemInArray(object, i, newitem);
+ newitem->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ ReplaceItemInArray(object, i, newitem);
}
}
/* Create basic types: */
-cJSON *cJSON_CreateNull(void)
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_NULL;
return item;
}
-cJSON *cJSON_CreateTrue(void)
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_True;
return item;
}
-cJSON *cJSON_CreateFalse(void)
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_False;
return item;
}
-cJSON *cJSON_CreateBool(int b)
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = b ? cJSON_True : cJSON_False;
return item;
}
-cJSON *cJSON_CreateNumber(double num)
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_Number;
item->valuedouble = num;
- item->valueint = (int)num;
+
+ /* use saturation in case of overflow */
+ if (num >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (num <= INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)num;
+ }
}
return item;
}
-cJSON *cJSON_CreateString(const char *string)
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_String;
- item->valuestring = cJSON_strdup(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_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 item;
}
-cJSON *cJSON_CreateArray(void)
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type=cJSON_Array;
return item;
}
-cJSON *cJSON_CreateObject(void)
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if (item)
{
item->type = cJSON_Object;
}
/* Create Arrays: */
-cJSON *cJSON_CreateIntArray(const int *numbers, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
{
- int i = 0;
+ size_t i = 0;
cJSON *n = NULL;
cJSON *p = NULL;
- cJSON *a = cJSON_CreateArray();
- for(i = 0; a && (i < count); i++)
+ cJSON *a = NULL;
+
+ if (count < 0)
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+ for(i = 0; a && (i < (size_t)count); i++)
{
n = cJSON_CreateNumber(numbers[i]);
if (!n)
return a;
}
-cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
{
- int i = 0;
+ size_t i = 0;
cJSON *n = NULL;
cJSON *p = NULL;
- cJSON *a = cJSON_CreateArray();
- for(i = 0; a && (i < count); i++)
+ cJSON *a = NULL;
+
+ if (count < 0)
{
- n = cJSON_CreateNumber(numbers[i]);
+ 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 a;
}
-cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
{
- int i = 0;
+ size_t i = 0;
cJSON *n = NULL;
cJSON *p = NULL;
- cJSON *a = cJSON_CreateArray();
- for(i = 0;a && (i < count); i++)
+ cJSON *a = NULL;
+
+ if (count < 0)
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0;a && (i < (size_t)count); i++)
{
n = cJSON_CreateNumber(numbers[i]);
if(!n)
return a;
}
-cJSON *cJSON_CreateStringArray(const char **strings, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count)
{
- int i = 0;
+ size_t i = 0;
cJSON *n = NULL;
cJSON *p = NULL;
- cJSON *a = cJSON_CreateArray();
- for (i = 0; a && (i < count); i++)
+ cJSON *a = NULL;
+
+ if (count < 0)
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++)
{
n = cJSON_CreateString(strings[i]);
if(!n)
}
/* Duplication */
-cJSON *cJSON_Duplicate(const cJSON *item, int recurse)
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
{
cJSON *newitem = NULL;
- cJSON *cptr = NULL;
- cJSON *nptr = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
cJSON *newchild = NULL;
/* Bail on bad ptr */
if (!item)
{
- return NULL;
+ goto fail;
}
/* Create new item */
- newitem = cJSON_New_Item();
+ newitem = cJSON_New_Item(&global_hooks);
if (!newitem)
{
- return NULL;
+ goto fail;
}
/* Copy over all vars */
newitem->type = item->type & (~cJSON_IsReference);
newitem->valuedouble = item->valuedouble;
if (item->valuestring)
{
- newitem->valuestring = cJSON_strdup(item->valuestring);
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
if (!newitem->valuestring)
{
- cJSON_Delete(newitem);
- return NULL;
+ goto fail;
}
}
if (item->string)
{
- newitem->string = cJSON_strdup(item->string);
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
if (!newitem->string)
{
- cJSON_Delete(newitem);
- return NULL;
+ goto fail;
}
}
/* If non-recursive, then we're done! */
return newitem;
}
/* Walk the ->next chain for the child. */
- cptr = item->child;
- while (cptr)
+ child = item->child;
+ while (child != NULL)
{
- newchild = cJSON_Duplicate(cptr, 1); /* Duplicate (with recurse) each item in the ->next chain */
+ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild)
{
- cJSON_Delete(newitem);
- return NULL;
+ goto fail;
}
- if (nptr)
+ if (next != NULL)
{
/* If newitem->child already set, then crosswire ->prev and ->next and move on */
- nptr->next = newchild;
- newchild->prev = nptr;
- nptr = newchild;
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
}
else
{
/* Set newitem->child and move to it */
- newitem->child = newchild; nptr = newchild;
+ newitem->child = newchild;
+ next = newchild;
}
- cptr = cptr->next;
+ child = child->next;
}
return newitem;
+
+fail:
+ if (newitem != NULL)
+ {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
}
-void cJSON_Minify(char *json)
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
{
- char *into = json;
+ unsigned char *into = (unsigned char*)json;
while (*json)
{
if (*json == ' ')
else if (*json == '\"')
{
/* string literals, which are \" sensitive. */
- *into++ = *json++;
+ *into++ = (unsigned char)*json++;
while (*json && (*json != '\"'))
{
if (*json == '\\')
{
- *into++=*json++;
+ *into++ = (unsigned char)*json++;
}
- *into++ = *json++;
+ *into++ = (unsigned char)*json++;
}
- *into++ = *json++;
+ *into++ = (unsigned char)*json++;
}
else
{
/* All other characters. */
- *into++ = *json++;
+ *into++ = (unsigned char)*json++;
}
}
/* 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;
+}