Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5437b79086 | ||
|
2371b7bc66 | ||
|
0b20df9ecf | ||
|
054b4d146d | ||
|
ddd93934e6 | ||
|
3bd3b7aae7 | ||
|
d06baf7052 | ||
|
cb5bd2c97b | ||
|
dcfa1618bb | ||
|
bd307ec3b5 | ||
|
4e9154458d | ||
|
a2ede77ee0 | ||
|
cfee6a7318 | ||
|
9000f08b17 | ||
|
1e95d4fe9a | ||
|
f520fdd432 | ||
|
c21efcbaee |
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,3 +14,5 @@ libcjson.so.*
|
||||
libcjson_utils.so.*
|
||||
*.orig
|
||||
.vscode
|
||||
.idea
|
||||
cmake-build-debug
|
||||
|
@ -64,7 +64,6 @@ if (ENABLE_SANITIZERS)
|
||||
-fno-omit-frame-pointer
|
||||
-fsanitize=address
|
||||
-fsanitize=undefined
|
||||
-fsanitize=float-divide-by-zero
|
||||
-fsanitize=float-cast-overflow
|
||||
-fsanitize-address-use-after-scope
|
||||
-fsanitize=integer
|
||||
|
@ -245,6 +245,12 @@ Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`
|
||||
cJSON *json = cJSON_Parse(string);
|
||||
```
|
||||
|
||||
Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`.
|
||||
|
||||
```c
|
||||
cJSON *json = cJSON_ParseWithLength(string, buffer_length);
|
||||
```
|
||||
|
||||
It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`.
|
||||
|
||||
The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`.
|
||||
@ -255,6 +261,8 @@ By default, characters in the input string that follow the parsed JSON will not
|
||||
If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`.
|
||||
`return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON.
|
||||
|
||||
If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`.
|
||||
|
||||
### Printing JSON
|
||||
|
||||
Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.
|
||||
|
324
cJSON.c
324
cJSON.c
@ -39,7 +39,6 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
@ -72,14 +71,26 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
|
||||
return (const char*) (global_error.json + global_error.position);
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) {
|
||||
if (!cJSON_IsString(item)) {
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item)
|
||||
{
|
||||
if (!cJSON_IsString(item))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return item->valuestring;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item)
|
||||
{
|
||||
if (!cJSON_IsNumber(item))
|
||||
{
|
||||
return 0.0/0.0;
|
||||
}
|
||||
|
||||
return item->valuedouble;
|
||||
}
|
||||
|
||||
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
|
||||
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 7)
|
||||
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
|
||||
@ -234,6 +245,20 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
|
||||
}
|
||||
}
|
||||
|
||||
static int double_to_saturated_integer(double number)
|
||||
{
|
||||
if (number >= INT_MAX)
|
||||
{
|
||||
return INT_MAX;
|
||||
}
|
||||
else if (number <= INT_MIN)
|
||||
{
|
||||
return INT_MIN;
|
||||
}
|
||||
|
||||
return (int)number;
|
||||
}
|
||||
|
||||
/* get the decimal point character of the current locale */
|
||||
static unsigned char get_decimal_point(void)
|
||||
{
|
||||
@ -255,9 +280,9 @@ typedef struct
|
||||
} parse_buffer;
|
||||
|
||||
/* check if the given size is left to read in a given parse buffer (starting with 1) */
|
||||
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
|
||||
#define can_read(buffer, size) (((buffer)->offset + (size)) <= (buffer)->length)
|
||||
/* check if the buffer can be accessed at the given index (starting with 0) */
|
||||
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
|
||||
#define can_access_at_index(buffer, index) (((buffer)->offset + (index)) < (buffer)->length)
|
||||
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
|
||||
/* get a pointer to the buffer at the position */
|
||||
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
|
||||
@ -318,21 +343,7 @@ loop_end:
|
||||
}
|
||||
|
||||
item->valuedouble = number;
|
||||
|
||||
/* use saturation in case of overflow */
|
||||
if (number >= INT_MAX)
|
||||
{
|
||||
item->valueint = INT_MAX;
|
||||
}
|
||||
else if (number <= INT_MIN)
|
||||
{
|
||||
item->valueint = INT_MIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->valueint = (int)number;
|
||||
}
|
||||
|
||||
item->valueint = double_to_saturated_integer(number);
|
||||
item->type = cJSON_Number;
|
||||
|
||||
input_buffer->offset += (size_t)(after_end - number_c_string);
|
||||
@ -342,18 +353,7 @@ loop_end:
|
||||
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
|
||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
|
||||
{
|
||||
if (number >= INT_MAX)
|
||||
{
|
||||
object->valueint = INT_MAX;
|
||||
}
|
||||
else if (number <= INT_MIN)
|
||||
{
|
||||
object->valueint = INT_MIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
object->valueint = (int)number;
|
||||
}
|
||||
object->valueint = double_to_saturated_integer(number);
|
||||
|
||||
return object->valuedouble = number;
|
||||
}
|
||||
@ -470,11 +470,15 @@ static void update_offset(printbuffer * const buffer)
|
||||
buffer->offset += strlen((const char*)buffer_pointer);
|
||||
}
|
||||
|
||||
#define is_nan(number) ((number) != (number))
|
||||
#define is_infinity(number) (!is_nan(number) && ((number) * 0) != 0)
|
||||
|
||||
/* Render the number nicely from the given item into a string. */
|
||||
static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
|
||||
{
|
||||
unsigned char *output_pointer = NULL;
|
||||
double d = item->valuedouble;
|
||||
double number = item->valuedouble;
|
||||
int integer = double_to_saturated_integer(number);
|
||||
int length = 0;
|
||||
size_t i = 0;
|
||||
unsigned char number_buffer[26]; /* temporary buffer to print the number into */
|
||||
@ -486,21 +490,24 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This checks for NaN and Infinity */
|
||||
if ((d * 0) != 0)
|
||||
if (is_nan(number) || is_infinity(number))
|
||||
{
|
||||
length = sprintf((char*)number_buffer, "null");
|
||||
}
|
||||
else if (number == integer) /* avoid overhead for integers */
|
||||
{
|
||||
length = sprintf((char*)number_buffer, "%d", integer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
|
||||
length = sprintf((char*)number_buffer, "%1.15g", d);
|
||||
length = sprintf((char*)number_buffer, "%1.15g", number);
|
||||
|
||||
/* Check whether the original double can be recovered */
|
||||
if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d))
|
||||
if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != number))
|
||||
{
|
||||
/* If not, print with 17 decimal places of precision */
|
||||
length = sprintf((char*)number_buffer, "%1.17g", d);
|
||||
length = sprintf((char*)number_buffer, "%1.17g", number);
|
||||
}
|
||||
}
|
||||
|
||||
@ -841,12 +848,13 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
|
||||
/* empty string */
|
||||
if (input == NULL)
|
||||
{
|
||||
output = ensure(output_buffer, sizeof("\"\""));
|
||||
const char quotes[] = "\"\"";
|
||||
output = ensure(output_buffer, sizeof(quotes));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "\"\"");
|
||||
memcpy(output, quotes, sizeof(quotes));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -967,6 +975,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cannot_access_at_index(buffer, 0))
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
|
||||
{
|
||||
buffer->offset++;
|
||||
@ -996,8 +1009,23 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/* Parse an object - create a new root, and populate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
|
||||
{
|
||||
size_t buffer_length;
|
||||
|
||||
if (NULL == value)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Adding null character size due to require_null_terminated. */
|
||||
buffer_length = strlen(value) + sizeof("");
|
||||
|
||||
return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
|
||||
}
|
||||
|
||||
/* Parse an object - create a new root, and populate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
|
||||
{
|
||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
cJSON *item = NULL;
|
||||
@ -1006,13 +1034,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return
|
||||
global_error.json = NULL;
|
||||
global_error.position = 0;
|
||||
|
||||
if (value == NULL)
|
||||
if (value == NULL || 0 == buffer_length)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buffer.content = (const unsigned char*)value;
|
||||
buffer.length = strlen((const char*)value) + sizeof("");
|
||||
buffer.length = buffer_length;
|
||||
buffer.offset = 0;
|
||||
buffer.hooks = global_hooks;
|
||||
|
||||
@ -1082,7 +1110,12 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
|
||||
return cJSON_ParseWithOpts(value, 0, 0);
|
||||
}
|
||||
|
||||
#define cjson_min(a, b) ((a < b) ? a : b)
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
|
||||
{
|
||||
return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
|
||||
}
|
||||
|
||||
#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
|
||||
{
|
||||
@ -1109,6 +1142,8 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i
|
||||
}
|
||||
update_offset(buffer);
|
||||
|
||||
/* Reallocate the buffer so that it only uses as much as it needs.
|
||||
This can save up to 50% because ensure increases the buffer size by a factor of 2 */
|
||||
/* check if reallocate is available */
|
||||
if (hooks->reallocate != NULL)
|
||||
{
|
||||
@ -1189,20 +1224,20 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON
|
||||
return (char*)p.buffer;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
|
||||
{
|
||||
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
|
||||
if ((len < 0) || (buf == NULL))
|
||||
if ((length < 0) || (buffer == NULL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
p.buffer = (unsigned char*)buf;
|
||||
p.length = (size_t)len;
|
||||
p.buffer = (unsigned char*)buffer;
|
||||
p.length = (size_t)length;
|
||||
p.offset = 0;
|
||||
p.noalloc = true;
|
||||
p.format = fmt;
|
||||
p.format = format;
|
||||
p.hooks = global_hooks;
|
||||
|
||||
return print_value(item, &p);
|
||||
@ -1216,51 +1251,75 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf
|
||||
return false; /* no input */
|
||||
}
|
||||
|
||||
/* parse the different types of values */
|
||||
/* null */
|
||||
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
|
||||
if (!can_read(input_buffer, 1))
|
||||
{
|
||||
item->type = cJSON_NULL;
|
||||
input_buffer->offset += 4;
|
||||
return true;
|
||||
}
|
||||
/* false */
|
||||
if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
|
||||
{
|
||||
item->type = cJSON_False;
|
||||
input_buffer->offset += 5;
|
||||
return true;
|
||||
}
|
||||
/* true */
|
||||
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
|
||||
{
|
||||
item->type = cJSON_True;
|
||||
item->valueint = 1;
|
||||
input_buffer->offset += 4;
|
||||
return true;
|
||||
}
|
||||
/* string */
|
||||
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
|
||||
{
|
||||
return parse_string(item, input_buffer);
|
||||
}
|
||||
/* number */
|
||||
if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
|
||||
{
|
||||
return parse_number(item, input_buffer);
|
||||
}
|
||||
/* array */
|
||||
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
|
||||
{
|
||||
return parse_array(item, input_buffer);
|
||||
}
|
||||
/* object */
|
||||
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
|
||||
{
|
||||
return parse_object(item, input_buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
/* parse the different types of values */
|
||||
switch (buffer_at_offset(input_buffer)[0])
|
||||
{
|
||||
/* number */
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return parse_number(item, input_buffer);
|
||||
|
||||
/* string */
|
||||
case '\"':
|
||||
return parse_string(item, input_buffer);
|
||||
|
||||
/* array */
|
||||
case '[':
|
||||
return parse_array(item, input_buffer);
|
||||
|
||||
/* object */
|
||||
case '{':
|
||||
return parse_object(item, input_buffer);
|
||||
|
||||
/* null */
|
||||
case 'n':
|
||||
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
|
||||
{
|
||||
item->type = cJSON_NULL;
|
||||
input_buffer->offset += 4;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
/* true */
|
||||
case 't':
|
||||
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
|
||||
{
|
||||
item->type = cJSON_True;
|
||||
item->valueint = 1;
|
||||
input_buffer->offset += 4;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
/* false */
|
||||
case 'f':
|
||||
if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
|
||||
{
|
||||
item->type = cJSON_False;
|
||||
input_buffer->offset += 5;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Render a value to text. */
|
||||
@ -1276,31 +1335,40 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp
|
||||
switch ((item->type) & 0xFF)
|
||||
{
|
||||
case cJSON_NULL:
|
||||
output = ensure(output_buffer, 5);
|
||||
{
|
||||
const char null_string[] = "null";
|
||||
output = ensure(output_buffer, sizeof(null_string));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "null");
|
||||
memcpy(output, null_string, sizeof(null_string));
|
||||
return true;
|
||||
}
|
||||
|
||||
case cJSON_False:
|
||||
output = ensure(output_buffer, 6);
|
||||
{
|
||||
const char false_string[] = "false";
|
||||
output = ensure(output_buffer, sizeof(false_string));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "false");
|
||||
memcpy(output, false_string, sizeof(false_string));
|
||||
return true;
|
||||
}
|
||||
|
||||
case cJSON_True:
|
||||
output = ensure(output_buffer, 5);
|
||||
{
|
||||
const char true_string[] = "true";
|
||||
output = ensure(output_buffer, sizeof(true_string));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "true");
|
||||
memcpy(output, true_string, sizeof(true_string));
|
||||
return true;
|
||||
}
|
||||
|
||||
case cJSON_Number:
|
||||
return print_number(item, output_buffer);
|
||||
@ -1716,8 +1784,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get Array size/item / object item. */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
|
||||
static size_t get_array_size(const cJSON * const array)
|
||||
{
|
||||
cJSON *child = NULL;
|
||||
size_t size = 0;
|
||||
@ -1729,13 +1796,25 @@ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
|
||||
|
||||
child = array->child;
|
||||
|
||||
while(child != NULL)
|
||||
while (child != NULL)
|
||||
{
|
||||
size++;
|
||||
child = child->next;
|
||||
}
|
||||
|
||||
/* FIXME: Can overflow here. Cannot be fixed without breaking the API */
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Get Array size/item / object item. */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
|
||||
{
|
||||
size_t size = get_array_size(array);
|
||||
|
||||
if (size > INT_MAX)
|
||||
{
|
||||
/* This is incorrect but can't be fixed without breaking the API */
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
return (int)size;
|
||||
}
|
||||
@ -2276,12 +2355,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
|
||||
return item;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b)
|
||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
|
||||
{
|
||||
cJSON *item = cJSON_New_Item(&global_hooks);
|
||||
if(item)
|
||||
{
|
||||
item->type = b ? cJSON_True : cJSON_False;
|
||||
item->type = boolean ? cJSON_True : cJSON_False;
|
||||
}
|
||||
|
||||
return item;
|
||||
@ -2294,20 +2373,7 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
|
||||
{
|
||||
item->type = cJSON_Number;
|
||||
item->valuedouble = num;
|
||||
|
||||
/* use saturation in case of overflow */
|
||||
if (num >= INT_MAX)
|
||||
{
|
||||
item->valueint = INT_MAX;
|
||||
}
|
||||
else if (num <= INT_MIN)
|
||||
{
|
||||
item->valueint = INT_MIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
item->valueint = (int)num;
|
||||
}
|
||||
item->valueint = double_to_saturated_integer(num);
|
||||
}
|
||||
|
||||
return item;
|
||||
@ -2882,6 +2948,14 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons
|
||||
{
|
||||
cJSON *a_element = NULL;
|
||||
cJSON *b_element = NULL;
|
||||
size_t a_size = get_array_size(a);
|
||||
size_t b_size = get_array_size(b);
|
||||
|
||||
if (a_size != b_size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cJSON_ArrayForEach(a_element, a)
|
||||
{
|
||||
/* TODO This has O(n^2) runtime, which is horrible! */
|
||||
@ -2897,22 +2971,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons
|
||||
}
|
||||
}
|
||||
|
||||
/* doing this twice, once on a and b to prevent true comparison if a subset of b
|
||||
* TODO: Do this the proper way, this is just a fix for now */
|
||||
cJSON_ArrayForEach(b_element, b)
|
||||
{
|
||||
a_element = get_object_item(a, b_element->string, case_sensitive);
|
||||
if (a_element == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cJSON_Compare(b_element, a_element, case_sensitive))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
7
cJSON.h
7
cJSON.h
@ -138,9 +138,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||
|
||||
/* Render a cJSON entity to text for transfer/storage. */
|
||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
@ -152,7 +154,7 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||
|
||||
/* Returns the number of items in an array (or object). */
|
||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||
@ -165,8 +167,9 @@ CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *st
|
||||
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||
|
||||
/* Check if the item is a string and return its valuestring */
|
||||
/* Check item type and return its value */
|
||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
|
||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item);
|
||||
|
||||
/* These functions check the type of an item */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||
|
@ -463,6 +463,19 @@ static void cjson_get_string_value_should_get_a_string(void)
|
||||
cJSON_Delete(string);
|
||||
}
|
||||
|
||||
static void cjson_get_number_value_should_get_a_number(void)
|
||||
{
|
||||
cJSON *string = cJSON_CreateString("test");
|
||||
cJSON *number = cJSON_CreateNumber(1);
|
||||
|
||||
TEST_ASSERT_EQUAL_DOUBLE(cJSON_GetNumberValue(number), number->valuedouble);
|
||||
TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(string));
|
||||
TEST_ASSERT_DOUBLE_IS_NAN(cJSON_GetNumberValue(NULL));
|
||||
|
||||
cJSON_Delete(number);
|
||||
cJSON_Delete(string);
|
||||
}
|
||||
|
||||
static void cjson_create_string_reference_should_create_a_string_reference(void) {
|
||||
const char *string = "I am a string!";
|
||||
|
||||
@ -527,6 +540,24 @@ static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_al
|
||||
cJSON_Delete(object);
|
||||
}
|
||||
|
||||
static void is_nan_should_detect_nan(void)
|
||||
{
|
||||
double nan = 0.0/0.0;
|
||||
|
||||
TEST_ASSERT_TRUE(is_nan(nan));
|
||||
TEST_ASSERT_FALSE(is_nan(1));
|
||||
}
|
||||
|
||||
static void is_infinity_should_detect_infinity(void)
|
||||
{
|
||||
double negative_infinity = -1.0/0.0;
|
||||
double positive_infinity = 1.0/0.0;
|
||||
|
||||
TEST_ASSERT_TRUE(is_infinity(negative_infinity));
|
||||
TEST_ASSERT_TRUE(is_infinity(positive_infinity));
|
||||
TEST_ASSERT_FALSE(is_infinity(1));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
@ -546,10 +577,13 @@ int main(void)
|
||||
RUN_TEST(skip_utf8_bom_should_skip_bom);
|
||||
RUN_TEST(skip_utf8_bom_should_not_skip_bom_if_not_at_beginning);
|
||||
RUN_TEST(cjson_get_string_value_should_get_a_string);
|
||||
RUN_TEST(cjson_get_number_value_should_get_a_number);
|
||||
RUN_TEST(cjson_create_string_reference_should_create_a_string_reference);
|
||||
RUN_TEST(cjson_create_object_reference_should_create_an_object_reference);
|
||||
RUN_TEST(cjson_create_array_reference_should_create_an_array_reference);
|
||||
RUN_TEST(cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased);
|
||||
RUN_TEST(is_nan_should_detect_nan);
|
||||
RUN_TEST(is_infinity_should_detect_infinity);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
@ -195,6 +195,61 @@ static void test12_should_not_be_parsed(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void test13_should_be_parsed_without_null_termination(void)
|
||||
{
|
||||
cJSON *tree = NULL;
|
||||
const char test_13[] = "{" \
|
||||
"\"Image\":{" \
|
||||
"\"Width\":800," \
|
||||
"\"Height\":600," \
|
||||
"\"Title\":\"Viewfrom15thFloor\"," \
|
||||
"\"Thumbnail\":{" \
|
||||
"\"Url\":\"http:/*www.example.com/image/481989943\"," \
|
||||
"\"Height\":125," \
|
||||
"\"Width\":\"100\"" \
|
||||
"}," \
|
||||
"\"IDs\":[116,943,234,38793]" \
|
||||
"}" \
|
||||
"}";
|
||||
|
||||
char test_13_wo_null[sizeof(test_13) - 1];
|
||||
memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1);
|
||||
|
||||
tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json.");
|
||||
|
||||
if (tree != NULL)
|
||||
{
|
||||
cJSON_Delete(tree);
|
||||
}
|
||||
}
|
||||
|
||||
static void test14_should_not_be_parsed(void)
|
||||
{
|
||||
cJSON *tree = NULL;
|
||||
const char test_14[] = "{" \
|
||||
"\"Image\":{" \
|
||||
"\"Width\":800," \
|
||||
"\"Height\":600," \
|
||||
"\"Title\":\"Viewfrom15thFloor\"," \
|
||||
"\"Thumbnail\":{" \
|
||||
"\"Url\":\"http:/*www.example.com/image/481989943\"," \
|
||||
"\"Height\":125," \
|
||||
"\"Width\":\"100\"" \
|
||||
"}," \
|
||||
"\"IDs\":[116,943,234,38793]" \
|
||||
"}" \
|
||||
"}";
|
||||
|
||||
tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2);
|
||||
TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached.");
|
||||
|
||||
if (tree != NULL)
|
||||
{
|
||||
cJSON_Delete(tree);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
@ -210,5 +265,7 @@ int main(void)
|
||||
RUN_TEST(file_test10_should_be_parsed_and_printed);
|
||||
RUN_TEST(file_test11_should_be_parsed_and_printed);
|
||||
RUN_TEST(test12_should_not_be_parsed);
|
||||
RUN_TEST(test13_should_be_parsed_without_null_termination);
|
||||
RUN_TEST(test14_should_not_be_parsed);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user