Browse Source

Added parse_json2() and parse_json_file()

Sergey Lyubka 11 years ago
parent
commit
4feebd6893
4 changed files with 116 additions and 16 deletions
  1. 21 8
      README.md
  2. 64 8
      frozen.c
  3. 3 0
      frozen.h
  4. 28 0
      unit_test.c

+ 21 - 8
README.md

@@ -4,10 +4,9 @@ JSON parser and generator for C/C++
 # Features
 
    * Portable to any environment
-   * Simple API: 2 functions for parsing and 4 for generation
+   * Simple, easy to understand API
    * Very small footprint
    * No dependencies
-   * Makes no memory allocations
    * Code is strict ISO C and strict ISO C++ at the same time
    * Supports superset of JSON: allows non-quoted identifiers as object keys
    * Complete 100% test coverage
@@ -17,7 +16,8 @@ JSON parser and generator for C/C++
    1. Copy `frozen.c` and `frozen.h` to your project
    2. Add `frozen.c` to the list of source files
    3. Parsing with Frozen is done in two steps: first, split JSON string
-      into tokens by `parse_json()`. Second, search for certain
+      into tokens by `parse_json()`, `parse_json2()` or `parse_json_file()`.
+      Second, search for certain
       parameters in parsed string by `find_json_token()`. Below is an example,
       error handling is omitted for clarity:
 
@@ -27,15 +27,18 @@ JSON parser and generator for C/C++
 
         int main(void) {
           static const char *json = " { foo: 1, bar: 2 } ";
-          struct json_token arr[10], *tok;
+          struct json_token *arr, *tok;
 
           // Tokenize json string, fill in tokens array
-          parse_json(json, strlen(json), arr, sizeof(arr) / sizeof(arr[0]));
+          arr = parse_json2(json, strlen(json));
 
           // Search for parameter "bar" and print it's value
           tok = find_json_token(arr, "bar");
           printf("Value of bar is: [%.*s]\n", tok->len, tok->ptr);
 
+          // Do not forget to free allocated tokens array
+          free(arr);
+
           return 0;
         }
 
@@ -43,10 +46,17 @@ JSON parser and generator for C/C++
 
     int parse_json(const char *json_string, int json_string_length,
                    struct json_token *tokens_array, int size_of_tokens_array);
+    struct json_token *parse_json2(const char *json_string, int string_length);
+    struct json_token *parse_json_file(const char *path);
+
+`parse_json()` and `parse_json2()` parse a string, `parse_json_file()` parses
+file. `parse_json()` needs pre-allocated tokens array or NULL, whereas
+`parse_json2()` and `parse_json_file()` allocate tokens array automatically.
 
-Tokenizes `json_string` of length `json_string_length`.
-If `tokens_array` is not `NULL`, then
-all `parse_json` will store tokens in the `tokens_array`. Token with type
+
+`parse_json()` tokenizes `json_string` of length `json_string_length`.
+If `tokens_array` is not `NULL`, then `parse_json()` will store tokens
+in the `tokens_array`. Token with type
 `JSON_TYPE_EOF` marks the end of parsed tokens. JSON token is defined as:
 
     struct json_token {
@@ -79,6 +89,9 @@ returned, one of:
     #define JSON_STRING_INCOMPLETE        -2
     #define JSON_TOKEN_ARRAY_TOO_SMALL    -3
 
+`parse_json2()` and `parse_json_file()` return NULL on error
+and non-NULL on success.
+
 Below is an illustration on how JSON string gets tokenized:
 
        JSON string:      {  "key_1" : "value_1",  "key_2": [ 12345, null  ]   }

+ 64 - 8
frozen.c

@@ -16,18 +16,29 @@
 // license, as set out in <http://cesanta.com/products.html>.
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include "frozen.h"
 
 #ifdef _WIN32
 #define snprintf _snprintf
 #endif
 
+#ifndef FROZEN_REALLOC
+#define FROZEN_REALLOC realloc
+#endif
+
+#ifndef FROZEN_FREE
+#define FROZEN_FREE free
+#endif
+
 struct frozen {
   const char *end;
   const char *cur;
   struct json_token *tokens;
   int max_tokens;
   int num_tokens;
+  int do_realloc;
 };
 
 static int parse_object(struct frozen *f);
@@ -87,7 +98,14 @@ static int get_escape_len(const char *s, int len) {
 }
 
 static int capture_ptr(struct frozen *f, const char *ptr, enum json_type type) {
-  if (f->tokens == 0 || f->max_tokens == 0) return 0;
+  if (f->do_realloc && f->num_tokens >= f->max_tokens) {
+    int new_size = f->max_tokens == 0 ? 100 : f->max_tokens * 2;
+    void *p = FROZEN_REALLOC(f->tokens, new_size * sizeof(f->tokens[0]));
+    if (p == NULL) return JSON_TOKEN_ARRAY_TOO_SMALL;
+    f->max_tokens = new_size;
+    f->tokens = (struct json_token *) p;
+  }
+  if (f->tokens == NULL || f->max_tokens == 0) return 0;
   if (f->num_tokens >= f->max_tokens) return JSON_TOKEN_ARRAY_TOO_SMALL;
   f->tokens[f->num_tokens].ptr = ptr;
   f->tokens[f->num_tokens].type = type;
@@ -263,18 +281,56 @@ static int parse_object(struct frozen *f) {
   return 0;
 }
 
+static int doit(struct frozen *f) {
+  if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID;
+  if (f->end == f->cur) return JSON_STRING_INCOMPLETE;
+  TRY(parse_object(f));
+  TRY(capture_ptr(f, f->cur, JSON_TYPE_EOF));
+  capture_len(f, f->num_tokens, f->cur);
+  return 0;
+}
+
 // json = object
 int parse_json(const char *s, int s_len, struct json_token *arr, int arr_len) {
-  struct frozen frozen = { s + s_len, s, arr, arr_len, 0 };
-  if (s == 0 || s_len < 0) return JSON_STRING_INVALID;
-  if (s_len == 0) return JSON_STRING_INCOMPLETE;
-  TRY(parse_object(&frozen));
-  TRY(capture_ptr(&frozen, frozen.cur, JSON_TYPE_EOF));
-  capture_len(&frozen, frozen.num_tokens, frozen.cur);
-
+  struct frozen frozen = { s + s_len, s, arr, arr_len, 0, 0 };
+  TRY(doit(&frozen));
   return frozen.cur - s;
 }
 
+struct json_token *parse_json2(const char *s, int s_len) {
+  struct frozen frozen = { s + s_len, s, NULL, 0, 0, 1 };
+  if (doit(&frozen) < 0) {
+    FROZEN_FREE((void *) frozen.tokens);
+    frozen.tokens = NULL;
+  }
+  return frozen.tokens;
+}
+
+struct json_token *parse_json_file(const char *path) {
+  FILE *fp;
+  char *file_data = NULL, buf[BUFSIZ];
+  int n, buf_size = 0, file_size = 0;
+  struct json_token *result;
+
+  if ((fp = fopen(path, "rb")) == NULL) return NULL;
+  while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
+    if (buf_size - file_size < n) {
+      int new_size = buf_size == 0 ? sizeof(buf) : buf_size * 2;
+      void *p = FROZEN_REALLOC(file_data, new_size);
+      if (p == NULL) { fclose(fp); return NULL; }
+      file_data = (char *) p;
+      buf_size = new_size;
+    }
+    memcpy(file_data + file_size, buf, n);
+    file_size += n;
+  }
+  fclose(fp);
+
+  result = parse_json2(file_data, file_size);
+  FROZEN_FREE((char *) file_data);
+  return result;
+}
+
 static int path_part_len(const char *p) {
   int i = 0;
   while (p[i] != '\0' && p[i] != '[' && p[i] != '.') i++;

+ 3 - 0
frozen.h

@@ -48,6 +48,9 @@ struct json_token {
 int parse_json(const char *json_string, int json_string_length,
                struct json_token *tokens_array, int size_of_tokens_array);
 
+struct json_token *parse_json2(const char *json_string, int string_length);
+struct json_token *parse_json_file(const char *path);
+
 const struct json_token *find_json_token(const struct json_token *toks,
                                          const char *path);
 

+ 28 - 0
unit_test.c

@@ -241,6 +241,32 @@ static const char *test_nested(void) {
   return NULL;
 }
 
+static const char *test_realloc(void) {
+  struct json_token *p;
+  ASSERT(parse_json2("{ foo: 2 }", 2) == NULL);
+  ASSERT((p = parse_json2("{ foo: 2 }", 10)) != NULL);
+  free(p);
+  return NULL;
+}
+
+static const char *test_file(void) {
+  static const char *test_file_name = "test.json";
+  struct json_token *p;
+  FILE *fp;
+
+  ASSERT((p = parse_json_file(test_file_name)) == NULL);
+
+  ASSERT((fp = fopen(test_file_name, "w+")) != NULL);
+  fprintf(fp, "%s\n", "{ foo: 1, bar: [1, 2, 3] }");
+  fclose(fp);
+
+  ASSERT((p = parse_json_file(test_file_name)) != NULL);
+  free(p);
+  remove(test_file_name);
+
+  return NULL;
+}
+
 static const char *run_all_tests(void) {
   RUN_TEST(test_errors);
   RUN_TEST(test_config);
@@ -248,6 +274,8 @@ static const char *run_all_tests(void) {
   RUN_TEST(test_emit_escapes);
   RUN_TEST(test_emit_overflow);
   RUN_TEST(test_nested);
+  RUN_TEST(test_realloc);
+  RUN_TEST(test_file);
   return NULL;
 }