Compare commits

..

38 Commits

Author SHA1 Message Date
Sang-Heon Jeon
5437b79086 Add getNumberValue function
* Add GetNumberValue function and testcase

Co-authored-by: Alan Wang <wp_scut@163.com>
2020-04-02 17:06:56 +08:00
caglarivriz
2371b7bc66 Added cJSON_ParseWithLength (#358)
Co-authored-by: Caglar Ivriz <caglar.ivriz@siemens.com>
2020-04-02 16:59:19 +08:00
Max Bruckner
0b20df9ecf Replace strcpy with memcpy and remove magic numbers for string sizes 2018-08-03 07:39:17 +02:00
Max Bruckner
054b4d146d Gitignore: add CLion files 2018-08-03 07:28:31 +02:00
Max Bruckner
ddd93934e6 cJSON: cjson_min: Wrap arguments in parentheses 2018-08-03 07:28:31 +02:00
Max Bruckner
3bd3b7aae7 cJSON.c: Remove unused cast 2018-08-03 07:28:31 +02:00
Max Bruckner
d06baf7052 is_{nan,infinity}: Wrap macro arguments in parentheses 2018-08-03 07:28:31 +02:00
Max Bruckner
cb5bd2c97b cJSON.c: Remove unnecessary includes 2018-08-03 07:28:31 +02:00
Max Bruckner
dcfa1618bb Remove superfluous null checks in can_read/access_at_index macros 2018-08-03 07:28:31 +02:00
Max Bruckner
bd307ec3b5 cJSON_Compare: Performance improvement for objects
Check the size to prevent comparing objects equal if they are prefixes
of each other.
2018-08-03 07:28:31 +02:00
Max Bruckner
4e9154458d parse_value: Check only first character at first
This should improve performance
2018-08-03 07:28:31 +02:00
Max Bruckner
a2ede77ee0 print_number: Introduce fast path for integers.
Thanks @Tangerino for suggesting this optimisation.
2018-08-03 07:28:31 +02:00
Max Bruckner
cfee6a7318 Extract helper: double_to_saturated_integer 2018-08-03 07:28:31 +02:00
Max Bruckner
9000f08b17 is_nan and is_infinity macros 2018-08-03 07:28:31 +02:00
Max Bruckner
1e95d4fe9a CMake: Remove -fsanitize=float-divide-by-zero
This is so that NaN and INFINITY values can be produced.
2018-08-03 07:28:31 +02:00
Max Bruckner
f520fdd432 Fix #234: Different argument names between declaration and definition 2018-08-03 07:28:31 +02:00
Max Bruckner
c21efcbaee print: Comment about why the buffer is reallocated 2018-08-03 07:28:31 +02:00
Max Bruckner
86234db095 Release cJSON v1.7.7 2018-05-21 22:08:21 +02:00
Max Bruckner
af5b4911de Fix memory leak if realloc returns NULL
Thanks @AlfieDeng for reporting
2018-05-21 22:00:07 +02:00
Max Bruckner
787d651e81 Contributors: Add Zhao Zhixu 2018-05-08 21:33:33 +02:00
Max Bruckner
1571a3ebe4 Merge pull request #266 from zhaozhixu/master
fix a typo in cJSON.h
2018-05-08 21:31:00 +02:00
Zhao Zhixu
0d5ecc11b6 fix typo 2018-05-08 22:45:14 +08:00
Max Bruckner
529ec06abb Makefile: Fix #263, use $(CC) instead of 'gcc' for detecting the version 2018-04-29 09:20:08 +02:00
Max Bruckner
3349978268 cJSON.c: Remove unnecessary include of float.h, fix #259 2018-04-26 23:58:51 +02:00
Max Bruckner
cbc05de76f Release version 1.7.6 2018-04-13 12:32:30 +08:00
Max Bruckner
7996a4a2ee Readme: Deprecate the Makefile 2018-04-13 12:12:26 +08:00
Max Bruckner
ed8fefc9ca Makefile: Fix #252, put soname in the ELF file 2018-04-13 12:03:33 +08:00
Max Bruckner
e6869c2e03 cJSON_Utils.h: Add include guards and extern C for C++ 2018-04-13 10:32:29 +08:00
Max Bruckner
0e0c463491 Release version 1.7.5 2018-03-22 20:29:17 +01:00
Max Bruckner
2336a0348d Contributors: Add Bob Kocisko 2018-03-22 20:28:54 +01:00
Max Bruckner
5d50f4efe1 Contributors: Thank all the non-code contributors 2018-03-22 20:28:30 +01:00
Max Bruckner
8abf110750 Merge pull request #251 from bobkocisko/master
json patch: adding to a subfield of a non-object now fails as expected
2018-03-22 20:00:24 +01:00
Bob Kocisko
d26a42af8d json patch: adding to a subfield of a non-object now fails as expected 2018-03-22 11:10:29 -04:00
Max Bruckner
6f264b5d0c Merge pull request #249 from DaveGamble/fix-add-item
Release 1.7.4
2018-03-02 20:04:29 +01:00
Max Bruckner
5da9edc8b1 Release version 1.7.4 2018-03-02 19:57:36 +01:00
Max Bruckner
22a7d04fa0 add_item_to_object: Fix use-after-free when string is aliased
If the `string` property of the item that is added is an alias to the
`string` parameter of `add_item_to_object`, and `constant` is false,
`cJSON_strdup` would access the string after it has been freed.

Thanks @hhallen for reporting this in #248.
2018-03-02 19:49:55 +01:00
Max Bruckner
a559eac472 Release version 1.7.3 2018-02-07 21:16:35 +01:00
Max Bruckner
d514bb866e Fix #241, potential double free 2018-02-07 19:36:59 +01:00
13 changed files with 423 additions and 168 deletions

2
.gitignore vendored
View File

@@ -14,3 +14,5 @@ libcjson.so.*
libcjson_utils.so.* libcjson_utils.so.*
*.orig *.orig
.vscode .vscode
.idea
cmake-build-debug

View File

@@ -1,3 +1,39 @@
1.7.7
=====
Fixes:
------
* Fix a memory leak when realloc fails (see #267), thanks @AlfieDeng for reporting
* Fix a typo in the header file (see #266), thanks @zhaozhixu
1.7.6
=====
Fixes:
------
* Add `SONAME` to the ELF files built by the Makefile (see #252), thanks @YanhaoMo for reporting
* Add include guards and `extern "C"` to `cJSON_Utils.h` (see #256), thanks @daschfg for reporting
Other changes:
--------------
* Mark the Makefile as deprecated in the README.
1.7.5
=====
Fixes:
------
* Fix a bug in the JSON Patch implementation of `cJSON Utils` (see #251), thanks @bobkocisko.
1.7.4
=====
Fixes:
------
* Fix potential use after free if the `string` parameter to `cJSON_AddItemToObject` is an alias of the `string` property of the object that is added (#248). Thanks @hhallen for reporting.
1.7.3
=====
Fixes:
------
* Fix potential double free, thanks @projectgus for reporting (see #241)
1.7.2 1.7.2
===== =====
Fixes: Fixes:

View File

@@ -7,7 +7,7 @@ include(GNUInstallDirs)
set(PROJECT_VERSION_MAJOR 1) set(PROJECT_VERSION_MAJOR 1)
set(PROJECT_VERSION_MINOR 7) set(PROJECT_VERSION_MINOR 7)
set(PROJECT_VERSION_PATCH 2) set(PROJECT_VERSION_PATCH 7)
set(CJSON_VERSION_SO 1) set(CJSON_VERSION_SO 1)
set(CJSON_UTILS_VERSION_SO 1) set(CJSON_UTILS_VERSION_SO 1)
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
@@ -64,7 +64,6 @@ 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

View File

@@ -7,6 +7,7 @@ Current Maintainer: [Max Bruckner](https://github.com/FSMaxB)
* [Ajay Bhargav](https://github.com/ajaybhargav) * [Ajay Bhargav](https://github.com/ajaybhargav)
* [Alper Akcan](https://github.com/alperakcan) * [Alper Akcan](https://github.com/alperakcan)
* [Anton Sergeev](https://github.com/anton-sergeev) * [Anton Sergeev](https://github.com/anton-sergeev)
* [Bob Kocisko](https://github.com/bobkocisko)
* [Christian Schulze](https://github.com/ChristianSch) * [Christian Schulze](https://github.com/ChristianSch)
* [Casperinous](https://github.com/Casperinous) * [Casperinous](https://github.com/Casperinous)
* [Debora Grosse](https://github.com/DeboraG) * [Debora Grosse](https://github.com/DeboraG)
@@ -40,5 +41,8 @@ Current Maintainer: [Max Bruckner](https://github.com/FSMaxB)
* [Stephan Gatzka](https://github.com/gatzka) * [Stephan Gatzka](https://github.com/gatzka)
* [Weston Schmidt](https://github.com/schmidtw) * [Weston Schmidt](https://github.com/schmidtw)
* [yangfl](https://github.com/yangfl) * [yangfl](https://github.com/yangfl)
* [Zhao Zhixu](https://github.com/zhaozhixu)
And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0) And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0)
Also thanks to all the people who reported bugs and suggested new features.

View File

@@ -8,10 +8,13 @@ CJSON_TEST_SRC = cJSON.c test.c
LDLIBS = -lm LDLIBS = -lm
LIBVERSION = 1.7.2 LIBVERSION = 1.7.7
CJSON_SOVERSION = 1 CJSON_SOVERSION = 1
UTILS_SOVERSION = 1 UTILS_SOVERSION = 1
CJSON_SO_LDFLAG=-Wl,-soname=$(CJSON_LIBNAME).so.$(CJSON_SOVERSION)
UTILS_SO_LDFLAG=-Wl,-soname=$(UTILS_LIBNAME).so.$(UTILS_SOVERSION)
PREFIX ?= /usr/local PREFIX ?= /usr/local
INCLUDE_PATH ?= include/cjson INCLUDE_PATH ?= include/cjson
LIBRARY_PATH ?= lib LIBRARY_PATH ?= lib
@@ -23,7 +26,7 @@ INSTALL ?= cp -a
# validate gcc version for use fstack-protector-strong # validate gcc version for use fstack-protector-strong
MIN_GCC_VERSION = "4.9" MIN_GCC_VERSION = "4.9"
GCC_VERSION := "`gcc -dumpversion`" GCC_VERSION := "`$(CC) -dumpversion`"
IS_GCC_ABOVE_MIN_VERSION := $(shell expr "$(GCC_VERSION)" ">=" "$(MIN_GCC_VERSION)") IS_GCC_ABOVE_MIN_VERSION := $(shell expr "$(GCC_VERSION)" ">=" "$(MIN_GCC_VERSION)")
ifeq "$(IS_GCC_ABOVE_MIN_VERSION)" "1" ifeq "$(IS_GCC_ABOVE_MIN_VERSION)" "1"
CFLAGS += -fstack-protector-strong CFLAGS += -fstack-protector-strong
@@ -42,6 +45,8 @@ STATIC = a
## create dynamic (shared) library on Darwin (base OS for MacOSX and IOS) ## create dynamic (shared) library on Darwin (base OS for MacOSX and IOS)
ifeq (Darwin, $(uname)) ifeq (Darwin, $(uname))
SHARED = dylib SHARED = dylib
CJSON_SO_LDFLAG = ""
UTILS_SO_LDFLAG = ""
endif endif
#cJSON library names #cJSON library names
@@ -90,10 +95,10 @@ $(UTILS_STATIC): $(UTILS_OBJ)
#shared libraries .so.1.0.0 #shared libraries .so.1.0.0
#cJSON #cJSON
$(CJSON_SHARED_VERSION): $(CJSON_OBJ) $(CJSON_SHARED_VERSION): $(CJSON_OBJ)
$(CC) -shared -o $@ $< $(LDFLAGS) $(CC) -shared -o $@ $< $(CJSON_SO_LDFLAG) $(LDFLAGS)
#cJSON_Utils #cJSON_Utils
$(UTILS_SHARED_VERSION): $(UTILS_OBJ) $(UTILS_SHARED_VERSION): $(UTILS_OBJ)
$(CC) -shared -o $@ $< $(LDFLAGS) $(CC) -shared -o $@ $< $(UTILS_SO_LDFLAG) $(LDFLAGS)
#objects #objects
#cJSON #cJSON

View File

@@ -127,9 +127,11 @@ make DESTDIR=$pkgdir install
On Windows CMake is usually used to create a Visual Studio solution file by running it inside the Developer Command Prompt for Visual Studio, for exact steps follow the official documentation from CMake and Microsoft and use the online search engine of your choice. The descriptions of the the options above still generally apply, although not all of them work on Windows. On Windows CMake is usually used to create a Visual Studio solution file by running it inside the Developer Command Prompt for Visual Studio, for exact steps follow the official documentation from CMake and Microsoft and use the online search engine of your choice. The descriptions of the the options above still generally apply, although not all of them work on Windows.
#### Makefile #### Makefile
**NOTE:** This Method is deprecated. Use CMake if at all possible. Makefile support is limited to fixing bugs.
If you don't have CMake available, but still have GNU make. You can use the makefile to build cJSON: If you don't have CMake available, but still have GNU make. You can use the makefile to build cJSON:
Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program. Run this command in the directory with the source code and it will automatically compile static and shared libraries and a little test program (not the full test suite).
``` ```
make all make all
@@ -243,6 +245,12 @@ 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`.
@@ -253,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)`. 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`.

370
cJSON.c
View File

@@ -39,9 +39,7 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <math.h>
#include <stdlib.h> #include <stdlib.h>
#include <float.h>
#include <limits.h> #include <limits.h>
#include <ctype.h> #include <ctype.h>
@@ -73,16 +71,28 @@ 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 != 2) #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.
#endif #endif
@@ -235,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 */ /* get the decimal point character of the current locale */
static unsigned char get_decimal_point(void) static unsigned char get_decimal_point(void)
{ {
@@ -256,9 +280,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 != 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) */ /* 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)) #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)
@@ -319,21 +343,7 @@ 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);
@@ -343,18 +353,7 @@ 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)
{ {
if (number >= INT_MAX) object->valueint = double_to_saturated_integer(number);
{
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;
} }
@@ -471,11 +470,15 @@ 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 d = item->valuedouble; double number = 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 */
@@ -487,21 +490,24 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
return false; return false;
} }
/* This checks for NaN and Infinity */ if (is_nan(number) || is_infinity(number))
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", d); length = sprintf((char*)number_buffer, "%1.15g", number);
/* 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 != d)) if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != number))
{ {
/* 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", d); length = sprintf((char*)number_buffer, "%1.17g", number);
} }
} }
@@ -842,12 +848,13 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
/* empty string */ /* empty string */
if (input == NULL) if (input == NULL)
{ {
output = ensure(output_buffer, sizeof("\"\"")); const char quotes[] = "\"\"";
output = ensure(output_buffer, sizeof(quotes));
if (output == NULL) if (output == NULL)
{ {
return false; return false;
} }
strcpy((char*)output, "\"\""); memcpy(output, quotes, sizeof(quotes));
return true; return true;
} }
@@ -968,6 +975,11 @@ 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++;
@@ -997,8 +1009,23 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
return 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) 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 } }; parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
cJSON *item = NULL; cJSON *item = NULL;
@@ -1007,13 +1034,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return
global_error.json = NULL; global_error.json = NULL;
global_error.position = 0; global_error.position = 0;
if (value == NULL) if (value == NULL || 0 == buffer_length)
{ {
goto fail; goto fail;
} }
buffer.content = (const unsigned char*)value; buffer.content = (const unsigned char*)value;
buffer.length = strlen((const char*)value) + sizeof(""); buffer.length = buffer_length;
buffer.offset = 0; buffer.offset = 0;
buffer.hooks = global_hooks; buffer.hooks = global_hooks;
@@ -1083,7 +1110,12 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
return cJSON_ParseWithOpts(value, 0, 0); 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) static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
{ {
@@ -1110,14 +1142,16 @@ 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)
{ {
printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
buffer->buffer = NULL;
if (printed == NULL) { if (printed == NULL) {
goto fail; goto fail;
} }
buffer->buffer = NULL;
} }
else /* otherwise copy the JSON over to a new buffer */ else /* otherwise copy the JSON over to a new buffer */
{ {
@@ -1190,20 +1224,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 *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 } }; printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
if ((len < 0) || (buf == NULL)) if ((length < 0) || (buffer == NULL))
{ {
return false; return false;
} }
p.buffer = (unsigned char*)buf; p.buffer = (unsigned char*)buffer;
p.length = (size_t)len; p.length = (size_t)length;
p.offset = 0; p.offset = 0;
p.noalloc = true; p.noalloc = true;
p.format = fmt; p.format = format;
p.hooks = global_hooks; p.hooks = global_hooks;
return print_value(item, &p); return print_value(item, &p);
@@ -1217,51 +1251,75 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf
return false; /* no input */ return false; /* no input */
} }
/* parse the different types of values */ if (!can_read(input_buffer, 1))
/* null */
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
{ {
item->type = cJSON_NULL; return false;
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; /* 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. */ /* Render a value to text. */
@@ -1277,31 +1335,40 @@ 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;
} }
strcpy((char*)output, "null"); memcpy(output, null_string, sizeof(null_string));
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;
} }
strcpy((char*)output, "false"); memcpy(output, false_string, sizeof(false_string));
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;
} }
strcpy((char*)output, "true"); memcpy(output, true_string, sizeof(true_string));
return true; return true;
}
case cJSON_Number: case cJSON_Number:
return print_number(item, output_buffer); return print_number(item, output_buffer);
@@ -1311,10 +1378,6 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp
size_t raw_length = 0; size_t raw_length = 0;
if (item->valuestring == NULL) if (item->valuestring == NULL)
{ {
if (!output_buffer->noalloc)
{
output_buffer->hooks.deallocate(output_buffer->buffer);
}
return false; return false;
} }
@@ -1721,8 +1784,7 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
return true; return true;
} }
/* Get Array size/item / object item. */ static size_t get_array_size(const cJSON * const array)
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
{ {
cJSON *child = NULL; cJSON *child = NULL;
size_t size = 0; size_t size = 0;
@@ -1734,13 +1796,25 @@ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
child = array->child; child = array->child;
while(child != NULL) while (child != NULL)
{ {
size++; size++;
child = child->next; 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; return (int)size;
} }
@@ -1899,32 +1973,37 @@ static void* cast_away_const(const void* string)
static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
{ {
char *new_key = NULL;
int new_type = cJSON_Invalid;
if ((object == NULL) || (string == NULL) || (item == NULL)) if ((object == NULL) || (string == NULL) || (item == NULL))
{ {
return false; return false;
} }
if (constant_key)
{
new_key = (char*)cast_away_const(string);
new_type = item->type | cJSON_StringIsConst;
}
else
{
new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
if (new_key == NULL)
{
return false;
}
new_type = item->type & ~cJSON_StringIsConst;
}
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{ {
hooks->deallocate(item->string); hooks->deallocate(item->string);
} }
if (constant_key) item->string = new_key;
{ item->type = new_type;
item->string = (char*)cast_away_const(string);
item->type |= cJSON_StringIsConst;
}
else
{
char *key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
if (key == NULL)
{
return false;
}
item->string = key;
item->type &= ~cJSON_StringIsConst;
}
return add_item_to_array(object, item); return add_item_to_array(object, item);
} }
@@ -2276,12 +2355,12 @@ CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
return item; 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); cJSON *item = cJSON_New_Item(&global_hooks);
if(item) if(item)
{ {
item->type = b ? cJSON_True : cJSON_False; item->type = boolean ? cJSON_True : cJSON_False;
} }
return item; return item;
@@ -2294,20 +2373,7 @@ 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;
@@ -2882,6 +2948,14 @@ 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! */
@@ -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; return true;
} }

11
cJSON.h
View File

@@ -31,7 +31,7 @@ extern "C"
/* project version */ /* project version */
#define CJSON_VERSION_MAJOR 1 #define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7 #define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 2 #define CJSON_VERSION_PATCH 7
#include <stddef.h> #include <stddef.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. */ /* 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);
@@ -152,11 +154,11 @@ 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 *c); CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
/* 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);
/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
/* Get item "string" from object. Case insensitive. */ /* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
@@ -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. */ /* 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 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(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);

View File

@@ -988,6 +988,12 @@ static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_
cJSON_AddItemToObject(parent, (char*)child_pointer, value); cJSON_AddItemToObject(parent, (char*)child_pointer, value);
value = NULL; value = NULL;
} }
else /* parent is not an object */
{
/* Couldn't find object to add to. */
status = 9;
goto cleanup;
}
cleanup: cleanup:
if (value != NULL) if (value != NULL)

View File

@@ -20,6 +20,14 @@
THE SOFTWARE. THE SOFTWARE.
*/ */
#ifndef cJSON_Utils__h
#define cJSON_Utils__h
#ifdef __cplusplus
extern "C"
{
#endif
#include "cJSON.h" #include "cJSON.h"
/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ /* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
@@ -72,3 +80,9 @@ CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const obje
/* Sorts the members of the object into alphabetical order. */ /* Sorts the members of the object into alphabetical order. */
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -80,5 +80,12 @@
"doc": { "foo": ["bar"] }, "doc": { "foo": ["bar"] },
"patch": [ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }], "patch": [ { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }],
"expected": {"foo": ["bar", ["abc", "def"]] } "expected": {"foo": ["bar", ["abc", "def"]] }
} },
{
"comment": "15",
"doc": {"foo": {"bar": 1}},
"patch": [{"op": "add", "path": "/foo/bar/baz", "value": "5"}],
"error": "attempting to add to subfield of non-object"
}
] ]

View File

@@ -463,6 +463,19 @@ 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!";
@@ -508,6 +521,43 @@ static void cjson_create_array_reference_should_create_an_array_reference(void)
cJSON_Delete(number_reference); cJSON_Delete(number_reference);
} }
static void cjson_add_item_to_object_should_not_use_after_free_when_string_is_aliased(void)
{
cJSON *object = cJSON_CreateObject();
cJSON *number = cJSON_CreateNumber(42);
char *name = (char*)cJSON_strdup((const unsigned char*)"number", &global_hooks);
TEST_ASSERT_NOT_NULL(object);
TEST_ASSERT_NOT_NULL(number);
TEST_ASSERT_NOT_NULL(name);
number->string = name;
/* The following should not have a use after free
* that would show up in valgrind or with AddressSanitizer */
cJSON_AddItemToObject(object, number->string, number);
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();
@@ -527,9 +577,13 @@ 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(is_nan_should_detect_nan);
RUN_TEST(is_infinity_should_detect_infinity);
return UNITY_END(); return UNITY_END();
} }

View File

@@ -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) int main(void)
{ {
UNITY_BEGIN(); UNITY_BEGIN();
@@ -210,5 +265,7 @@ 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();
} }