Skip to content

Commit 2371b7b

Browse files
Added cJSON_ParseWithLength (#358)
Co-authored-by: Caglar Ivriz <[email protected]>
1 parent 0b20df9 commit 2371b7b

File tree

4 files changed

+95
-3
lines changed

4 files changed

+95
-3
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,12 @@ Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`
245245
cJSON *json = cJSON_Parse(string);
246246
```
247247

248+
Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`.
249+
250+
```c
251+
cJSON *json = cJSON_ParseWithLength(string, buffer_length);
252+
```
253+
248254
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`.
249255

250256
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
255261
If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`.
256262
`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.
257263

264+
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)`.
265+
258266
### Printing JSON
259267

260268
Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.

cJSON.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
963963
return NULL;
964964
}
965965

966+
if (cannot_access_at_index(buffer, 0))
967+
{
968+
return buffer;
969+
}
970+
966971
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
967972
{
968973
buffer->offset++;
@@ -992,8 +997,23 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
992997
return buffer;
993998
}
994999

995-
/* Parse an object - create a new root, and populate. */
9961000
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
1001+
{
1002+
size_t buffer_length;
1003+
1004+
if (NULL == value)
1005+
{
1006+
return NULL;
1007+
}
1008+
1009+
/* Adding null character size due to require_null_terminated. */
1010+
buffer_length = strlen(value) + sizeof("");
1011+
1012+
return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
1013+
}
1014+
1015+
/* Parse an object - create a new root, and populate. */
1016+
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
9971017
{
9981018
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
9991019
cJSON *item = NULL;
@@ -1002,13 +1022,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return
10021022
global_error.json = NULL;
10031023
global_error.position = 0;
10041024

1005-
if (value == NULL)
1025+
if (value == NULL || 0 == buffer_length)
10061026
{
10071027
goto fail;
10081028
}
10091029

10101030
buffer.content = (const unsigned char*)value;
1011-
buffer.length = strlen(value) + sizeof("");
1031+
buffer.length = buffer_length;
10121032
buffer.offset = 0;
10131033
buffer.hooks = global_hooks;
10141034

@@ -1078,6 +1098,11 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
10781098
return cJSON_ParseWithOpts(value, 0, 0);
10791099
}
10801100

1101+
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
1102+
{
1103+
return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
1104+
}
1105+
10811106
#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
10821107

10831108
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)

cJSON.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
138138
/* 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. */
139139
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
140140
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
141+
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
141142
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
142143
/* 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(). */
143144
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
145+
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
144146

145147
/* Render a cJSON entity to text for transfer/storage. */
146148
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);

tests/parse_examples.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,61 @@ static void test12_should_not_be_parsed(void)
195195
}
196196
}
197197

198+
static void test13_should_be_parsed_without_null_termination(void)
199+
{
200+
cJSON *tree = NULL;
201+
const char test_13[] = "{" \
202+
"\"Image\":{" \
203+
"\"Width\":800," \
204+
"\"Height\":600," \
205+
"\"Title\":\"Viewfrom15thFloor\"," \
206+
"\"Thumbnail\":{" \
207+
"\"Url\":\"http:/*www.example.com/image/481989943\"," \
208+
"\"Height\":125," \
209+
"\"Width\":\"100\"" \
210+
"}," \
211+
"\"IDs\":[116,943,234,38793]" \
212+
"}" \
213+
"}";
214+
215+
char test_13_wo_null[sizeof(test_13) - 1];
216+
memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1);
217+
218+
tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1);
219+
TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json.");
220+
221+
if (tree != NULL)
222+
{
223+
cJSON_Delete(tree);
224+
}
225+
}
226+
227+
static void test14_should_not_be_parsed(void)
228+
{
229+
cJSON *tree = NULL;
230+
const char test_14[] = "{" \
231+
"\"Image\":{" \
232+
"\"Width\":800," \
233+
"\"Height\":600," \
234+
"\"Title\":\"Viewfrom15thFloor\"," \
235+
"\"Thumbnail\":{" \
236+
"\"Url\":\"http:/*www.example.com/image/481989943\"," \
237+
"\"Height\":125," \
238+
"\"Width\":\"100\"" \
239+
"}," \
240+
"\"IDs\":[116,943,234,38793]" \
241+
"}" \
242+
"}";
243+
244+
tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2);
245+
TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached.");
246+
247+
if (tree != NULL)
248+
{
249+
cJSON_Delete(tree);
250+
}
251+
}
252+
198253
int main(void)
199254
{
200255
UNITY_BEGIN();
@@ -210,5 +265,7 @@ int main(void)
210265
RUN_TEST(file_test10_should_be_parsed_and_printed);
211266
RUN_TEST(file_test11_should_be_parsed_and_printed);
212267
RUN_TEST(test12_should_not_be_parsed);
268+
RUN_TEST(test13_should_be_parsed_without_null_termination);
269+
RUN_TEST(test14_should_not_be_parsed);
213270
return UNITY_END();
214271
}

0 commit comments

Comments
 (0)