Skip to content

Commit

Permalink
Added cJSON_ParseWithLength (#358)
Browse files Browse the repository at this point in the history
Co-authored-by: Caglar Ivriz <[email protected]>
  • Loading branch information
caglarivriz and ivrizcaglar authored Apr 2, 2020
1 parent 0b20df9 commit 2371b7b
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 3 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`
cJSON *json = cJSON_Parse(string);
```

Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`.

```c
cJSON *json = cJSON_ParseWithLength(string, buffer_length);
```

It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`.

The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`.
Expand All @@ -255,6 +261,8 @@ By default, characters in the input string that follow the parsed JSON will not
If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`.
`return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON.

If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`.

### Printing JSON

Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.
Expand Down
31 changes: 28 additions & 3 deletions cJSON.c
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,11 @@ static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
return NULL;
}

if (cannot_access_at_index(buffer, 0))
{
return buffer;
}

while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
{
buffer->offset++;
Expand Down Expand Up @@ -992,8 +997,23 @@ static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
return buffer;
}

/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{
size_t buffer_length;

if (NULL == value)
{
return NULL;
}

/* Adding null character size due to require_null_terminated. */
buffer_length = strlen(value) + sizeof("");

return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
}

/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
{
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
cJSON *item = NULL;
Expand All @@ -1002,13 +1022,13 @@ CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return
global_error.json = NULL;
global_error.position = 0;

if (value == NULL)
if (value == NULL || 0 == buffer_length)
{
goto fail;
}

buffer.content = (const unsigned char*)value;
buffer.length = strlen(value) + sizeof("");
buffer.length = buffer_length;
buffer.offset = 0;
buffer.hooks = global_hooks;

Expand Down Expand Up @@ -1078,6 +1098,11 @@ CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
return cJSON_ParseWithOpts(value, 0, 0);
}

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)
Expand Down
2 changes: 2 additions & 0 deletions cJSON.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);

/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
Expand Down
57 changes: 57 additions & 0 deletions tests/parse_examples.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,61 @@ static void test12_should_not_be_parsed(void)
}
}

static void test13_should_be_parsed_without_null_termination(void)
{
cJSON *tree = NULL;
const char test_13[] = "{" \
"\"Image\":{" \
"\"Width\":800," \
"\"Height\":600," \
"\"Title\":\"Viewfrom15thFloor\"," \
"\"Thumbnail\":{" \
"\"Url\":\"http:/*www.example.com/image/481989943\"," \
"\"Height\":125," \
"\"Width\":\"100\"" \
"}," \
"\"IDs\":[116,943,234,38793]" \
"}" \
"}";

char test_13_wo_null[sizeof(test_13) - 1];
memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1);

tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1);
TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json.");

if (tree != NULL)
{
cJSON_Delete(tree);
}
}

static void test14_should_not_be_parsed(void)
{
cJSON *tree = NULL;
const char test_14[] = "{" \
"\"Image\":{" \
"\"Width\":800," \
"\"Height\":600," \
"\"Title\":\"Viewfrom15thFloor\"," \
"\"Thumbnail\":{" \
"\"Url\":\"http:/*www.example.com/image/481989943\"," \
"\"Height\":125," \
"\"Width\":\"100\"" \
"}," \
"\"IDs\":[116,943,234,38793]" \
"}" \
"}";

tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2);
TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached.");

if (tree != NULL)
{
cJSON_Delete(tree);
}
}

int main(void)
{
UNITY_BEGIN();
Expand All @@ -210,5 +265,7 @@ int main(void)
RUN_TEST(file_test10_should_be_parsed_and_printed);
RUN_TEST(file_test11_should_be_parsed_and_printed);
RUN_TEST(test12_should_not_be_parsed);
RUN_TEST(test13_should_be_parsed_without_null_termination);
RUN_TEST(test14_should_not_be_parsed);
return UNITY_END();
}

0 comments on commit 2371b7b

Please sign in to comment.