浏览代码

Added cJSON_ParseWithLength (#358)

Co-authored-by: Caglar Ivriz <caglar.ivriz@siemens.com>
caglarivriz 5 年之前
父节点
当前提交
2371b7bc66
共有 4 个文件被更改,包括 95 次插入3 次删除
  1. 8 0
      README.md
  2. 28 3
      cJSON.c
  3. 2 0
      cJSON.h
  4. 57 0
      tests/parse_examples.c

+ 8 - 0
README.md

@@ -245,6 +245,12 @@ Given some JSON in a zero terminated string, you can parse it with `cJSON_Parse`
 cJSON *json = cJSON_Parse(string);
 ```
 
+Given some JSON in a string (whether zero terminated or not), you can parse it with `cJSON_ParseWithLength`.
+
+```c
+cJSON *json = cJSON_ParseWithLength(string, buffer_length);
+```
+
 It will parse the JSON and allocate a tree of `cJSON` items that represents it. Once it returns, you are fully responsible for deallocating it after use with `cJSON_Delete`.
 
 The allocator used by `cJSON_Parse` is `malloc` and `free` by default but can be changed (globally) with `cJSON_InitHooks`.
@@ -255,6 +261,8 @@ By default, characters in the input string that follow the parsed JSON will not
 If you want more options, use `cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)`.
 `return_parse_end` returns a pointer to the end of the JSON in the input string or the position that an error occurs at (thereby replacing `cJSON_GetErrorPtr` in a thread safe way). `require_null_terminated`, if set to `1` will make it an error if the input string contains data after the JSON.
 
+If you want more options giving buffer length, use `cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)`.
+
 ### Printing JSON
 
 Given a tree of `cJSON` items, you can print them as a string using `cJSON_Print`.

+ 28 - 3
cJSON.c

@@ -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++;
@@ -992,9 +997,24 @@ 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;
 
@@ -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;
 
@@ -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)

+ 2 - 0
cJSON.h

@@ -138,9 +138,11 @@ CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
 /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
 /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
 CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
 /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
 /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
 CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
 
 /* Render a cJSON entity to text for transfer/storage. */
 CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);

+ 57 - 0
tests/parse_examples.c

@@ -195,6 +195,61 @@ static void test12_should_not_be_parsed(void)
     }
 }
 
+static void test13_should_be_parsed_without_null_termination(void)
+{
+    cJSON *tree = NULL;
+    const char test_13[] = "{" \
+                                "\"Image\":{" \
+                                    "\"Width\":800," \
+                                    "\"Height\":600," \
+                                    "\"Title\":\"Viewfrom15thFloor\"," \
+                                    "\"Thumbnail\":{" \
+                                        "\"Url\":\"http:/*www.example.com/image/481989943\"," \
+                                        "\"Height\":125," \
+                                        "\"Width\":\"100\"" \
+                                    "}," \
+                                    "\"IDs\":[116,943,234,38793]" \
+                                "}" \
+                            "}";
+
+    char test_13_wo_null[sizeof(test_13) - 1];
+    memcpy(test_13_wo_null, test_13, sizeof(test_13) - 1);
+
+    tree = cJSON_ParseWithLength(test_13_wo_null, sizeof(test_13) - 1);
+    TEST_ASSERT_NOT_NULL_MESSAGE(tree, "Failed to parse valid json.");
+
+    if (tree != NULL)
+    {
+        cJSON_Delete(tree);
+    }
+}
+
+static void test14_should_not_be_parsed(void)
+{
+    cJSON *tree = NULL;
+    const char test_14[] = "{" \
+                                "\"Image\":{" \
+                                    "\"Width\":800," \
+                                    "\"Height\":600," \
+                                    "\"Title\":\"Viewfrom15thFloor\"," \
+                                    "\"Thumbnail\":{" \
+                                        "\"Url\":\"http:/*www.example.com/image/481989943\"," \
+                                        "\"Height\":125," \
+                                        "\"Width\":\"100\"" \
+                                    "}," \
+                                    "\"IDs\":[116,943,234,38793]" \
+                                "}" \
+                            "}";
+
+    tree = cJSON_ParseWithLength(test_14, sizeof(test_14) - 2);
+    TEST_ASSERT_NULL_MESSAGE(tree, "Should not continue after buffer_length is reached.");
+
+    if (tree != NULL)
+    {
+        cJSON_Delete(tree);
+    }
+}
+
 int main(void)
 {
     UNITY_BEGIN();
@@ -210,5 +265,7 @@ int main(void)
     RUN_TEST(file_test10_should_be_parsed_and_printed);
     RUN_TEST(file_test11_should_be_parsed_and_printed);
     RUN_TEST(test12_should_not_be_parsed);
+    RUN_TEST(test13_should_be_parsed_without_null_termination);
+    RUN_TEST(test14_should_not_be_parsed);
     return UNITY_END();
 }