commit 030d0c14cc5e411758aaa4680e11ba82ebf61bc0
parent c66d95d11631801bb1b58bea93448521e30291a8
Author: Max Bruckner <max@maxbruckner.de>
Date: Fri, 3 Mar 2017 23:26:36 +0100
Merge branch 'develop' (Release 1.4.0)
Diffstat:
45 files changed, 2223 insertions(+), 920 deletions(-)
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
@@ -0,0 +1,54 @@
+Contribution Guidelines
+=======================
+
+Contributions to cJSON are welcome. If you find a bug or want to improve cJSON in another way, pull requests are appreciated.
+
+For bigger changes, in order to avoid wasted effort, please open an issue to discuss the technical details before creating a pull request.
+
+The further sections explain the process in more detail and provides some guidelines on how contributions should look like.
+
+Branches
+--------
+There are two branches to be aware of, the `master` and the `develop` branch. The `master` branch is reserved for the latest release, so only make pull requests to the `master` branch for small bug- or security fixes (these are usually just a few lines). In all other cases, please make a pull request to the `develop` branch.
+
+Coding Style
+------------
+The coding style has been discussed in [#24](https://github.com/DaveGamble/cJSON/issues/24). The basics are:
+
+* Use 4 tabs for indentation
+* No oneliners (conditions, loops, variable declarations ...)
+* Always use parenthesis for control structures
+* Don't implicitly rely on operator precedence, use round brackets in expressions. e.g. `(a > b) && (c < d)` instead of `a>b && c<d`
+* opening curly braces start in the next line
+* use spaces around operators
+* lines should not have trailing whitespace
+* use spaces between function parameters
+* use pronouncable variable names, not just a combination of letters
+
+Example:
+
+```c
+/* 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);
+}
+```
+
+Unit Tests
+----------
+cJSON uses the [Unity](https://github.com/ThrowTheSwitch/Unity) library for unit tests. The tests are located in the `tests` directory. In order to add a new test, either add it to one of the existing files (if it fits) or add a new C file for the test. That new file has to be added to the list of tests in `tests/CMakeLists.txt`.
+
+All new features have to be covered by unit tests.
+
+Other Notes
+-----------
+* Internal functions are to be declared static.
+* Wrap the return type of external function in the `CJSON_PUBLIC` macro.
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -1,15 +1,15 @@
set(CMAKE_LEGACY_CYGWIN_WIN32 0)
cmake_minimum_required(VERSION 2.8)
-subdirs(tests)
+subdirs(tests fuzzing)
include(GNUInstallDirs)
project(cJSON C)
set(PROJECT_VERSION_MAJOR 1)
-set(PROJECT_VERSION_MINOR 3)
-set(PROJECT_VERSION_PATCH 2)
+set(PROJECT_VERSION_MINOR 4)
+set(PROJECT_VERSION_PATCH 0)
set(CJSON_VERSION_SO 1)
set(CJSON_UTILS_VERSION_SO 1)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
@@ -40,6 +40,9 @@ if (ENABLE_CUSTOM_COMPILER_FLAGS)
-Wconversion
-Wc++-compat
-fstack-protector-strong
+ -Wcomma
+ -Wdouble-promotion
+ -Wparentheses
)
endif()
@@ -58,6 +61,16 @@ if (ENABLE_SANITIZERS)
)
endif()
+option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On)
+if (ENABLE_PUBLIC_SYMBOLS)
+ list(APPEND custom_compiler_flags -fvisibility=hidden)
+ add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY)
+endif()
+option(ENABLE_HIDDEN_SYMBOLS "Hide library symbols." Off)
+if (ENABLE_HIDDEN_SYMBOLS)
+ add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY)
+endif()
+
# apply custom compiler flags
foreach(compiler_flag ${custom_compiler_flags})
#remove problematic characters
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
@@ -21,6 +21,7 @@ Contributors
* [Linus Wallgren](https://github.com/ecksun)
* [Max Bruckner](https://github.com/FSMaxB)
* Mike Pontillo
+* [Mike Jerris](https://github.com/mjerris)
* Paulo Antonio Alvarez
* [Rafael Leal Dias](https://github.com/rafaeldias)
* [Rod Vagg](https://github.com/rvagg)
diff --git a/Makefile b/Makefile
@@ -10,7 +10,7 @@ UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c
LDLIBS = -lm
-LIBVERSION = 1.3.2
+LIBVERSION = 1.4.0
CJSON_SOVERSION = 1
UTILS_SOVERSION = 1
diff --git a/cJSON.c b/cJSON.c
@@ -23,6 +23,7 @@
/* cJSON */
/* JSON parser in C. */
+#pragma GCC visibility push(default)
#include <string.h>
#include <stdio.h>
#include <math.h>
@@ -30,26 +31,27 @@
#include <float.h>
#include <limits.h>
#include <ctype.h>
+#pragma GCC visibility pop
+
#include "cJSON.h"
/* define our own boolean type */
-typedef int cjbool;
-#define true ((cjbool)1)
-#define false ((cjbool)0)
+#define true ((cJSON_bool)1)
+#define false ((cJSON_bool)0)
static const unsigned char *global_ep = NULL;
-const char *cJSON_GetErrorPtr(void)
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
{
return (const char*) global_ep;
}
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
-#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 3) || (CJSON_VERSION_PATCH != 2)
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 4) || (CJSON_VERSION_PATCH != 0)
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif
-extern const char* cJSON_Version(void)
+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);
@@ -68,7 +70,7 @@ static int cJSON_strcasecmp(const unsigned char *s1, const unsigned char *s2)
{
return 1;
}
- for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
+ for(; tolower(*s1) == tolower(*s2); (void)++s1, ++s2)
{
if (*s1 == '\0')
{
@@ -79,10 +81,16 @@ static int cJSON_strcasecmp(const unsigned char *s1, const unsigned char *s2)
return tolower(*s1) - tolower(*s2);
}
-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 unsigned char* cJSON_strdup(const unsigned char* str)
+static unsigned char* cJSON_strdup(const unsigned char* str, const internal_hooks * const hooks)
{
size_t len = 0;
unsigned char *copy = NULL;
@@ -93,7 +101,7 @@ static unsigned char* cJSON_strdup(const unsigned char* str)
}
len = strlen((const char*)str) + 1;
- if (!(copy = (unsigned char*)cJSON_malloc(len)))
+ if (!(copy = (unsigned char*)hooks->allocate(len)))
{
return NULL;
}
@@ -102,24 +110,41 @@ static unsigned char* cJSON_strdup(const unsigned char* str)
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));
@@ -129,7 +154,7 @@ static cJSON *cJSON_New_Item(void)
}
/* Delete a cJSON structure. */
-void cJSON_Delete(cJSON *c)
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *c)
{
cJSON *next = NULL;
while (c)
@@ -141,13 +166,13 @@ void cJSON_Delete(cJSON *c)
}
if (!(c->type & cJSON_IsReference) && c->valuestring)
{
- cJSON_free(c->valuestring);
+ global_hooks.deallocate(c->valuestring);
}
if (!(c->type & cJSON_StringIsConst) && c->string)
{
- cJSON_free(c->string);
+ global_hooks.deallocate(c->string);
}
- cJSON_free(c);
+ global_hooks.deallocate(c);
c = next;
}
}
@@ -191,7 +216,7 @@ static const unsigned char *parse_number(cJSON * const item, const unsigned char
}
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
-double cJSON_SetNumberHelper(cJSON *object, double number)
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
{
if (number >= INT_MAX)
{
@@ -214,25 +239,26 @@ typedef struct
unsigned char *buffer;
size_t length;
size_t offset;
- cjbool noalloc;
+ cJSON_bool noalloc;
} printbuffer;
/* realloc printbuffer if necessary to have at least "needed" bytes more */
-static unsigned char* ensure(printbuffer *p, size_t needed)
+static unsigned char* ensure(printbuffer * const p, size_t needed, const internal_hooks * const hooks)
{
unsigned char *newbuffer = NULL;
size_t newsize = 0;
- if (needed > INT_MAX)
+ if ((p == NULL) || (p->buffer == NULL))
{
- /* sizes bigger than INT_MAX are currently not supported */
return NULL;
}
- if (!p || !p->buffer)
+ if (needed > INT_MAX)
{
+ /* sizes bigger than INT_MAX are currently not supported */
return NULL;
}
+
needed += p->offset;
if (needed <= p->length)
{
@@ -258,112 +284,132 @@ static unsigned char* ensure(printbuffer *p, size_t needed)
}
}
- newbuffer = (unsigned char*)cJSON_malloc(newsize);
- if (!newbuffer)
+ if (hooks->reallocate != NULL)
{
- cJSON_free(p->buffer);
- p->length = 0;
- p->buffer = NULL;
-
- return 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 size_t 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)
{
- const unsigned 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'))
{
- return 0;
+ offset--;
}
- str = p->buffer + p->offset;
+ if ((offset > 0) && (content[offset] == '.'))
+ {
+ offset--;
+ }
+
+ offset++;
+ content[offset] = '\0';
- return p->offset + strlen((const char*)str);
+ buffer->offset = offset;
+
+ return true;
}
/* Render the number nicely from the given item into a string. */
-static unsigned 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)
{
- unsigned 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 = (unsigned char*)cJSON_malloc(2);
- }
- if (str)
- {
- strcpy((char*)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
+ /* This checks for NaN and Infinity */
+ if ((d * 0) != 0)
{
- /* 2^64+1 can be represented in 21 chars. */
- str = (unsigned char*)cJSON_malloc(21);
+ length = sprintf((char*)output_pointer, "null");
}
- if (str)
+ else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
{
- sprintf((char*)str, "%d", item->valueint);
+ /* integer */
+ length = sprintf((char*)output_pointer, "%.0f", d);
+ trim_zeroes = false; /* don't remove zeroes for "big integers" */
}
- }
- /* value is a floating point number */
- else
- {
- if (p)
+ else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
{
- /* This is a nice tradeoff. */
- str = ensure(p, 64);
+ length = sprintf((char*)output_pointer, "%e", d);
+ trim_zeroes = false; /* don't remove zeroes in engineering notation */
}
else
{
- /* This is a nice tradeoff. */
- str = (unsigned char*)cJSON_malloc(64);
- }
- if (str)
- {
- /* This checks for NaN and Infinity */
- if ((d * 0) != 0)
- {
- sprintf((char*)str, "null");
- }
- else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
- {
- sprintf((char*)str, "%.0f", d);
- }
- else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
- {
- sprintf((char*)str, "%e", d);
- }
- else
- {
- sprintf((char*)str, "%f", d);
- }
+ length = sprintf((char*)output_pointer, "%f", d);
}
}
- return str;
+
+ /* 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 */
@@ -406,21 +452,13 @@ static unsigned parse_hex4(const unsigned char * const input)
* 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, const unsigned char **error_pointer)
{
- /* first bytes of UTF8 encoding for a given length in bytes */
- static const unsigned char firstByteMark[5] =
- {
- 0x00, /* should never happen */
- 0x00, /* 0xxxxxxx */
- 0xC0, /* 110xxxxx */
- 0xE0, /* 1110xxxx */
- 0xF0 /* 11110xxx */
- };
-
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)
{
@@ -492,16 +530,19 @@ static unsigned char utf16_literal_to_utf8(const unsigned char * const input_poi
{
/* 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
{
@@ -511,28 +552,22 @@ static unsigned char utf16_literal_to_utf8(const unsigned char * const input_poi
}
/* encode as utf8 */
- switch (utf8_length)
- {
- case 4:
- /* 10xxxxxx */
- (*output_pointer)[3] = (unsigned char)((codepoint | 0x80) & 0xBF);
- codepoint >>= 6;
- case 3:
- /* 10xxxxxx */
- (*output_pointer)[2] = (unsigned char)((codepoint | 0x80) & 0xBF);
- codepoint >>= 6;
- case 2:
- (*output_pointer)[1] = (unsigned char)((codepoint | 0x80) & 0xBF);
- codepoint >>= 6;
- case 1:
- /* depending on the length in bytes this determines the
- encoding of the first UTF8 byte */
- (*output_pointer)[0] = (unsigned char)((codepoint | firstByteMark[utf8_length]) & 0xFF);
- break;
- default:
- *error_pointer = first_sequence;
- goto fail;
+ 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;
@@ -542,7 +577,7 @@ fail:
}
/* Parse the input text into an unescaped cinput, and populate item. */
-static const unsigned char *parse_string(cJSON * const item, const unsigned char * const input, const unsigned char ** const error_pointer)
+static const unsigned char *parse_string(cJSON * const item, const unsigned char * const input, const unsigned char ** const error_pointer, const internal_hooks * const hooks)
{
const unsigned char *input_pointer = input + 1;
const unsigned char *input_end = input + 1;
@@ -582,7 +617,7 @@ static const unsigned char *parse_string(cJSON * const item, const unsigned char
/* This is at most how much we need for the output */
allocation_length = (size_t) (input_end - input) - skipped_bytes;
- output = (unsigned char*)cJSON_malloc(allocation_length + sizeof('\0'));
+ output = (unsigned char*)hooks->allocate(allocation_length + sizeof('\0'));
if (output == NULL)
{
goto fail; /* allocation failure */
@@ -653,174 +688,140 @@ static const unsigned char *parse_string(cJSON * const item, const unsigned char
fail:
if (output != NULL)
{
- cJSON_free(output);
+ hooks->deallocate(output);
}
return NULL;
}
/* Render the cstring provided to an escaped version that can be printed. */
-static unsigned char *print_string_ptr(const unsigned 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 unsigned char *ptr = NULL;
- unsigned char *ptr2 = NULL;
- unsigned char *out = NULL;
- size_t len = 0;
- cjbool flag = false;
- 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;
- /* empty string */
- if (!str)
+ if (output_buffer == NULL)
{
- if (p)
- {
- out = ensure(p, 3);
- }
- else
- {
- out = (unsigned char*)cJSON_malloc(3);
- }
- if (!out)
- {
- return NULL;
- }
- strcpy((char*)out, "\"\"");
-
- return out;
+ return false;
}
- /* set "flag" to 1 if something needs to be escaped */
- for (ptr = str; *ptr; ptr++)
- {
- flag |= (((*ptr > 0) && (*ptr < 32)) /* unprintable characters */
- || (*ptr == '\"') /* double quote */
- || (*ptr == '\\')) /* backslash */
- ? 1
- : 0;
- }
- /* no characters have to be escaped */
- if (!flag)
+ /* empty string */
+ if (input == NULL)
{
- len = (size_t)(ptr - str);
- if (p)
- {
- out = ensure(p, len + 3);
- }
- else
- {
- out = (unsigned char*)cJSON_malloc(len + 3);
- }
- if (!out)
+ output = ensure(output_buffer, sizeof("\"\""), hooks);
+ if (output == NULL)
{
- return NULL;
+ return false;
}
+ strcpy((char*)output, "\"\"");
- ptr2 = out;
- *ptr2++ = '\"';
- strcpy((char*)ptr2, (const char*)str);
- ptr2[len] = '\"';
- ptr2[len + 1] = '\0';
-
- return out;
+ return true;
}
- ptr = str;
- /* calculate additional space that is needed for escaping */
- while ((token = *ptr))
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++)
{
- ++len;
- if (strchr("\"\\\b\f\n\r\t", token))
+ if (strchr("\"\\\b\f\n\r\t", *input_pointer))
{
- len++; /* +1 for the backslash */
+ /* one character escape sequence */
+ escape_characters++;
}
- else if (token < 32)
+ else if (*input_pointer < 32)
{
- len += 5; /* +5 for \uXXXX */
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
}
- ptr++;
}
+ output_length = (size_t)(input_pointer - input) + escape_characters;
- if (p)
- {
- out = ensure(p, len + 3);
- }
- else
+ output = ensure(output_buffer, output_length + sizeof("\"\""), hooks);
+ if (output == NULL)
{
- out = (unsigned char*)cJSON_malloc(len + 3);
+ return false;
}
- if (!out)
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0)
{
- return NULL;
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
}
- 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 ((*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((char*)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 unsigned 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((unsigned char*)item->valuestring, p);
+ return print_string_ptr((unsigned char*)item->valuestring, p, hooks);
}
/* Predeclare these prototypes. */
-static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const ep);
-static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p);
-static const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const ep);
-static unsigned char *print_array(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p);
-static const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const ep);
-static unsigned char *print_object(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p);
+static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const ep, 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 const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const ep, 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 const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const ep, 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 unsigned char *skip(const unsigned char *in)
+static const unsigned char *skip_whitespace(const unsigned char *in)
{
while (in && *in && (*in <= 32))
{
@@ -831,19 +832,19 @@ static const unsigned char *skip(const unsigned char *in)
}
/* Parse an object - create a new root, and populate. */
-cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cjbool require_null_terminated)
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{
const unsigned char *end = NULL;
/* use global error pointer if no specific one was given */
const unsigned char **ep = return_parse_end ? (const unsigned char**)return_parse_end : &global_ep;
- cJSON *c = cJSON_New_Item();
+ cJSON *c = cJSON_New_Item(&global_hooks);
*ep = NULL;
if (!c) /* memory fail */
{
return NULL;
}
- end = parse_value(c, skip((const unsigned char*)value), ep);
+ end = parse_value(c, skip_whitespace((const unsigned char*)value), ep, &global_hooks);
if (!end)
{
/* parse failure. ep is set. */
@@ -854,7 +855,7 @@ cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cjb
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated)
{
- end = skip(end);
+ end = skip_whitespace(end);
if (*end)
{
cJSON_Delete(c);
@@ -871,23 +872,74 @@ cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cjb
}
/* 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 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);
+
+ /* copy the buffer over to a new one */
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ strncpy((char*)printed, (char*)buffer->buffer, 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 (char*)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 (char*)print_value(item, 0, 0, 0);
+ return (char*)print(item, false, &global_hooks);
}
-char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, cjbool fmt)
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
{
printbuffer p;
@@ -896,7 +948,7 @@ char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, cjbool fmt)
return NULL;
}
- p.buffer = (unsigned char*)cJSON_malloc((size_t)prebuffer);
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
if (!p.buffer)
{
return NULL;
@@ -906,10 +958,15 @@ char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, cjbool fmt)
p.offset = 0;
p.noalloc = false;
- return (char*)print_value(item, 0, fmt, &p);
+ if (!print_value(item, 0, fmt, &p, &global_hooks))
+ {
+ return NULL;
+ }
+
+ return (char*)p.buffer;
}
-int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cjbool fmt)
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
{
printbuffer p;
@@ -922,11 +979,11 @@ int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cjbool
p.length = (size_t)len;
p.offset = 0;
p.noalloc = true;
- return print_value(item, 0, fmt, &p) != NULL;
+ return print_value(item, 0, fmt, &p, &global_hooks);
}
/* Parser core - when encountering text, process appropriately. */
-static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const error_pointer)
+static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const error_pointer, const internal_hooks * const hooks)
{
if (input == NULL)
{
@@ -956,7 +1013,7 @@ static const unsigned char *parse_value(cJSON * const item, const unsigned char
/* string */
if (*input == '\"')
{
- return parse_string(item, input, error_pointer);
+ return parse_string(item, input, error_pointer, hooks);
}
/* number */
if ((*input == '-') || ((*input >= '0') && (*input <= '9')))
@@ -966,12 +1023,12 @@ static const unsigned char *parse_value(cJSON * const item, const unsigned char
/* array */
if (*input == '[')
{
- return parse_array(item, input, error_pointer);
+ return parse_array(item, input, error_pointer, hooks);
}
/* object */
if (*input == '{')
{
- return parse_object(item, input, error_pointer);
+ return parse_object(item, input, error_pointer, hooks);
}
/* failure. */
@@ -980,116 +1037,85 @@ static const unsigned char *parse_value(cJSON * const item, const unsigned char
}
/* Render a value to text. */
-static unsigned char *print_value(const cJSON *item, size_t depth, cjbool 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)
{
- unsigned 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((char*)out, "null");
- }
- break;
- case cJSON_False:
- out = ensure(p, 6);
- if (out)
- {
- strcpy((char*)out, "false");
- }
- break;
- case cJSON_True:
- out = ensure(p, 5);
- if (out)
- {
- strcpy((char*)out, "true");
- }
- break;
- case cJSON_Number:
- out = print_number(item, p);
- break;
- case cJSON_Raw:
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
{
- size_t raw_length = 0;
- if (item->valuestring == NULL)
+ if (!output_buffer->noalloc)
{
- if (!p->noalloc)
- {
- cJSON_free(p->buffer);
- }
- out = NULL;
- break;
+ hooks->deallocate(output_buffer->buffer);
}
+ return false;
+ }
- raw_length = strlen(item->valuestring) + sizeof('\0');
- out = ensure(p, raw_length);
- if (out)
- {
- memcpy(out, item->valuestring, raw_length);
- }
- break;
+ raw_length = strlen(item->valuestring) + sizeof('\0');
+ output = ensure(output_buffer, raw_length, hooks);
+ if (output == NULL)
+ {
+ return false;
}
- 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;
- default:
- out = NULL;
- break;
+ memcpy(output, item->valuestring, raw_length);
+ return true;
}
+
+ 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;
}
- else
- {
- switch ((item->type) & 0xFF)
- {
- case cJSON_NULL:
- out = cJSON_strdup((const unsigned char*)"null");
- break;
- case cJSON_False:
- out = cJSON_strdup((const unsigned char*)"false");
- break;
- case cJSON_True:
- out = cJSON_strdup((const unsigned char*)"true");
- break;
- case cJSON_Number:
- out = print_number(item, 0);
- break;
- case cJSON_Raw:
- out = cJSON_strdup((unsigned char*)item->valuestring);
- 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;
- default:
- out = NULL;
- break;
- }
- }
-
- return out;
}
/* Build an array from input text. */
-static const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const error_pointer)
+static const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const error_pointer, const internal_hooks * const hooks)
{
cJSON *head = NULL; /* head of the linked list */
cJSON *current_item = NULL;
@@ -1101,7 +1127,7 @@ static const unsigned char *parse_array(cJSON * const item, const unsigned char
goto fail;
}
- input = skip(input + 1); /* skip whitespace */
+ input = skip_whitespace(input + 1);
if (*input == ']')
{
/* empty array */
@@ -1114,7 +1140,7 @@ static const unsigned char *parse_array(cJSON * const item, const unsigned char
do
{
/* allocate next item */
- cJSON *new_item = cJSON_New_Item();
+ cJSON *new_item = cJSON_New_Item(hooks);
if (new_item == NULL)
{
goto fail; /* allocation failure */
@@ -1135,9 +1161,9 @@ static const unsigned char *parse_array(cJSON * const item, const unsigned char
}
/* parse next value */
- input = skip(input + 1); /* skip whitespace before value */
- input = parse_value(current_item, input, error_pointer);
- input = skip(input); /* skip whitespace after value */
+ input = skip_whitespace(input + 1);
+ input = parse_value(current_item, input, error_pointer, hooks);
+ input = skip_whitespace(input);
if (input == NULL)
{
goto fail; /* failed to parse value */
@@ -1167,176 +1193,67 @@ fail:
}
/* Render an array to text */
-static unsigned char *print_array(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p)
-{
- unsigned char **entries;
- unsigned char *out = NULL;
- unsigned char *ptr = NULL;
- unsigned char *ret = NULL;
- size_t len = 5;
- cJSON *child = item->child;
- size_t numentries = 0;
- size_t i = 0;
- cjbool fail = false;
- size_t tmplen = 0;
+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;
- /* How many entries in the array? */
- while (child)
+ if (output_buffer == NULL)
{
- numentries++;
- child = child->next;
+ return false;
}
- /* Explicitly handle numentries == 0 */
- if (!numentries)
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1, hooks);
+ if (output_pointer == NULL)
{
- if (p)
- {
- out = ensure(p, 3);
- }
- else
- {
- out = (unsigned char*)cJSON_malloc(3);
- }
- if (out)
- {
- strcpy((char*)out, "[]");
- }
-
- return out;
+ return false;
}
- if (p)
- {
- /* Compose the output array. */
- /* opening square bracket */
- i = p->offset;
- ptr = ensure(p, 1);
- if (!ptr)
- {
- return NULL;
- }
- *ptr = '[';
- p->offset++;
+ *output_pointer = '[';
+ output_buffer->offset++;
- child = item->child;
- while (child && !fail)
- {
- if (!print_value(child, depth + 1, fmt, p))
- {
- return NULL;
- }
- 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;
- }
- else
+ while (current_element != NULL)
{
- /* Allocate an array to hold the pointers to all printed values */
- entries = (unsigned char**)cJSON_malloc(numentries * sizeof(unsigned char*));
- if (!entries)
+ if (!print_value(current_element, depth + 1, format, output_buffer, hooks))
{
- return NULL;
+ return false;
}
- memset(entries, '\0', numentries * sizeof(unsigned char*));
-
- /* Retrieve all the results: */
- child = item->child;
- while (child && !fail)
+ update_offset(output_buffer);
+ if (current_element->next)
{
- ret = print_value(child, depth + 1, fmt, 0);
- entries[i++] = ret;
- if (ret)
+ length = format ? 2 : 1;
+ output_pointer = ensure(output_buffer, length + 1, hooks);
+ if (output_pointer == NULL)
{
- len += strlen((char*)ret) + 2 + (fmt ? 1 : 0);
+ return false;
}
- else
+ *output_pointer++ = ',';
+ if(format)
{
- fail = true;
- }
- child = child->next;
- }
-
- /* If we didn't fail, try to malloc the output string */
- if (!fail)
- {
- out = (unsigned char*)cJSON_malloc(len);
- }
- /* If that fails, we fail. */
- if (!out)
- {
- fail = true;
- }
-
- /* Handle failure. */
- if (fail)
- {
- /* free all the entries in the array */
- for (i = 0; i < numentries; i++)
- {
- if (entries[i])
- {
- cJSON_free(entries[i]);
- }
+ *output_pointer++ = ' ';
}
- cJSON_free(entries);
- return NULL;
+ *output_pointer = '\0';
+ output_buffer->offset += length;
}
+ current_element = current_element->next;
+ }
- /* Compose the output array. */
- *out='[';
- ptr = out + 1;
- *ptr = '\0';
- for (i = 0; i < numentries; i++)
- {
- tmplen = strlen((char*)entries[i]);
- memcpy(ptr, entries[i], tmplen);
- ptr += tmplen;
- if (i != (numentries - 1))
- {
- *ptr++ = ',';
- if(fmt)
- {
- *ptr++ = ' ';
- }
- *ptr = '\0';
- }
- cJSON_free(entries[i]);
- }
- cJSON_free(entries);
- *ptr++ = ']';
- *ptr++ = '\0';
+ output_pointer = ensure(output_buffer, 2, hooks);
+ if (output_pointer == NULL)
+ {
+ return false;
}
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
- return out;
+ return true;
}
/* Build an object from the text. */
-static const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const error_pointer)
+static const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const error_pointer, const internal_hooks * const hooks)
{
cJSON *head = NULL; /* linked list head */
cJSON *current_item = NULL;
@@ -1347,7 +1264,7 @@ static const unsigned char *parse_object(cJSON * const item, const unsigned char
goto fail; /* not an object */
}
- input = skip(input + 1); /* skip whitespace */
+ input = skip_whitespace(input + 1);
if (*input == '}')
{
goto success; /* empty object */
@@ -1359,7 +1276,7 @@ static const unsigned char *parse_object(cJSON * const item, const unsigned char
do
{
/* allocate next item */
- cJSON *new_item = cJSON_New_Item();
+ cJSON *new_item = cJSON_New_Item(hooks);
if (new_item == NULL)
{
goto fail; /* allocation failure */
@@ -1380,9 +1297,9 @@ static const unsigned char *parse_object(cJSON * const item, const unsigned char
}
/* parse the name of the child */
- input = skip(input + 1); /* skip whitespaces before name */
- input = parse_string(current_item, input, error_pointer);
- input = skip(input); /* skip whitespaces after name */
+ input = skip_whitespace(input + 1);
+ input = parse_string(current_item, input, error_pointer, hooks);
+ input = skip_whitespace(input);
if (input == NULL)
{
goto fail; /* faile to parse name */
@@ -1399,9 +1316,9 @@ static const unsigned char *parse_object(cJSON * const item, const unsigned char
}
/* parse the value */
- input = skip(input + 1); /* skip whitespaces before value */
- input = parse_value(current_item, input, error_pointer);
- input = skip(input); /* skip whitespaces after the value */
+ input = skip_whitespace(input + 1);
+ input = parse_value(current_item, input, error_pointer, hooks);
+ input = skip_whitespace(input);
if (input == NULL)
{
goto fail; /* failed to parse value */
@@ -1431,288 +1348,119 @@ fail:
}
/* Render an object to text. */
-static unsigned char *print_object(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p)
-{
- unsigned char **entries = NULL;
- unsigned char **names = NULL;
- unsigned char *out = NULL;
- unsigned char *ptr = NULL;
- unsigned char *ret = NULL;
- unsigned char *str = NULL;
- size_t len = 7;
- size_t i = 0;
- size_t j = 0;
- cJSON *child = item->child;
- size_t numentries = 0;
- cjbool fail = false;
- size_t tmplen = 0;
+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;
- /* Count the number of entries. */
- while (child)
+ if (output_buffer == NULL)
{
- numentries++;
- child = child->next;
+ return false;
}
- /* Explicitly handle empty object case */
- if (!numentries)
+ /* Compose the output: */
+ length = format ? 2 : 1; /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1, hooks);
+ if (output_pointer == NULL)
{
- if (p)
- {
- out = ensure(p, fmt ? depth + 4 : 3);
- }
- else
- {
- out = (unsigned char*)cJSON_malloc(fmt ? depth + 4 : 3);
- }
- if (!out)
- {
- return NULL;
- }
- ptr = out;
- *ptr++ = '{';
- if (fmt) {
- *ptr++ = '\n';
- for (i = 0; i < depth; i++)
- {
- *ptr++ = '\t';
- }
- }
- *ptr++ = '}';
- *ptr++ = '\0';
-
- return out;
+ return false;
}
- if (p)
+ *output_pointer++ = '{';
+ if (format)
{
- /* Compose the output: */
- i = p->offset;
- len = fmt ? 2 : 1; /* fmt: {\n */
- ptr = ensure(p, len + 1);
- if (!ptr)
- {
- return NULL;
- }
-
- *ptr++ = '{';
- if (fmt)
- {
- *ptr++ = '\n';
- }
- *ptr = '\0';
- p->offset += len;
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
- child = item->child;
- depth++;
- while (child)
+ while (current_item)
+ {
+ if (format)
{
- if (fmt)
- {
- ptr = ensure(p, depth);
- if (!ptr)
- {
- return NULL;
- }
- for (j = 0; j < depth; j++)
- {
- *ptr++ = '\t';
- }
- p->offset += depth;
- }
-
- /* print key */
- if (!print_string_ptr((unsigned char*)child->string, p))
- {
- return NULL;
- }
- p->offset = update(p);
-
- len = fmt ? 2 : 1;
- ptr = ensure(p, len);
- if (!ptr)
- {
- return NULL;
- }
- *ptr++ = ':';
- if (fmt)
- {
- *ptr++ = '\t';
- }
- p->offset+=len;
-
- /* print value */
- if (!print_value(child, depth, fmt, p))
- {
- return NULL;
- };
- p->offset = update(p);
-
- /* print comma if not last */
- len = (size_t) (fmt ? 1 : 0) + (child->next ? 1 : 0);
- ptr = ensure(p, len + 1);
- if (!ptr)
+ size_t i;
+ output_pointer = ensure(output_buffer, depth + 1, hooks);
+ if (output_pointer == NULL)
{
- return NULL;
+ return false;
}
- if (child->next)
+ for (i = 0; i < depth + 1; i++)
{
- *ptr++ = ',';
+ *output_pointer++ = '\t';
}
-
- if (fmt)
- {
- *ptr++ = '\n';
- }
- *ptr = '\0';
- p->offset += len;
-
- child = child->next;
+ output_buffer->offset += depth + 1;
}
- ptr = ensure(p, fmt ? (depth + 1) : 2);
- if (!ptr)
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer, hooks))
{
- return NULL;
+ return false;
}
- if (fmt)
+ update_offset(output_buffer);
+
+ length = format ? 2 : 1;
+ output_pointer = ensure(output_buffer, length, hooks);
+ if (output_pointer == NULL)
{
- for (i = 0; i < (depth - 1); i++)
- {
- *ptr++ = '\t';
- }
+ return false;
}
- *ptr++ = '}';
- *ptr = '\0';
- out = (p->buffer) + i;
- }
- else
- {
- /* Allocate space for the names and the objects */
- entries = (unsigned char**)cJSON_malloc(numentries * sizeof(unsigned char*));
- if (!entries)
+ *output_pointer++ = ':';
+ if (format)
{
- return NULL;
+ *output_pointer++ = '\t';
}
- names = (unsigned char**)cJSON_malloc(numentries * sizeof(unsigned char*));
- if (!names)
- {
- cJSON_free(entries);
- return NULL;
- }
- memset(entries, '\0', sizeof(unsigned char*) * numentries);
- memset(names, '\0', sizeof(unsigned char*) * numentries);
+ output_buffer->offset += length;
- /* Collect all the results into our arrays: */
- child = item->child;
- depth++;
- if (fmt)
+ /* print value */
+ if (!print_value(current_item, depth + 1, format, output_buffer, hooks))
{
- len += depth;
- }
- while (child && !fail)
- {
- names[i] = str = print_string_ptr((unsigned char*)child->string, 0); /* print key */
- entries[i++] = ret = print_value(child, depth, fmt, 0);
- if (str && ret)
- {
- len += strlen((char*)ret) + strlen((char*)str) + 2 + (fmt ? 2 + depth : 0);
- }
- else
- {
- fail = true;
- }
- child = child->next;
+ return false;
}
+ update_offset(output_buffer);
- /* Try to allocate the output string */
- if (!fail)
+ /* 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)
{
- out = (unsigned char*)cJSON_malloc(len);
+ return false;
}
- if (!out)
+ if (current_item->next)
{
- fail = true;
+ *output_pointer++ = ',';
}
- /* Handle failure */
- if (fail)
+ if (format)
{
- /* 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;
+ *output_pointer++ = '\n';
}
+ *output_pointer = '\0';
+ output_buffer->offset += length;
- /* Compose the output: */
- *out = '{';
- ptr = out + 1;
- if (fmt)
- {
- *ptr++ = '\n';
- }
- *ptr = '\0';
- for (i = 0; i < numentries; i++)
- {
- if (fmt)
- {
- for (j = 0; j < depth; j++)
- {
- *ptr++='\t';
- }
- }
- tmplen = strlen((char*)names[i]);
- memcpy(ptr, names[i], tmplen);
- ptr += tmplen;
- *ptr++ = ':';
- if (fmt)
- {
- *ptr++ = '\t';
- }
- strcpy((char*)ptr, (char*)entries[i]);
- ptr += strlen((char*)entries[i]);
- if (i != (numentries - 1))
- {
- *ptr++ = ',';
- }
- if (fmt)
- {
- *ptr++ = '\n';
- }
- *ptr = '\0';
- cJSON_free(names[i]);
- cJSON_free(entries[i]);
- }
+ current_item = current_item->next;
+ }
- cJSON_free(names);
- cJSON_free(entries);
- if (fmt)
+ 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++)
{
- for (i = 0; i < (depth - 1); i++)
- {
- *ptr++ = '\t';
- }
+ *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;
size_t i = 0;
@@ -1727,7 +1475,7 @@ int cJSON_GetArraySize(const cJSON *array)
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)
@@ -1739,7 +1487,7 @@ cJSON *cJSON_GetArrayItem(const cJSON *array, int item)
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((unsigned char*)c->string, (const unsigned char*)string))
@@ -1749,7 +1497,25 @@ cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string)
return c;
}
-cjbool 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;
}
@@ -1762,9 +1528,9 @@ static void suffix_object(cJSON *prev, cJSON *item)
}
/* 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;
@@ -1777,7 +1543,7 @@ static cJSON *create_reference(const cJSON *item)
}
/* Add item to array/object. */
-void cJSON_AddItemToArray(cJSON *array, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
cJSON *child = NULL;
@@ -1804,16 +1570,16 @@ void cJSON_AddItemToArray(cJSON *array, cJSON *item)
}
}
-void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
/* call cJSON_AddItemToObjectCS for code reuse */
- cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string), item);
+ cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item);
/* remove cJSON_StringIsConst flag */
item->type &= ~cJSON_StringIsConst;
}
/* 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)
{
@@ -1821,7 +1587,7 @@ void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
}
if (!(item->type & cJSON_StringIsConst) && item->string)
{
- cJSON_free(item->string);
+ global_hooks.deallocate(item->string);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
@@ -1831,14 +1597,14 @@ void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
cJSON_AddItemToArray(object, item);
}
-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));
}
static cJSON *DetachItemFromArray(cJSON *array, size_t which)
@@ -1872,7 +1638,7 @@ static cJSON *DetachItemFromArray(cJSON *array, size_t which)
return c;
}
-cJSON *cJSON_DetachItemFromArray(cJSON *array, int which)
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
{
if (which < 0)
{
@@ -1882,12 +1648,12 @@ cJSON *cJSON_DetachItemFromArray(cJSON *array, int which)
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)
{
size_t i = 0;
cJSON *c = object->child;
@@ -1904,13 +1670,13 @@ cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string)
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))
@@ -1965,7 +1731,7 @@ static void ReplaceItemInArray(cJSON *array, size_t which, cJSON *newitem)
c->next = c->prev = NULL;
cJSON_Delete(c);
}
-void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
{
if (which < 0)
{
@@ -1975,7 +1741,7 @@ void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
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)
{
size_t i = 0;
cJSON *c = object->child;
@@ -1989,18 +1755,18 @@ void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem
/* 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 = (char*)cJSON_strdup((const unsigned char*)string);
+ 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;
@@ -2009,9 +1775,9 @@ cJSON *cJSON_CreateNull(void)
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;
@@ -2020,9 +1786,9 @@ cJSON *cJSON_CreateTrue(void)
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;
@@ -2031,9 +1797,9 @@ cJSON *cJSON_CreateFalse(void)
return item;
}
-cJSON *cJSON_CreateBool(cjbool 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;
@@ -2042,9 +1808,9 @@ cJSON *cJSON_CreateBool(cjbool b)
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;
@@ -2068,13 +1834,13 @@ cJSON *cJSON_CreateNumber(double 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 = (char*)cJSON_strdup((const unsigned char*)string);
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
if(!item->valuestring)
{
cJSON_Delete(item);
@@ -2085,13 +1851,13 @@ cJSON *cJSON_CreateString(const char *string)
return item;
}
-extern cJSON *cJSON_CreateRaw(const char *raw)
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
{
- cJSON *item = cJSON_New_Item();
+ cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_Raw;
- item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw);
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
if(!item->valuestring)
{
cJSON_Delete(item);
@@ -2102,9 +1868,9 @@ extern cJSON *cJSON_CreateRaw(const char *raw)
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;
@@ -2113,9 +1879,9 @@ cJSON *cJSON_CreateArray(void)
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;
@@ -2125,7 +1891,7 @@ cJSON *cJSON_CreateObject(void)
}
/* Create Arrays: */
-cJSON *cJSON_CreateIntArray(const int *numbers, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
{
size_t i = 0;
cJSON *n = NULL;
@@ -2160,7 +1926,7 @@ cJSON *cJSON_CreateIntArray(const int *numbers, int count)
return a;
}
-cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
{
size_t i = 0;
cJSON *n = NULL;
@@ -2176,7 +1942,7 @@ cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
for(i = 0; a && (i < (size_t)count); i++)
{
- n = cJSON_CreateNumber(numbers[i]);
+ n = cJSON_CreateNumber((double)numbers[i]);
if(!n)
{
cJSON_Delete(a);
@@ -2196,7 +1962,7 @@ cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
return a;
}
-cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
{
size_t i = 0;
cJSON *n = NULL;
@@ -2232,7 +1998,7 @@ cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)
return a;
}
-cJSON *cJSON_CreateStringArray(const char **strings, int count)
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count)
{
size_t i = 0;
cJSON *n = NULL;
@@ -2269,7 +2035,7 @@ cJSON *cJSON_CreateStringArray(const char **strings, int count)
}
/* Duplication */
-cJSON *cJSON_Duplicate(const cJSON *item, cjbool recurse)
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
{
cJSON *newitem = NULL;
cJSON *child = NULL;
@@ -2282,7 +2048,7 @@ cJSON *cJSON_Duplicate(const cJSON *item, cjbool recurse)
goto fail;
}
/* Create new item */
- newitem = cJSON_New_Item();
+ newitem = cJSON_New_Item(&global_hooks);
if (!newitem)
{
goto fail;
@@ -2293,7 +2059,7 @@ cJSON *cJSON_Duplicate(const cJSON *item, cjbool recurse)
newitem->valuedouble = item->valuedouble;
if (item->valuestring)
{
- newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring);
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
if (!newitem->valuestring)
{
goto fail;
@@ -2301,7 +2067,7 @@ cJSON *cJSON_Duplicate(const cJSON *item, cjbool recurse)
}
if (item->string)
{
- newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string);
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
if (!newitem->string)
{
goto fail;
@@ -2348,7 +2114,7 @@ fail:
return NULL;
}
-void cJSON_Minify(char *json)
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
{
unsigned char *into = (unsigned char*)json;
while (*json)
@@ -2411,3 +2177,103 @@ void cJSON_Minify(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;
+}
diff --git a/cJSON.h b/cJSON.h
@@ -30,11 +30,8 @@ extern "C"
/* project version */
#define CJSON_VERSION_MAJOR 1
-#define CJSON_VERSION_MINOR 3
-#define CJSON_VERSION_PATCH 2
-
-/* returns the version of cJSON as a string */
-extern const char* cJSON_Version(void);
+#define CJSON_VERSION_MINOR 4
+#define CJSON_VERSION_PATCH 0
#include <stddef.h>
@@ -81,84 +78,139 @@ typedef struct cJSON_Hooks
void (*free_fn)(void *ptr);
} cJSON_Hooks;
+typedef int cJSON_bool;
+
+#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 2 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
+
+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
+
+*/
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type __stdcall
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall
+#else
+#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall
+#endif
+#else /* !WIN32 */
+#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
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
/* Supply malloc, realloc and free functions to cJSON */
-extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
-extern cJSON *cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
-extern char *cJSON_Print(const cJSON *item);
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
-extern char *cJSON_PrintUnformatted(const cJSON *item);
+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 */
-extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt);
+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 length buf_len. Returns 1 on success and 0 on failure. */
-extern int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const int fmt);
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt);
/* Delete a cJSON entity and all subentities. */
-extern void cJSON_Delete(cJSON *c);
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
/* Returns the number of items in an array (or object). */
-extern int cJSON_GetArraySize(const cJSON *array);
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
-extern cJSON *cJSON_GetArrayItem(const cJSON *array, int item);
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item);
/* Get item "string" from object. Case insensitive. */
-extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string);
-extern int cJSON_HasObjectItem(const cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *object, const char *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. */
-extern const char *cJSON_GetErrorPtr(void);
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* 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. */
-extern cJSON *cJSON_CreateNull(void);
-extern cJSON *cJSON_CreateTrue(void);
-extern cJSON *cJSON_CreateFalse(void);
-extern cJSON *cJSON_CreateBool(int b);
-extern cJSON *cJSON_CreateNumber(double num);
-extern cJSON *cJSON_CreateString(const char *string);
+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 */
-extern cJSON *cJSON_CreateRaw(const char *raw);
-extern cJSON *cJSON_CreateArray(void);
-extern cJSON *cJSON_CreateObject(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
/* These utilities create an Array of count items. */
-extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
-extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
-extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
-extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
+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 **strings, int count);
/* Append item to the specified array/object. */
-extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
-extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) 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` */
-extern void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+CJSON_PUBLIC(void) 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. */
-extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
/* Remove/Detatch items from Arrays/Objects. */
-extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which);
-extern void cJSON_DeleteItemFromArray(cJSON *array, int which);
-extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string);
-extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+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(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
/* Update array items. */
-extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
-extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
-extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* Duplicate a cJSON item */
-extern cJSON *cJSON_Duplicate(const cJSON *item, int recurse);
+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. */
/* 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. If not, then cJSON_GetErrorPtr() does the job. */
-extern 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);
-extern void cJSON_Minify(char *json);
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
@@ -172,11 +224,11 @@ extern void cJSON_Minify(char *json);
/* 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 */
-extern double cJSON_SetNumberHelper(cJSON *object, double number);
-#define cJSON_SetNumberValue(object, number) ((object) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* Macro for iterating over an array */
-#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
#ifdef __cplusplus
}
diff --git a/cJSON_Utils.c b/cJSON_Utils.c
@@ -1,8 +1,10 @@
+#pragma GCC visibility push(default)
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
+#pragma GCC visibility pop
#include "cJSON_Utils.h"
@@ -31,7 +33,7 @@ static int cJSONUtils_strcasecmp(const unsigned char *s1, const unsigned char *s
{
return 1;
}
- for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
+ for(; tolower(*s1) == tolower(*s2); (void)++s1, ++s2)
{
if(*s1 == 0)
{
@@ -49,7 +51,7 @@ static int cJSONUtils_Pstrcasecmp(const unsigned char *a, const unsigned char *e
{
return (a == e) ? 0 : 1; /* both NULL? */
}
- for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */
+ for (; *a && *e && (*e != '/'); (void)a++, e++) /* compare until next '/' */
{
if (*e == '~')
{
@@ -81,7 +83,7 @@ static int cJSONUtils_Pstrcasecmp(const unsigned char *a, const unsigned char *e
static size_t cJSONUtils_PointerEncodedstrlen(const unsigned char *s)
{
size_t l = 0;
- for (; *s; s++, l++)
+ for (; *s; (void)s++, l++)
{
if ((*s == '~') || (*s == '/'))
{
@@ -115,9 +117,8 @@ static void cJSONUtils_PointerEncodedstrcpy(unsigned char *d, const unsigned cha
*d = '\0';
}
-char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
{
- int type = object->type;
size_t c = 0;
cJSON *obj = 0;
@@ -128,12 +129,12 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
}
/* recursively search all children of the object */
- for (obj = object->child; obj; obj = obj->next, c++)
+ for (obj = object->child; obj; (void)(obj = obj->next), c++)
{
unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
if (found)
{
- if ((type & 0xFF) == cJSON_Array)
+ if (cJSON_IsArray(object))
{
/* reserve enough memory for a 64 bit integer + '/' and '\0' */
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23);
@@ -150,7 +151,7 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
return (char*)ret;
}
- else if ((type & 0xFF) == cJSON_Object)
+ else if (cJSON_IsObject(object))
{
unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
*ret = '/';
@@ -171,12 +172,12 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
return NULL;
}
-cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer)
{
/* follow path of the pointer */
while ((*pointer++ == '/') && object)
{
- if ((object->type & 0xFF) == cJSON_Array)
+ if (cJSON_IsArray(object))
{
size_t which = 0;
/* parse array index */
@@ -195,7 +196,7 @@ cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
}
object = cJSON_GetArrayItem(object, (int)which);
}
- else if ((object->type & 0xFF) == cJSON_Object)
+ else if (cJSON_IsObject(object))
{
object = object->child;
/* GetObjectItem. */
@@ -227,7 +228,7 @@ static void cJSONUtils_InplaceDecodePointerString(unsigned char *string)
return;
}
- for (; *string; s2++, string++)
+ for (; *string; (void)s2++, string++)
{
*s2 = (*string != '~')
? (*string)
@@ -269,11 +270,11 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const unsigned char *path)
/* Couldn't find object to remove child from. */
ret = NULL;
}
- else if ((parent->type & 0xFF) == cJSON_Array)
+ else if (cJSON_IsArray(parent))
{
ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr));
}
- else if ((parent->type & 0xFF) == cJSON_Object)
+ else if (cJSON_IsObject(parent))
{
ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
}
@@ -299,7 +300,7 @@ static int cJSONUtils_Compare(cJSON *a, cJSON *b)
/* string mismatch. */
return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0;
case cJSON_Array:
- for (a = a->child, b = b->child; a && b; a = a->next, b = b->next)
+ for ((void)(a = a->child), b = b->child; a && b; (void)(a = a->next), b = b->next)
{
int err = cJSONUtils_Compare(a, b);
if (err)
@@ -474,7 +475,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
cJSON_Delete(value);
return 9;
}
- else if ((parent->type & 0xFF) == cJSON_Array)
+ else if (cJSON_IsArray(parent))
{
if (!strcmp((char*)childptr, "-"))
{
@@ -485,7 +486,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
cJSON_InsertItemInArray(parent, atoi((char*)childptr), value);
}
}
- else if ((parent->type & 0xFF) == cJSON_Object)
+ else if (cJSON_IsObject(parent))
{
cJSON_DeleteItemFromObject(parent, (char*)childptr);
cJSON_AddItemToObject(parent, (char*)childptr, value);
@@ -499,7 +500,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
return 0;
}
-int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
{
int err = 0;
@@ -508,7 +509,7 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
return 1;
}
- if ((patches->type & 0xFF) != cJSON_Array)
+ if (cJSON_IsArray(patches))
{
/* malformed patches. */
return 1;
@@ -551,7 +552,7 @@ static void cJSONUtils_GeneratePatch(cJSON *patches, const unsigned char *op, co
cJSON_AddItemToArray(patches, patch);
}
-void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
{
cJSONUtils_GeneratePatch(array, (const unsigned char*)op, (const unsigned char*)path, 0, val);
}
@@ -590,7 +591,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
size_t c = 0;
unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */
/* generate patches for all array elements that exist in "from" and "to" */
- for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++)
+ for ((void)(c = 0), (void)(from = from->child), to = to->child; from && to; (void)(from = from->next), (void)(to = to->next), c++)
{
/* check if conversion to unsigned long is valid
* This should be eliminated at compile time by dead code elimination
@@ -604,7 +605,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
cJSONUtils_CompareToPatch(patches, newpath, from, to);
}
/* remove leftover elements from 'from' that are not in 'to' */
- for (; from; from = from->next, c++)
+ for (; from; (void)(from = from->next), c++)
{
/* check if conversion to unsigned long is valid
* This should be eliminated at compile time by dead code elimination
@@ -618,7 +619,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0);
}
/* add new elements in 'to' that were not in 'from' */
- for (; to; to = to->next, c++)
+ for (; to; (void)(to = to->next), c++)
{
cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
}
@@ -671,7 +672,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
}
}
-cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
{
cJSON *patches = cJSON_CreateArray();
cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
@@ -786,21 +787,21 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
return list;
}
-void cJSONUtils_SortObject(cJSON *object)
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object)
{
object->child = cJSONUtils_SortList(object->child);
}
-cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
{
- if (!patch || ((patch->type & 0xFF) != cJSON_Object))
+ if (!cJSON_IsObject(patch))
{
/* scalar value, array or NULL, just duplicate */
cJSON_Delete(target);
return cJSON_Duplicate(patch, 1);
}
- if (!target || ((target->type & 0xFF) != cJSON_Object))
+ if (!cJSON_IsObject(target))
{
cJSON_Delete(target);
target = cJSON_CreateObject();
@@ -809,7 +810,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
patch = patch->child;
while (patch)
{
- if ((patch->type & 0xFF) == cJSON_NULL)
+ if (cJSON_IsNull(patch))
{
/* NULL is the indicator to remove a value, see RFC7396 */
cJSON_DeleteItemFromObject(target, patch->string);
@@ -824,7 +825,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
return target;
}
-cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
{
cJSON *patch = NULL;
if (!to)
@@ -832,7 +833,7 @@ cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
/* patch to delete everything */
return cJSON_CreateNull();
}
- if (((to->type & 0xFF) != cJSON_Object) || !from || ((from->type & 0xFF) != cJSON_Object))
+ if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
{
return cJSON_Duplicate(to, 1);
}
diff --git a/cJSON_Utils.h b/cJSON_Utils.h
@@ -1,14 +1,14 @@
#include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
-cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer);
/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
-cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
/* Utility for generating patch array entries. */
-void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
/* Returns 0 for success. */
-int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
/*
// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
@@ -33,12 +33,12 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
/* target will be modified by patch. return value is new ptr for target. */
-cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
/* generates a patch to move from -> to */
-cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
/* Given a root object and a target object, construct a pointer from one to the other. */
-char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
/* Sorts the members of the object into alphabetical order. */
-void cJSONUtils_SortObject(cJSON *object);
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object);
diff --git a/fuzzing/.gitignore b/fuzzing/.gitignore
@@ -0,0 +1 @@
+afl-build
diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt
@@ -0,0 +1,28 @@
+option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off)
+if (ENABLE_FUZZING)
+ find_program(AFL_FUZZ afl-fuzz)
+ if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND")
+ message(FATAL_ERROR "Couldn't find afl-fuzz.")
+ endif()
+
+ add_executable(afl-main afl.c)
+ target_link_libraries(afl-main "${CJSON_LIB}")
+
+ if (NOT ENABLE_SANITIZERS)
+ message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.")
+ endif()
+
+ option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On)
+ set(fuzz_print_parameter "no")
+ if (ENABLE_FUZZING_PRINT)
+ set(fuzz_print_parameter "yes")
+ endif()
+
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
+
+ add_custom_target(afl
+ COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}"
+ DEPENDS afl-main)
+
+
+endif()
diff --git a/fuzzing/afl-prepare-linux.sh b/fuzzing/afl-prepare-linux.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -x
+echo core | sudo tee /proc/sys/kernel/core_pattern
+echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
diff --git a/fuzzing/afl.c b/fuzzing/afl.c
@@ -0,0 +1,176 @@
+/*
+ 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../cJSON.h"
+
+static char *read_file(const char *filename)
+{
+ FILE *file = NULL;
+ long length = 0;
+ char *content = NULL;
+ size_t read_chars = 0;
+
+ /* open in read binary mode */
+ file = fopen(filename, "rb");
+ if (file == NULL)
+ {
+ goto cleanup;
+ }
+
+ /* get the length */
+ if (fseek(file, 0, SEEK_END) != 0)
+ {
+ goto cleanup;
+ }
+ length = ftell(file);
+ if (length < 0)
+ {
+ goto cleanup;
+ }
+ if (fseek(file, 0, SEEK_SET) != 0)
+ {
+ goto cleanup;
+ }
+
+ /* allocate content buffer */
+ content = (char*)malloc((size_t)length + sizeof('\0'));
+ if (content == NULL)
+ {
+ goto cleanup;
+ }
+
+ /* read the file into memory */
+ read_chars = fread(content, sizeof(char), (size_t)length, file);
+ if ((long)read_chars != length)
+ {
+ free(content);
+ content = NULL;
+ goto cleanup;
+ }
+ content[read_chars] = '\0';
+
+
+cleanup:
+ if (file != NULL)
+ {
+ fclose(file);
+ }
+
+ return content;
+}
+
+int main(int argc, char** argv)
+{
+ const char *filename = NULL;
+ cJSON *item = NULL;
+ char *json = NULL;
+ int status = EXIT_FAILURE;
+ char *printed_json = NULL;
+
+ if ((argc < 2) || (argc > 3))
+ {
+ printf("Usage:\n");
+ printf("%s input_file [enable_printing]\n", argv[0]);
+ printf("\t input_file: file containing the test data\n");
+ printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n");
+ goto cleanup;
+ }
+
+ filename = argv[1];
+
+#if __AFL_HAVE_MANUAL_CONTROL
+ while (__AFL_LOOP(1000))
+ {
+#endif
+ status = EXIT_SUCCESS;
+
+ json = read_file(filename);
+ if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
+ {
+ status = EXIT_FAILURE;
+ goto cleanup;
+ }
+ item = cJSON_Parse(json + 2);
+ if (item == NULL)
+ {
+ goto cleanup;
+ }
+
+ if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0))
+ {
+ int do_format = 0;
+ if (json[1] == 'f')
+ {
+ do_format = 1;
+ }
+
+ if (json[0] == 'b')
+ {
+ /* buffered printing */
+ printed_json = cJSON_PrintBuffered(item, 1, do_format);
+ }
+ else
+ {
+ /* unbuffered printing */
+ if (do_format)
+ {
+ printed_json = cJSON_Print(item);
+ }
+ else
+ {
+ printed_json = cJSON_PrintUnformatted(item);
+ }
+ }
+ if (printed_json == NULL)
+ {
+ status = EXIT_FAILURE;
+ goto cleanup;
+ }
+ printf("%s\n", printed_json);
+ }
+
+cleanup:
+ if (item != NULL)
+ {
+ cJSON_Delete(item);
+ item = NULL;
+ }
+ if (json != NULL)
+ {
+ free(json);
+ json = NULL;
+ }
+ if (printed_json != NULL)
+ {
+ free(printed_json);
+ printed_json = NULL;
+ }
+#if __AFL_HAVE_MANUAL_CONTROL
+ }
+#endif
+
+ return status;
+}
diff --git a/fuzzing/afl.sh b/fuzzing/afl.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+mkdir -p afl-build || exit 1
+cd afl-build || exit 1
+#cleanup
+rm -r -- *
+
+CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off
+make afl
diff --git a/fuzzing/inputs/test1 b/fuzzing/inputs/test1
@@ -0,0 +1,22 @@
+bf{
+ "glossary": {
+ "title": "example glossary",
+ "GlossDiv": {
+ "title": "S",
+ "GlossList": {
+ "GlossEntry": {
+ "ID": "SGML",
+ "SortAs": "SGML",
+ "GlossTerm": "Standard Generalized Markup Language",
+ "Acronym": "SGML",
+ "Abbrev": "ISO 8879:1986",
+ "GlossDef": {
+ "para": "A meta-markup language, used to create markup languages such as DocBook.",
+ "GlossSeeAlso": ["GML", "XML"]
+ },
+ "GlossSee": "markup"
+ }
+ }
+ }
+ }
+}
diff --git a/fuzzing/inputs/test10 b/fuzzing/inputs/test10
@@ -0,0 +1 @@
+bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
diff --git a/fuzzing/inputs/test11 b/fuzzing/inputs/test11
@@ -0,0 +1,8 @@
+bf{
+"name": "Jack (\"Bee\") Nimble",
+"format": {"type": "rect",
+"width": 1920,
+"height": 1080,
+"interlace": false,"frame rate": 24
+}
+}
diff --git a/fuzzing/inputs/test2 b/fuzzing/inputs/test2
@@ -0,0 +1,11 @@
+bf{"menu": {
+ "id": "file",
+ "value": "File",
+ "popup": {
+ "menuitem": [
+ {"value": "New", "onclick": "CreateNewDoc()"},
+ {"value": "Open", "onclick": "OpenDoc()"},
+ {"value": "Close", "onclick": "CloseDoc()"}
+ ]
+ }
+}}
diff --git a/fuzzing/inputs/test3 b/fuzzing/inputs/test3
@@ -0,0 +1,26 @@
+bf{"widget": {
+ "debug": "on",
+ "window": {
+ "title": "Sample Konfabulator Widget",
+ "name": "main_window",
+ "width": 500,
+ "height": 500
+ },
+ "image": {
+ "src": "Images/Sun.png",
+ "name": "sun1",
+ "hOffset": 250,
+ "vOffset": 250,
+ "alignment": "center"
+ },
+ "text": {
+ "data": "Click Here",
+ "size": 36,
+ "style": "bold",
+ "name": "text1",
+ "hOffset": 250,
+ "vOffset": 100,
+ "alignment": "center",
+ "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+ }
+}}
diff --git a/fuzzing/inputs/test3.bu b/fuzzing/inputs/test3.bu
@@ -0,0 +1,26 @@
+bu{"widget": {
+ "debug": "on",
+ "window": {
+ "title": "Sample Konfabulator Widget",
+ "name": "main_window",
+ "width": 500,
+ "height": 500
+ },
+ "image": {
+ "src": "Images/Sun.png",
+ "name": "sun1",
+ "hOffset": 250,
+ "vOffset": 250,
+ "alignment": "center"
+ },
+ "text": {
+ "data": "Click Here",
+ "size": 36,
+ "style": "bold",
+ "name": "text1",
+ "hOffset": 250,
+ "vOffset": 100,
+ "alignment": "center",
+ "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+ }
+}}
diff --git a/fuzzing/inputs/test3.uf b/fuzzing/inputs/test3.uf
@@ -0,0 +1,26 @@
+uf{"widget": {
+ "debug": "on",
+ "window": {
+ "title": "Sample Konfabulator Widget",
+ "name": "main_window",
+ "width": 500,
+ "height": 500
+ },
+ "image": {
+ "src": "Images/Sun.png",
+ "name": "sun1",
+ "hOffset": 250,
+ "vOffset": 250,
+ "alignment": "center"
+ },
+ "text": {
+ "data": "Click Here",
+ "size": 36,
+ "style": "bold",
+ "name": "text1",
+ "hOffset": 250,
+ "vOffset": 100,
+ "alignment": "center",
+ "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+ }
+}}
diff --git a/fuzzing/inputs/test3.uu b/fuzzing/inputs/test3.uu
@@ -0,0 +1,26 @@
+uu{"widget": {
+ "debug": "on",
+ "window": {
+ "title": "Sample Konfabulator Widget",
+ "name": "main_window",
+ "width": 500,
+ "height": 500
+ },
+ "image": {
+ "src": "Images/Sun.png",
+ "name": "sun1",
+ "hOffset": 250,
+ "vOffset": 250,
+ "alignment": "center"
+ },
+ "text": {
+ "data": "Click Here",
+ "size": 36,
+ "style": "bold",
+ "name": "text1",
+ "hOffset": 250,
+ "vOffset": 100,
+ "alignment": "center",
+ "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+ }
+}}
diff --git a/fuzzing/inputs/test4 b/fuzzing/inputs/test4
@@ -0,0 +1,88 @@
+bf{"web-app": {
+ "servlet": [
+ {
+ "servlet-name": "cofaxCDS",
+ "servlet-class": "org.cofax.cds.CDSServlet",
+ "init-param": {
+ "configGlossary:installationAt": "Philadelphia, PA",
+ "configGlossary:adminEmail": "ksm@pobox.com",
+ "configGlossary:poweredBy": "Cofax",
+ "configGlossary:poweredByIcon": "/images/cofax.gif",
+ "configGlossary:staticPath": "/content/static",
+ "templateProcessorClass": "org.cofax.WysiwygTemplate",
+ "templateLoaderClass": "org.cofax.FilesTemplateLoader",
+ "templatePath": "templates",
+ "templateOverridePath": "",
+ "defaultListTemplate": "listTemplate.htm",
+ "defaultFileTemplate": "articleTemplate.htm",
+ "useJSP": false,
+ "jspListTemplate": "listTemplate.jsp",
+ "jspFileTemplate": "articleTemplate.jsp",
+ "cachePackageTagsTrack": 200,
+ "cachePackageTagsStore": 200,
+ "cachePackageTagsRefresh": 60,
+ "cacheTemplatesTrack": 100,
+ "cacheTemplatesStore": 50,
+ "cacheTemplatesRefresh": 15,
+ "cachePagesTrack": 200,
+ "cachePagesStore": 100,
+ "cachePagesRefresh": 10,
+ "cachePagesDirtyRead": 10,
+ "searchEngineListTemplate": "forSearchEnginesList.htm",
+ "searchEngineFileTemplate": "forSearchEngines.htm",
+ "searchEngineRobotsDb": "WEB-INF/robots.db",
+ "useDataStore": true,
+ "dataStoreClass": "org.cofax.SqlDataStore",
+ "redirectionClass": "org.cofax.SqlRedirection",
+ "dataStoreName": "cofax",
+ "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
+ "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
+ "dataStoreUser": "sa",
+ "dataStorePassword": "dataStoreTestQuery",
+ "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
+ "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
+ "dataStoreInitConns": 10,
+ "dataStoreMaxConns": 100,
+ "dataStoreConnUsageLimit": 100,
+ "dataStoreLogLevel": "debug",
+ "maxUrlLength": 500}},
+ {
+ "servlet-name": "cofaxEmail",
+ "servlet-class": "org.cofax.cds.EmailServlet",
+ "init-param": {
+ "mailHost": "mail1",
+ "mailHostOverride": "mail2"}},
+ {
+ "servlet-name": "cofaxAdmin",
+ "servlet-class": "org.cofax.cds.AdminServlet"},
+
+ {
+ "servlet-name": "fileServlet",
+ "servlet-class": "org.cofax.cds.FileServlet"},
+ {
+ "servlet-name": "cofaxTools",
+ "servlet-class": "org.cofax.cms.CofaxToolsServlet",
+ "init-param": {
+ "templatePath": "toolstemplates/",
+ "log": 1,
+ "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
+ "logMaxSize": "",
+ "dataLog": 1,
+ "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
+ "dataLogMaxSize": "",
+ "removePageCache": "/content/admin/remove?cache=pages&id=",
+ "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
+ "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
+ "lookInContext": 1,
+ "adminGroupID": 4,
+ "betaServer": true}}],
+ "servlet-mapping": {
+ "cofaxCDS": "/",
+ "cofaxEmail": "/cofaxutil/aemail/*",
+ "cofaxAdmin": "/admin/*",
+ "fileServlet": "/static/*",
+ "cofaxTools": "/tools/*"},
+
+ "taglib": {
+ "taglib-uri": "cofax.tld",
+ "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}
diff --git a/fuzzing/inputs/test5 b/fuzzing/inputs/test5
@@ -0,0 +1,27 @@
+bf{"menu": {
+ "header": "SVG Viewer",
+ "items": [
+ {"id": "Open"},
+ {"id": "OpenNew", "label": "Open New"},
+ null,
+ {"id": "ZoomIn", "label": "Zoom In"},
+ {"id": "ZoomOut", "label": "Zoom Out"},
+ {"id": "OriginalView", "label": "Original View"},
+ null,
+ {"id": "Quality"},
+ {"id": "Pause"},
+ {"id": "Mute"},
+ null,
+ {"id": "Find", "label": "Find..."},
+ {"id": "FindAgain", "label": "Find Again"},
+ {"id": "Copy"},
+ {"id": "CopyAgain", "label": "Copy Again"},
+ {"id": "CopySVG", "label": "Copy SVG"},
+ {"id": "ViewSVG", "label": "View SVG"},
+ {"id": "ViewSource", "label": "View Source"},
+ {"id": "SaveAs", "label": "Save As"},
+ null,
+ {"id": "Help"},
+ {"id": "About", "label": "About Adobe CVG Viewer..."}
+ ]
+}}
diff --git a/fuzzing/inputs/test6 b/fuzzing/inputs/test6
@@ -0,0 +1,16 @@
+bf<!DOCTYPE html>
+ <html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <style type="text/css">
+ html, body, iframe { margin: 0; padding: 0; height: 100%; }
+ iframe { display: block; width: 100%; border: none; }
+ </style>
+ <title>Application Error</title>
+ </head>
+ <body>
+ <iframe src="//s3.amazonaws.com/heroku_pages/error.html">
+ <p>Application Error</p>
+ </iframe>
+ </body>
+ </html>
diff --git a/fuzzing/inputs/test7 b/fuzzing/inputs/test7
@@ -0,0 +1,22 @@
+bf[
+ {
+ "precision": "zip",
+ "Latitude": 37.7668,
+ "Longitude": -122.3959,
+ "Address": "",
+ "City": "SAN FRANCISCO",
+ "State": "CA",
+ "Zip": "94107",
+ "Country": "US"
+ },
+ {
+ "precision": "zip",
+ "Latitude": 37.371991,
+ "Longitude": -122.026020,
+ "Address": "",
+ "City": "SUNNYVALE",
+ "State": "CA",
+ "Zip": "94085",
+ "Country": "US"
+ }
+ ]
diff --git a/fuzzing/inputs/test8 b/fuzzing/inputs/test8
@@ -0,0 +1,13 @@
+bf{
+ "Image": {
+ "Width": 800,
+ "Height": 600,
+ "Title": "View from 15th Floor",
+ "Thumbnail": {
+ "Url": "http:/*www.example.com/image/481989943",
+ "Height": 125,
+ "Width": "100"
+ },
+ "IDs": [116, 943, 234, 38793]
+ }
+ }
diff --git a/fuzzing/inputs/test9 b/fuzzing/inputs/test9
@@ -0,0 +1,5 @@
+bf[
+ [0, -1, 0],
+ [1, 0, 0],
+ [0, 0, 1]
+ ]
diff --git a/fuzzing/json.dict b/fuzzing/json.dict
@@ -0,0 +1,47 @@
+#
+# AFL dictionary for JSON
+# -----------------------------
+#
+
+object_start="{"
+object_end="}"
+object_empty="{}"
+object_one_element="{\"one\":1}"
+object_two_elements="{\"1\":1,\"2\":2}"
+object_separator=":"
+
+array_start="["
+array_end="]"
+array_empty="[]"
+array_one_element="[1]"
+array_two_elements="[1,2]"
+
+separator=","
+
+escape_sequence_b="\\b"
+escape_sequence_f="\\f"
+escape_sequence_n="\\n"
+escape_sequence_r="\\r"
+escape_sequence_t="\\t"
+escape_sequence_quote="\\\""
+escape_sequence_backslash="\\\\"
+escapce_sequence_slash="\\/"
+escpae_sequence_utf16_base="\\u"
+escape_sequence_utf16="\\u12ab"
+
+number_integer="1"
+number_double="1.0"
+number_negative_integer="-1"
+number_negative_double="-1.0"
+number_engineering1="1e1"
+number_engineering2="1e-1"
+number_positive_integer="+1"
+number_positive_double="+1.0"
+number_e="e"
+number_plus="+"
+number_minus="-"
+number_separator="."
+
+null="null"
+true="true"
+false="false"
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
@@ -1,6 +1,17 @@
if(ENABLE_CJSON_TEST)
add_library(unity unity/src/unity.c)
+ # Disable -Werror for Unity
+ list(FIND custom_compiler_flags "-Werror" werror_found)
+ if (werror_found)
+ target_compile_options(unity PRIVATE "-Wno-error")
+ endif()
+ # Disable -fvisibility=hidden for Unity
+ list(FIND custom_compiler_flags "-fvisibility=hidden" visibility_found)
+ if (visibility_found)
+ target_compile_options(unity PRIVATE "-fvisibility=default")
+ endif()
+
#copy test files
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")
file(GLOB test_files "inputs/*")
@@ -14,6 +25,12 @@ if(ENABLE_CJSON_TEST)
parse_array
parse_object
parse_value
+ print_string
+ print_number
+ print_array
+ print_object
+ print_value
+ misc_tests
)
add_library(test-common common.c)
@@ -29,6 +46,11 @@ if(ENABLE_CJSON_TEST)
endif()
endif()
+ #"check" target that automatically builds everything and runs the tests
+ add_custom_target(check
+ COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
+ DEPENDS ${unity_tests})
+
foreach(unity_test ${unity_tests})
add_executable("${unity_test}" "${unity_test}.c")
target_link_libraries("${unity_test}" "${CJSON_LIB}" unity test-common)
diff --git a/tests/common.c b/tests/common.c
@@ -22,7 +22,7 @@
#include "common.h"
-extern void reset(cJSON *item)
+CJSON_PUBLIC(void) reset(cJSON *item)
{
if ((item != NULL) && (item->child != NULL))
{
@@ -30,17 +30,17 @@ extern void reset(cJSON *item)
}
if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
{
- cJSON_free(item->valuestring);
+ global_hooks.deallocate(item->valuestring);
}
if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
{
- cJSON_free(item->string);
+ global_hooks.deallocate(item->string);
}
memset(item, 0, sizeof(cJSON));
}
-extern char *read_file(const char *filename)
+CJSON_PUBLIC(char*) read_file(const char *filename)
{
FILE *file = NULL;
long length = 0;
diff --git a/tests/common.h b/tests/common.h
@@ -25,9 +25,8 @@
#include "../cJSON.c"
-extern void reset(cJSON *item);
-extern char *read_file(const char *filename);
-extern cjbool assert_is_invalid(cJSON *item);
+CJSON_PUBLIC(void) reset(cJSON *item);
+CJSON_PUBLIC(char*) read_file(const char *filename);
/* assertion helper macros */
#define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")
diff --git a/tests/inputs/test7.expected b/tests/inputs/test7.expected
@@ -1,7 +1,7 @@
[{
"precision": "zip",
- "Latitude": 37.766800,
- "Longitude": -122.395900,
+ "Latitude": 37.7668,
+ "Longitude": -122.3959,
"Address": "",
"City": "SAN FRANCISCO",
"State": "CA",
@@ -10,7 +10,7 @@
}, {
"precision": "zip",
"Latitude": 37.371991,
- "Longitude": -122.026020,
+ "Longitude": -122.02602,
"Address": "",
"City": "SUNNYVALE",
"State": "CA",
diff --git a/tests/misc_tests.c b/tests/misc_tests.c
@@ -0,0 +1,197 @@
+/*
+ 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+
+static void cjson_array_foreach_should_loop_over_arrays(void)
+{
+ cJSON array[1];
+ cJSON elements[10];
+ cJSON *element_pointer = NULL;
+ size_t i = 0;
+
+ memset(array, 0, sizeof(array));
+ memset(elements, 0, sizeof(elements));
+
+ /* create array */
+ array[0].child = &elements[0];
+ elements[0].prev = NULL;
+ elements[9].next = NULL;
+ for (i = 0; i < 9; i++)
+ {
+ elements[i].next = &elements[i + 1];
+ elements[i + 1].prev = &elements[i];
+ }
+
+ i = 0;
+ cJSON_ArrayForEach(element_pointer, array)
+ {
+ TEST_ASSERT_TRUE_MESSAGE(element_pointer == &elements[i], "Not iterating over array properly");
+ i++;
+ }
+}
+
+static void cjson_array_foreach_should_not_dereference_null_pointer(void)
+{
+ cJSON *array = NULL;
+ cJSON *element = NULL;
+ cJSON_ArrayForEach(element, array);
+}
+
+static void cjson_get_object_item_should_get_object_items(void)
+{
+ cJSON *item = NULL;
+ cJSON *found = NULL;
+
+ item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
+
+ found = cJSON_GetObjectItem(NULL, "test");
+ TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
+
+ found = cJSON_GetObjectItem(item, NULL);
+ TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
+
+
+ found = cJSON_GetObjectItem(item, "one");
+ TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+ TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
+
+ found = cJSON_GetObjectItem(item, "tWo");
+ TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+ TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
+
+ found = cJSON_GetObjectItem(item, "three");
+ TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
+ TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
+
+ found = cJSON_GetObjectItem(item, "four");
+ TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
+
+ cJSON_Delete(item);
+}
+
+static void cjson_get_object_item_case_sensitive_should_get_object_items(void)
+{
+ cJSON *item = NULL;
+ cJSON *found = NULL;
+
+ item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
+
+ found = cJSON_GetObjectItemCaseSensitive(NULL, "test");
+ TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
+
+ found = cJSON_GetObjectItemCaseSensitive(item, NULL);
+ TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
+
+ found = cJSON_GetObjectItemCaseSensitive(item, "one");
+ TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+ TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
+
+ found = cJSON_GetObjectItemCaseSensitive(item, "Two");
+ TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+ TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
+
+ found = cJSON_GetObjectItemCaseSensitive(item, "tHree");
+ TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
+ TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
+
+ found = cJSON_GetObjectItemCaseSensitive(item, "One");
+ TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
+
+ cJSON_Delete(item);
+}
+
+static void typecheck_functions_should_check_type(void)
+{
+ cJSON invalid[1];
+ cJSON item[1];
+ invalid->type = cJSON_Invalid;
+ invalid->type |= cJSON_StringIsConst;
+ item->type = cJSON_False;
+ item->type |= cJSON_StringIsConst;
+
+ TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsInvalid(item));
+ TEST_ASSERT_TRUE(cJSON_IsInvalid(invalid));
+
+ item->type = cJSON_False | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsFalse(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsFalse(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsFalse(item));
+ TEST_ASSERT_TRUE(cJSON_IsBool(item));
+
+ item->type = cJSON_True | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsTrue(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsTrue(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsTrue(item));
+ TEST_ASSERT_TRUE(cJSON_IsBool(item));
+
+ item->type = cJSON_NULL | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsNull(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsNull(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsNull(item));
+
+ item->type = cJSON_Number | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsNumber(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsNumber(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsNumber(item));
+
+ item->type = cJSON_String | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsString(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsString(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsString(item));
+
+ item->type = cJSON_Array | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsArray(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsArray(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsArray(item));
+
+ item->type = cJSON_Object | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsObject(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsObject(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsObject(item));
+
+ item->type = cJSON_Raw | cJSON_StringIsConst;
+ TEST_ASSERT_FALSE(cJSON_IsRaw(NULL));
+ TEST_ASSERT_FALSE(cJSON_IsRaw(invalid));
+ TEST_ASSERT_TRUE(cJSON_IsRaw(item));
+}
+
+int main(void)
+{
+ UNITY_BEGIN();
+
+ RUN_TEST(cjson_array_foreach_should_loop_over_arrays);
+ RUN_TEST(cjson_array_foreach_should_not_dereference_null_pointer);
+ RUN_TEST(cjson_get_object_item_should_get_object_items);
+ RUN_TEST(cjson_get_object_item_case_sensitive_should_get_object_items);
+ RUN_TEST(typecheck_functions_should_check_type);
+
+ return UNITY_END();
+}
diff --git a/tests/parse_array.c b/tests/parse_array.c
@@ -46,13 +46,13 @@ static void assert_is_array(cJSON *array_item)
static void assert_not_array(const char *json)
{
- TEST_ASSERT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer));
+ TEST_ASSERT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
assert_is_invalid(item);
}
static void assert_parse_array(const char *json)
{
- TEST_ASSERT_NOT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer));
+ TEST_ASSERT_NOT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
assert_is_array(item);
}
@@ -124,7 +124,7 @@ static void parse_array_should_parse_arrays_with_multiple_elements(void)
i = 0;
(i < (sizeof(expected_types)/sizeof(int)))
&& (node != NULL);
- i++, node = node->next)
+ (void)i++, node = node->next)
{
TEST_ASSERT_BITS(0xFF, expected_types[i], node->type);
}
diff --git a/tests/parse_object.c b/tests/parse_object.c
@@ -54,14 +54,14 @@ static void assert_is_child(cJSON *child_item, const char *name, int type)
static void assert_not_object(const char *json)
{
- TEST_ASSERT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer));
+ TEST_ASSERT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
assert_is_invalid(item);
reset(item);
}
static void assert_parse_object(const char *json)
{
- TEST_ASSERT_NOT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer));
+ TEST_ASSERT_NOT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
assert_is_object(item);
}
@@ -76,7 +76,7 @@ static void parse_object_should_parse_empty_objects(void)
reset(item);
}
-static void parse_array_should_parse_arrays_with_one_element(void)
+static void parse_object_should_parse_objects_with_one_element(void)
{
assert_parse_object("{\"one\":1}");
@@ -134,7 +134,7 @@ static void parse_object_should_parse_objects_with_multiple_elements(void)
i = 0;
(i < (sizeof(expected_types)/sizeof(int)))
&& (node != NULL);
- i++, node = node->next)
+ (void)i++, node = node->next)
{
assert_is_child(node, expected_names[i], expected_types[i]);
}
@@ -163,6 +163,6 @@ int main(void)
RUN_TEST(parse_object_should_parse_empty_objects);
RUN_TEST(parse_object_should_not_parse_non_objects);
RUN_TEST(parse_object_should_parse_objects_with_multiple_elements);
- RUN_TEST(parse_array_should_parse_arrays_with_one_element);
+ RUN_TEST(parse_object_should_parse_objects_with_one_element);
return UNITY_END();
}
diff --git a/tests/parse_string.c b/tests/parse_string.c
@@ -47,15 +47,15 @@ static void assert_is_string(cJSON *string_item)
static void assert_parse_string(const char *string, const char *expected)
{
- TEST_ASSERT_NOT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer), "Couldn't parse string.");
+ TEST_ASSERT_NOT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Couldn't parse string.");
assert_is_string(item);
TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected.");
- cJSON_free(item->valuestring);
+ global_hooks.deallocate(item->valuestring);
item->valuestring = NULL;
}
#define assert_not_parse_string(string) \
- TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer), "Malformed string should not be accepted");\
+ TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Malformed string should not be accepted");\
assert_is_invalid(item)
diff --git a/tests/parse_value.c b/tests/parse_value.c
@@ -44,7 +44,7 @@ static void assert_is_value(cJSON *value_item, int type)
static void assert_parse_value(const char *string, int type)
{
- TEST_ASSERT_NOT_NULL(parse_value(item, (const unsigned char*)string, &error_pointer));
+ TEST_ASSERT_NOT_NULL(parse_value(item, (const unsigned char*)string, &error_pointer, &global_hooks));
assert_is_value(item, type);
}
diff --git a/tests/print_array.c b/tests/print_array.c
@@ -0,0 +1,92 @@
+/*
+ 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.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_array(const char * const expected, const char * const input)
+{
+ unsigned char printed_unformatted[1024];
+ unsigned char printed_formatted[1024];
+
+ const unsigned char *error_pointer;
+ cJSON item[1];
+
+ printbuffer formatted_buffer;
+ printbuffer unformatted_buffer;
+
+ /* buffer for formatted printing */
+ formatted_buffer.buffer = printed_formatted;
+ formatted_buffer.length = sizeof(printed_formatted);
+ formatted_buffer.offset = 0;
+ formatted_buffer.noalloc = true;
+
+ /* buffer for unformatted printing */
+ unformatted_buffer.buffer = printed_unformatted;
+ unformatted_buffer.length = sizeof(printed_unformatted);
+ unformatted_buffer.offset = 0;
+ unformatted_buffer.noalloc = true;
+
+ memset(item, 0, sizeof(item));
+ TEST_ASSERT_NOT_NULL_MESSAGE(parse_array(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse array.");
+
+ TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct.");
+
+ TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted array is not correct.");
+
+ reset(item);
+}
+
+static void print_array_should_print_empty_arrays(void)
+{
+ assert_print_array("[]", "[]");
+}
+
+static void print_array_should_print_arrays_with_one_element(void)
+{
+
+ assert_print_array("[1]", "[1]");
+ assert_print_array("[\"hello!\"]", "[\"hello!\"]");
+ assert_print_array("[[]]", "[[]]");
+ assert_print_array("[null]", "[null]");
+}
+
+static void print_array_should_print_arrays_with_multiple_elements(void)
+{
+ assert_print_array("[1, 2, 3]", "[1,2,3]");
+ assert_print_array("[1, null, true, false, [], \"hello\", {\n\t}]", "[1,null,true,false,[],\"hello\",{}]");
+}
+
+int main(void)
+{
+ /* initialize cJSON item */
+ UNITY_BEGIN();
+
+ RUN_TEST(print_array_should_print_empty_arrays);
+ RUN_TEST(print_array_should_print_arrays_with_one_element);
+ RUN_TEST(print_array_should_print_arrays_with_multiple_elements);
+
+ return UNITY_END();
+}
diff --git a/tests/print_number.c b/tests/print_number.c
@@ -0,0 +1,119 @@
+/*
+ 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.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_number(const char *expected, double input)
+{
+ unsigned char printed[1024];
+ cJSON item[1];
+ printbuffer buffer;
+ buffer.buffer = printed;
+ buffer.length = sizeof(printed);
+ buffer.offset = 0;
+ buffer.noalloc = true;
+
+ memset(item, 0, sizeof(item));
+ cJSON_SetNumberValue(item, input);
+
+ TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer, &global_hooks), "Failed to print number.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected.");
+}
+
+static void print_number_should_print_zero(void)
+{
+ assert_print_number("0", 0);
+}
+
+static void print_number_should_print_negative_integers(void)
+{
+ assert_print_number("-1", -1);
+ assert_print_number("-32768", -32768);
+ assert_print_number("-2147483648", -2147483648);
+}
+
+static void print_number_should_print_positive_integers(void)
+{
+ assert_print_number("1", 1);
+ assert_print_number("32767", 32767);
+ assert_print_number("2147483647", 2147483647);
+}
+
+static void print_number_should_print_positive_reals(void)
+{
+ assert_print_number("0.123", 0.123);
+ assert_print_number("1.000000e-09", 10e-10);
+ assert_print_number("1000000000000", 10e11);
+ assert_print_number("1.230000e+129", 123e+127);
+ assert_print_number("0", 123e-128); /* TODO: Maybe this shouldn't be 0 */
+}
+
+static void print_number_should_print_negative_reals(void)
+{
+ assert_print_number("-0.0123", -0.0123);
+ assert_print_number("-1.000000e-09", -10e-10);
+ assert_print_number("-1000000000000000000000", -10e20);
+ assert_print_number("-1.230000e+129", -123e+127);
+ assert_print_number("-1.230000e-126", -123e-128);
+}
+
+static void print_number_should_print_non_number(void)
+{
+ TEST_IGNORE();
+ /* FIXME: Cannot test this easily in C89! */
+ /* assert_print_number("null", NaN); */
+ /* assert_print_number("null", INFTY); */
+ /* assert_print_number("null", -INFTY); */
+}
+
+static void trim_trailing_zeroes_should_trim_trailing_zeroes(void)
+{
+ printbuffer buffer;
+ unsigned char number[100];
+ buffer.length = sizeof(number);
+ buffer.buffer = number;
+
+ strcpy((char*)number, "10.00");
+ buffer.offset = sizeof("10.00") - 1;
+ TEST_ASSERT_TRUE(trim_trailing_zeroes(&buffer));
+ TEST_ASSERT_EQUAL_UINT8('\0', buffer.buffer[buffer.offset]);
+ TEST_ASSERT_EQUAL_STRING("10", number);
+ TEST_ASSERT_EQUAL_UINT(sizeof("10") - 1, buffer.offset);
+}
+
+int main(void)
+{
+ /* initialize cJSON item */
+ UNITY_BEGIN();
+
+ RUN_TEST(print_number_should_print_zero);
+ RUN_TEST(print_number_should_print_negative_integers);
+ RUN_TEST(print_number_should_print_positive_integers);
+ RUN_TEST(print_number_should_print_positive_reals);
+ RUN_TEST(print_number_should_print_negative_reals);
+ RUN_TEST(print_number_should_print_non_number);
+ RUN_TEST(trim_trailing_zeroes_should_trim_trailing_zeroes);
+
+ return UNITY_END();
+}
diff --git a/tests/print_object.c b/tests/print_object.c
@@ -0,0 +1,92 @@
+/*
+ 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.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_object(const char * const expected, const char * const input)
+{
+ unsigned char printed_unformatted[1024];
+ unsigned char printed_formatted[1024];
+
+ const unsigned char *error_pointer;
+ cJSON item[1];
+
+ printbuffer formatted_buffer;
+ printbuffer unformatted_buffer;
+
+ /* buffer for formatted printing */
+ formatted_buffer.buffer = printed_formatted;
+ formatted_buffer.length = sizeof(printed_formatted);
+ formatted_buffer.offset = 0;
+ formatted_buffer.noalloc = true;
+
+ /* buffer for unformatted printing */
+ unformatted_buffer.buffer = printed_unformatted;
+ unformatted_buffer.length = sizeof(printed_unformatted);
+ unformatted_buffer.offset = 0;
+ unformatted_buffer.noalloc = true;
+
+ memset(item, 0, sizeof(item));
+ TEST_ASSERT_NOT_NULL_MESSAGE(parse_object(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse object.");
+
+ TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct.");
+
+ TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct.");
+
+ reset(item);
+}
+
+static void print_object_should_print_empty_objects(void)
+{
+ assert_print_object("{\n}", "{}");
+}
+
+static void print_object_should_print_objects_with_one_element(void)
+{
+
+ assert_print_object("{\n\t\"one\":\t1\n}", "{\"one\":1}");
+ assert_print_object("{\n\t\"hello\":\t\"world!\"\n}", "{\"hello\":\"world!\"}");
+ assert_print_object("{\n\t\"array\":\t[]\n}", "{\"array\":[]}");
+ assert_print_object("{\n\t\"null\":\tnull\n}", "{\"null\":null}");
+}
+
+static void print_object_should_print_objects_with_multiple_elements(void)
+{
+ assert_print_object("{\n\t\"one\":\t1,\n\t\"two\":\t2,\n\t\"three\":\t3\n}", "{\"one\":1,\"two\":2,\"three\":3}");
+ assert_print_object("{\n\t\"one\":\t1,\n\t\"NULL\":\tnull,\n\t\"TRUE\":\ttrue,\n\t\"FALSE\":\tfalse,\n\t\"array\":\t[],\n\t\"world\":\t\"hello\",\n\t\"object\":\t{\n\t}\n}", "{\"one\":1,\"NULL\":null,\"TRUE\":true,\"FALSE\":false,\"array\":[],\"world\":\"hello\",\"object\":{}}");
+}
+
+int main(void)
+{
+ /* initialize cJSON item */
+ UNITY_BEGIN();
+
+ RUN_TEST(print_object_should_print_empty_objects);
+ RUN_TEST(print_object_should_print_objects_with_one_element);
+ RUN_TEST(print_object_should_print_objects_with_multiple_elements);
+
+ return UNITY_END();
+}
diff --git a/tests/print_string.c b/tests/print_string.c
@@ -0,0 +1,77 @@
+/*
+ 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.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_string(const char *expected, const char *input)
+{
+ unsigned char printed[1024];
+ printbuffer buffer;
+ buffer.buffer = printed;
+ buffer.length = sizeof(printed);
+ buffer.offset = 0;
+ buffer.noalloc = true;
+
+ TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer, &global_hooks), "Failed to print string.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected.");
+}
+
+static void print_string_should_print_empty_strings(void)
+{
+ assert_print_string("\"\"", "");
+ assert_print_string("\"\"", NULL);
+}
+
+static void print_string_should_print_ascii(void)
+{
+ char ascii[0x7F];
+ size_t i = 1;
+
+ /* create ascii table */
+ for (i = 1; i < 0x7F; i++)
+ {
+ ascii[i-1] = (char)i;
+ }
+ ascii[0x7F-1] = '\0';
+
+ assert_print_string("\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
+ ascii);
+}
+
+static void print_string_should_print_utf8(void)
+{
+ assert_print_string("\"ü猫慕\"", "ü猫慕");
+}
+
+int main(void)
+{
+ /* initialize cJSON item */
+ UNITY_BEGIN();
+
+ RUN_TEST(print_string_should_print_empty_strings);
+ RUN_TEST(print_string_should_print_ascii);
+ RUN_TEST(print_string_should_print_utf8);
+
+ return UNITY_END();
+}
diff --git a/tests/print_value.c b/tests/print_value.c
@@ -0,0 +1,102 @@
+/*
+ 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_value(const char *input)
+{
+ unsigned char printed[1024];
+ const unsigned char *error_pointer = NULL;
+ cJSON item[1];
+ printbuffer buffer;
+ buffer.buffer = printed;
+ buffer.length = sizeof(printed);
+ buffer.offset = 0;
+ buffer.noalloc = true;
+
+ memset(item, 0, sizeof(item));
+
+ TEST_ASSERT_NOT_NULL_MESSAGE(parse_value(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse value.");
+
+ TEST_ASSERT_TRUE_MESSAGE(print_value(item, 0, false, &buffer, &global_hooks), "Failed to print value.");
+ TEST_ASSERT_EQUAL_STRING_MESSAGE(input, buffer.buffer, "Printed value is not as expected.");
+
+ reset(item);
+}
+
+static void print_value_should_print_null(void)
+{
+ assert_print_value("null");
+}
+
+static void print_value_should_print_true(void)
+{
+ assert_print_value("true");
+}
+
+static void print_value_should_print_false(void)
+{
+ assert_print_value("false");
+}
+
+static void print_value_should_print_number(void)
+{
+ assert_print_value("1.5");
+}
+
+static void print_value_should_print_string(void)
+{
+ assert_print_value("\"\"");
+ assert_print_value("\"hello\"");
+}
+
+static void print_value_should_print_array(void)
+{
+ assert_print_value("[]");
+}
+
+static void print_value_should_print_object(void)
+{
+ assert_print_value("{}");
+}
+
+int main(void)
+{
+ /* initialize cJSON item */
+ UNITY_BEGIN();
+
+ RUN_TEST(print_value_should_print_null);
+ RUN_TEST(print_value_should_print_true);
+ RUN_TEST(print_value_should_print_false);
+ RUN_TEST(print_value_should_print_number);
+ RUN_TEST(print_value_should_print_string);
+ RUN_TEST(print_value_should_print_array);
+ RUN_TEST(print_value_should_print_object);
+
+ return UNITY_END();
+}
diff --git a/tests/unity/README.md b/tests/unity/README.md
@@ -41,13 +41,13 @@ Example:
main()
{
- if (TEST_PROTECT() == 0)
+ if (TEST_PROTECT())
{
MyTest();
}
}
-If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a non-zero return value.
+If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a return value of zero.
Unity Assertion Summary
diff --git a/tests/unity/auto/parseOutput.rb b/tests/unity/auto/parseOutput.rb
@@ -65,6 +65,17 @@ class ParseOutput
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
end
end
+
+# Test was flagged as having passed so format the output.
+# This is using the Unity fixture output and not the original Unity output.
+ def testPassedUnityFixture(array)
+ testSuite = array[0].sub("TEST(", "")
+ testSuite = testSuite.sub(",", "")
+ testName = array[1].sub(")", "")
+ if @xmlOut == true
+ @arrayList.push " <testcase classname=\"" + testSuite + "\" name=\"" + testName + "\"/>"
+ end
+ end
# Test was flagged as being ingored so format the output
def testIgnored(array)
@@ -73,6 +84,14 @@ class ParseOutput
reason = array[lastItem].chomp
testSuiteVerify(array[@className])
printf "%-40s IGNORED\n", testName
+
+ if testName.start_with? "TEST("
+ array2 = testName.split(" ")
+ @testSuite = array2[0].sub("TEST(", "")
+ @testSuite = @testSuite.sub(",", "")
+ testName = array2[1].sub(")", "")
+ end
+
if @xmlOut == true
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
@arrayList.push " <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
@@ -87,6 +106,14 @@ class ParseOutput
reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
testSuiteVerify(array[@className])
printf "%-40s FAILED\n", testName
+
+ if testName.start_with? "TEST("
+ array2 = testName.split(" ")
+ @testSuite = array2[0].sub("TEST(", "")
+ @testSuite = @testSuite.sub(",", "")
+ testName = array2[1].sub(")", "")
+ end
+
if @xmlOut == true
@arrayList.push " <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
@arrayList.push " <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
@@ -138,7 +165,7 @@ class ParseOutput
lineSize = lineArray.size
# If we were able to split the line then we can look to see if any of our target words
# were found. Case is important.
- if lineSize >= 4
+ if ((lineSize >= 4) || (line.start_with? "TEST("))
# Determine if this test passed
if line.include? ":PASS"
testPassed(lineArray)
@@ -149,6 +176,12 @@ class ParseOutput
elsif line.include? ":IGNORE:"
testIgnored(lineArray)
testIgnore += 1
+ elsif line.start_with? "TEST("
+ if line.include? " PASS"
+ lineArray = line.split(" ")
+ testPassedUnityFixture(lineArray)
+ testPass += 1
+ end
# If none of the keywords are found there are no more tests for this suite so clear
# the test flag
else
diff --git a/tests/unity/src/unity.c b/tests/unity/src/unity.c
@@ -669,7 +669,7 @@ void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
UnityTestResultsFailBegin(lineNumber);
UnityPrint(UnityStrElement);
UnityPrintNumberUnsigned(num_elements - elements - 1);
- UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual);
+ UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual);
UnityAddMsgIfSpecified(msg);
UNITY_FAIL_AND_BAIL;
}
@@ -691,7 +691,7 @@ void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
if (!UnityFloatsWithin(delta, expected, actual))
{
UnityTestResultsFailBegin(lineNumber);
- UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual);
+ UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual);
UnityAddMsgIfSpecified(msg);
UNITY_FAIL_AND_BAIL;
}
@@ -746,7 +746,7 @@ void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
UnityPrint(trait_names[trait_index]);
UnityPrint(UnityStrWas);
#ifndef UNITY_EXCLUDE_FLOAT_PRINT
- UnityPrintFloat(actual);
+ UnityPrintFloat((UNITY_DOUBLE)actual);
#else
if (should_be_trait)
UnityPrint(UnityStrNot);