mirror of
https://github.com/DaveGamble/cJSON.git
synced 2023-08-10 21:13:26 +03:00
Compare commits
1 Commits
develop
...
disable-no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b68e159e5f |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -14,5 +14,3 @@ libcjson.so.*
|
|||||||
libcjson_utils.so.*
|
libcjson_utils.so.*
|
||||||
*.orig
|
*.orig
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
|
||||||
cmake-build-debug
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ if (ENABLE_SANITIZERS)
|
|||||||
-fno-omit-frame-pointer
|
-fno-omit-frame-pointer
|
||||||
-fsanitize=address
|
-fsanitize=address
|
||||||
-fsanitize=undefined
|
-fsanitize=undefined
|
||||||
|
-fsanitize=float-divide-by-zero
|
||||||
-fsanitize=float-cast-overflow
|
-fsanitize=float-cast-overflow
|
||||||
-fsanitize-address-use-after-scope
|
-fsanitize-address-use-after-scope
|
||||||
-fsanitize=integer
|
-fsanitize=integer
|
||||||
|
|||||||
@@ -245,12 +245,6 @@ Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`
|
|||||||
cJSON *json = cJSON_Parse(string);
|
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`.
|
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`.
|
The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`.
|
||||||
@@ -261,8 +255,6 @@ 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)`.
|
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.
|
`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
|
### Printing JSON
|
||||||
|
|
||||||
Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.
|
Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.
|
||||||
|
|||||||
341
cJSON.c
341
cJSON.c
@@ -37,8 +37,26 @@
|
|||||||
#pragma warning (disable : 4001)
|
#pragma warning (disable : 4001)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Disable some non-standard functionality */
|
||||||
|
#ifdef _POSIX_SOURCE
|
||||||
|
#undef _POSIX_SOURCE
|
||||||
|
#endif
|
||||||
|
#ifdef _POSIX_C_SOURCE
|
||||||
|
#undef _POSIX_C_SOURCE
|
||||||
|
#endif
|
||||||
|
#ifdef _XOPEN_SOURCE
|
||||||
|
#undef _XOPEN_SOURCE
|
||||||
|
#endif
|
||||||
|
#ifdef _GNU_SOURCE
|
||||||
|
#undef _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
#ifdef _DEFAULT_SOURCE
|
||||||
|
#undef _DEFAULT_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -71,26 +89,14 @@ CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
|
|||||||
return (const char*) (global_error.json + global_error.position);
|
return (const char*) (global_error.json + global_error.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item)
|
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) {
|
||||||
{
|
if (!cJSON_IsString(item)) {
|
||||||
if (!cJSON_IsString(item))
|
|
||||||
{
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item->valuestring;
|
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 */
|
/* 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)
|
#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.
|
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
|
||||||
@@ -245,20 +251,6 @@ 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 */
|
/* get the decimal point character of the current locale */
|
||||||
static unsigned char get_decimal_point(void)
|
static unsigned char get_decimal_point(void)
|
||||||
{
|
{
|
||||||
@@ -280,9 +272,9 @@ typedef struct
|
|||||||
} parse_buffer;
|
} parse_buffer;
|
||||||
|
|
||||||
/* check if the given size is left to read in a given parse buffer (starting with 1) */
|
/* check if the given size is left to read in a given parse buffer (starting with 1) */
|
||||||
#define can_read(buffer, size) (((buffer)->offset + (size)) <= (buffer)->length)
|
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
|
||||||
/* check if the buffer can be accessed at the given index (starting with 0) */
|
/* check if the buffer can be accessed at the given index (starting with 0) */
|
||||||
#define can_access_at_index(buffer, index) (((buffer)->offset + (index)) < (buffer)->length)
|
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
|
||||||
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
|
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
|
||||||
/* get a pointer to the buffer at the position */
|
/* get a pointer to the buffer at the position */
|
||||||
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
|
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
|
||||||
@@ -343,7 +335,21 @@ loop_end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
item->valuedouble = number;
|
item->valuedouble = number;
|
||||||
item->valueint = double_to_saturated_integer(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->type = cJSON_Number;
|
item->type = cJSON_Number;
|
||||||
|
|
||||||
input_buffer->offset += (size_t)(after_end - number_c_string);
|
input_buffer->offset += (size_t)(after_end - number_c_string);
|
||||||
@@ -353,7 +359,18 @@ loop_end:
|
|||||||
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
|
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
|
||||||
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
|
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
|
||||||
{
|
{
|
||||||
object->valueint = double_to_saturated_integer(number);
|
if (number >= INT_MAX)
|
||||||
|
{
|
||||||
|
object->valueint = INT_MAX;
|
||||||
|
}
|
||||||
|
else if (number <= INT_MIN)
|
||||||
|
{
|
||||||
|
object->valueint = INT_MIN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object->valueint = (int)number;
|
||||||
|
}
|
||||||
|
|
||||||
return object->valuedouble = number;
|
return object->valuedouble = number;
|
||||||
}
|
}
|
||||||
@@ -470,15 +487,11 @@ static void update_offset(printbuffer * const buffer)
|
|||||||
buffer->offset += strlen((const char*)buffer_pointer);
|
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. */
|
/* Render the number nicely from the given item into a string. */
|
||||||
static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
|
static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
|
||||||
{
|
{
|
||||||
unsigned char *output_pointer = NULL;
|
unsigned char *output_pointer = NULL;
|
||||||
double number = item->valuedouble;
|
double d = item->valuedouble;
|
||||||
int integer = double_to_saturated_integer(number);
|
|
||||||
int length = 0;
|
int length = 0;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
unsigned char number_buffer[26]; /* temporary buffer to print the number into */
|
unsigned char number_buffer[26]; /* temporary buffer to print the number into */
|
||||||
@@ -490,24 +503,21 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_nan(number) || is_infinity(number))
|
/* This checks for NaN and Infinity */
|
||||||
|
if ((d * 0) != 0)
|
||||||
{
|
{
|
||||||
length = sprintf((char*)number_buffer, "null");
|
length = sprintf((char*)number_buffer, "null");
|
||||||
}
|
}
|
||||||
else if (number == integer) /* avoid overhead for integers */
|
|
||||||
{
|
|
||||||
length = sprintf((char*)number_buffer, "%d", integer);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
|
/* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
|
||||||
length = sprintf((char*)number_buffer, "%1.15g", number);
|
length = sprintf((char*)number_buffer, "%1.15g", d);
|
||||||
|
|
||||||
/* Check whether the original double can be recovered */
|
/* Check whether the original double can be recovered */
|
||||||
if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != number))
|
if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d))
|
||||||
{
|
{
|
||||||
/* If not, print with 17 decimal places of precision */
|
/* If not, print with 17 decimal places of precision */
|
||||||
length = sprintf((char*)number_buffer, "%1.17g", number);
|
length = sprintf((char*)number_buffer, "%1.17g", d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,13 +858,12 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
|
|||||||
/* empty string */
|
/* empty string */
|
||||||
if (input == NULL)
|
if (input == NULL)
|
||||||
{
|
{
|
||||||
const char quotes[] = "\"\"";
|
output = ensure(output_buffer, sizeof("\"\""));
|
||||||
output = ensure(output_buffer, sizeof(quotes));
|
|
||||||
if (output == NULL)
|
if (output == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(output, quotes, sizeof(quotes));
|
strcpy((char*)output, "\"\"");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -975,11 +984,6 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cannot_access_at_index(buffer, 0))
|
|
||||||
{
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
|
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
|
||||||
{
|
{
|
||||||
buffer->offset++;
|
buffer->offset++;
|
||||||
@@ -1009,23 +1013,8 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
|
|
||||||
{
|
|
||||||
size_t buffer_length;
|
|
||||||
|
|
||||||
if (NULL == value)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adding null character size due to require_null_terminated. */
|
|
||||||
buffer_length = strlen(value) + sizeof("");
|
|
||||||
|
|
||||||
return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Parse an object - create a new root, and populate. */
|
/* 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)
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
|
||||||
{
|
{
|
||||||
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
|
||||||
cJSON *item = NULL;
|
cJSON *item = NULL;
|
||||||
@@ -1034,13 +1023,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer
|
|||||||
global_error.json = NULL;
|
global_error.json = NULL;
|
||||||
global_error.position = 0;
|
global_error.position = 0;
|
||||||
|
|
||||||
if (value == NULL || 0 == buffer_length)
|
if (value == NULL)
|
||||||
{
|
{
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.content = (const unsigned char*)value;
|
buffer.content = (const unsigned char*)value;
|
||||||
buffer.length = buffer_length;
|
buffer.length = strlen((const char*)value) + sizeof("");
|
||||||
buffer.offset = 0;
|
buffer.offset = 0;
|
||||||
buffer.hooks = global_hooks;
|
buffer.hooks = global_hooks;
|
||||||
|
|
||||||
@@ -1110,12 +1099,7 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
|
|||||||
return cJSON_ParseWithOpts(value, 0, 0);
|
return cJSON_ParseWithOpts(value, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
|
#define cjson_min(a, b) ((a < b) ? a : b)
|
||||||
{
|
|
||||||
return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
|
|
||||||
|
|
||||||
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
|
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
|
||||||
{
|
{
|
||||||
@@ -1142,8 +1126,6 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i
|
|||||||
}
|
}
|
||||||
update_offset(buffer);
|
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 */
|
/* check if reallocate is available */
|
||||||
if (hooks->reallocate != NULL)
|
if (hooks->reallocate != NULL)
|
||||||
{
|
{
|
||||||
@@ -1224,20 +1206,20 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON
|
|||||||
return (char*)p.buffer;
|
return (char*)p.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
|
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt)
|
||||||
{
|
{
|
||||||
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||||
|
|
||||||
if ((length < 0) || (buffer == NULL))
|
if ((len < 0) || (buf == NULL))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.buffer = (unsigned char*)buffer;
|
p.buffer = (unsigned char*)buf;
|
||||||
p.length = (size_t)length;
|
p.length = (size_t)len;
|
||||||
p.offset = 0;
|
p.offset = 0;
|
||||||
p.noalloc = true;
|
p.noalloc = true;
|
||||||
p.format = format;
|
p.format = fmt;
|
||||||
p.hooks = global_hooks;
|
p.hooks = global_hooks;
|
||||||
|
|
||||||
return print_value(item, &p);
|
return print_value(item, &p);
|
||||||
@@ -1251,75 +1233,51 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf
|
|||||||
return false; /* no input */
|
return false; /* no input */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!can_read(input_buffer, 1))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* parse the different types of values */
|
/* parse the different types of values */
|
||||||
switch (buffer_at_offset(input_buffer)[0])
|
/* null */
|
||||||
|
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
|
||||||
{
|
{
|
||||||
/* number */
|
item->type = cJSON_NULL;
|
||||||
case '-':
|
input_buffer->offset += 4;
|
||||||
case '0':
|
return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
/* false */
|
||||||
|
if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
|
||||||
|
{
|
||||||
|
item->type = cJSON_False;
|
||||||
|
input_buffer->offset += 5;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* true */
|
||||||
|
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
|
||||||
|
{
|
||||||
|
item->type = cJSON_True;
|
||||||
|
item->valueint = 1;
|
||||||
|
input_buffer->offset += 4;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* string */
|
||||||
|
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
|
||||||
|
{
|
||||||
|
return parse_string(item, input_buffer);
|
||||||
|
}
|
||||||
|
/* number */
|
||||||
|
if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
|
||||||
|
{
|
||||||
|
return parse_number(item, input_buffer);
|
||||||
|
}
|
||||||
|
/* array */
|
||||||
|
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
|
||||||
|
{
|
||||||
|
return parse_array(item, input_buffer);
|
||||||
|
}
|
||||||
|
/* object */
|
||||||
|
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
|
||||||
|
{
|
||||||
|
return parse_object(item, input_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Render a value to text. */
|
/* Render a value to text. */
|
||||||
@@ -1335,40 +1293,31 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp
|
|||||||
switch ((item->type) & 0xFF)
|
switch ((item->type) & 0xFF)
|
||||||
{
|
{
|
||||||
case cJSON_NULL:
|
case cJSON_NULL:
|
||||||
{
|
output = ensure(output_buffer, 5);
|
||||||
const char null_string[] = "null";
|
|
||||||
output = ensure(output_buffer, sizeof(null_string));
|
|
||||||
if (output == NULL)
|
if (output == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(output, null_string, sizeof(null_string));
|
strcpy((char*)output, "null");
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
case cJSON_False:
|
case cJSON_False:
|
||||||
{
|
output = ensure(output_buffer, 6);
|
||||||
const char false_string[] = "false";
|
|
||||||
output = ensure(output_buffer, sizeof(false_string));
|
|
||||||
if (output == NULL)
|
if (output == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(output, false_string, sizeof(false_string));
|
strcpy((char*)output, "false");
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
case cJSON_True:
|
case cJSON_True:
|
||||||
{
|
output = ensure(output_buffer, 5);
|
||||||
const char true_string[] = "true";
|
|
||||||
output = ensure(output_buffer, sizeof(true_string));
|
|
||||||
if (output == NULL)
|
if (output == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memcpy(output, true_string, sizeof(true_string));
|
strcpy((char*)output, "true");
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
case cJSON_Number:
|
case cJSON_Number:
|
||||||
return print_number(item, output_buffer);
|
return print_number(item, output_buffer);
|
||||||
@@ -1784,7 +1733,8 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t get_array_size(const cJSON * const array)
|
/* Get Array size/item / object item. */
|
||||||
|
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
|
||||||
{
|
{
|
||||||
cJSON *child = NULL;
|
cJSON *child = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
@@ -1796,25 +1746,13 @@ static size_t get_array_size(const cJSON * const array)
|
|||||||
|
|
||||||
child = array->child;
|
child = array->child;
|
||||||
|
|
||||||
while (child != NULL)
|
while(child != NULL)
|
||||||
{
|
{
|
||||||
size++;
|
size++;
|
||||||
child = child->next;
|
child = child->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
/* FIXME: Can overflow here. Cannot be fixed without breaking the API */
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
return (int)size;
|
||||||
}
|
}
|
||||||
@@ -2355,12 +2293,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
|
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b)
|
||||||
{
|
{
|
||||||
cJSON *item = cJSON_New_Item(&global_hooks);
|
cJSON *item = cJSON_New_Item(&global_hooks);
|
||||||
if(item)
|
if(item)
|
||||||
{
|
{
|
||||||
item->type = boolean ? cJSON_True : cJSON_False;
|
item->type = b ? cJSON_True : cJSON_False;
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@@ -2373,7 +2311,20 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
|
|||||||
{
|
{
|
||||||
item->type = cJSON_Number;
|
item->type = cJSON_Number;
|
||||||
item->valuedouble = num;
|
item->valuedouble = num;
|
||||||
item->valueint = double_to_saturated_integer(num);
|
|
||||||
|
/* use saturation in case of overflow */
|
||||||
|
if (num >= INT_MAX)
|
||||||
|
{
|
||||||
|
item->valueint = INT_MAX;
|
||||||
|
}
|
||||||
|
else if (num <= INT_MIN)
|
||||||
|
{
|
||||||
|
item->valueint = INT_MIN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item->valueint = (int)num;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@@ -2948,14 +2899,6 @@ CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * cons
|
|||||||
{
|
{
|
||||||
cJSON *a_element = NULL;
|
cJSON *a_element = NULL;
|
||||||
cJSON *b_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)
|
cJSON_ArrayForEach(a_element, a)
|
||||||
{
|
{
|
||||||
/* TODO This has O(n^2) runtime, which is horrible! */
|
/* TODO This has O(n^2) runtime, which is horrible! */
|
||||||
@@ -2971,6 +2914,22 @@ 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
cJSON.h
7
cJSON.h
@@ -138,11 +138,9 @@ 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. */
|
/* 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. */
|
/* 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_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. */
|
/* 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(). */
|
/* 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_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. */
|
/* Render a cJSON entity to text for transfer/storage. */
|
||||||
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||||
@@ -154,7 +152,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 */
|
/* 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);
|
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||||
/* Delete a cJSON entity and all subentities. */
|
/* Delete a cJSON entity and all subentities. */
|
||||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
|
||||||
|
|
||||||
/* Returns the number of items in an array (or object). */
|
/* Returns the number of items in an array (or object). */
|
||||||
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||||
@@ -167,9 +165,8 @@ 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. */
|
/* 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);
|
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||||
|
|
||||||
/* Check item type and return its value */
|
/* Check if the item is a string and return its valuestring */
|
||||||
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
|
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
|
||||||
CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item);
|
|
||||||
|
|
||||||
/* These functions check the type of an item */
|
/* These functions check the type of an item */
|
||||||
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||||
|
|||||||
@@ -463,19 +463,6 @@ static void cjson_get_string_value_should_get_a_string(void)
|
|||||||
cJSON_Delete(string);
|
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) {
|
static void cjson_create_string_reference_should_create_a_string_reference(void) {
|
||||||
const char *string = "I am a string!";
|
const char *string = "I am a string!";
|
||||||
|
|
||||||
@@ -540,24 +527,6 @@ static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_al
|
|||||||
cJSON_Delete(object);
|
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)
|
int main(void)
|
||||||
{
|
{
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
@@ -577,13 +546,10 @@ int main(void)
|
|||||||
RUN_TEST(skip_utf8_bom_should_skip_bom);
|
RUN_TEST(skip_utf8_bom_should_skip_bom);
|
||||||
RUN_TEST(skip_utf8_bom_should_not_skip_bom_if_not_at_beginning);
|
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_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_string_reference_should_create_a_string_reference);
|
||||||
RUN_TEST(cjson_create_object_reference_should_create_an_object_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_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(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();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,61 +195,6 @@ 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)
|
int main(void)
|
||||||
{
|
{
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
@@ -265,7 +210,5 @@ int main(void)
|
|||||||
RUN_TEST(file_test10_should_be_parsed_and_printed);
|
RUN_TEST(file_test10_should_be_parsed_and_printed);
|
||||||
RUN_TEST(file_test11_should_be_parsed_and_printed);
|
RUN_TEST(file_test11_should_be_parsed_and_printed);
|
||||||
RUN_TEST(test12_should_not_be_parsed);
|
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();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user