فهرست منبع

Add callback API to frozen

PUBLISHED_FROM=72720aa2cf85d1cdbfdd2d634d646f3e930de22d
Sergey Lyubka 9 سال پیش
والد
کامیت
e6c91d223b
3فایلهای تغییر یافته به همراه151 افزوده شده و 34 حذف شده
  1. 122 34
      frozen.c
  2. 11 0
      frozen.h
  3. 18 0
      unit_test.c

+ 122 - 34
frozen.c

@@ -65,6 +65,10 @@ typedef unsigned _int64 uint64_t;
 #define FROZEN_FREE free
 #define FROZEN_FREE free
 #endif
 #endif
 
 
+#ifndef JSON_MAX_PATH_LEN
+#define JSON_MAX_PATH_LEN 60
+#endif
+
 struct frozen {
 struct frozen {
   const char *end;
   const char *end;
   const char *cur;
   const char *cur;
@@ -72,8 +76,48 @@ struct frozen {
   int max_tokens;
   int max_tokens;
   int num_tokens;
   int num_tokens;
   int do_realloc;
   int do_realloc;
+
+  /* For callback API */
+  char path[JSON_MAX_PATH_LEN];
+  int path_len;
+  void *callback_data;
+  json_parse_callback_t callback;
+};
+
+struct fstate {
+  const char *ptr;
+  enum json_type type;
+  int path_len;
 };
 };
 
 
+#define SET_STATE(fr, ptr, type, str, len)                \
+  struct fstate fstate = {(ptr), (type), (fr)->path_len}; \
+  append_to_path((fr), (str), (len));
+
+#define CALL_BACK(fr)                                                       \
+  do {                                                                      \
+    struct json_token __t = {                                               \
+        fstate.ptr, (fr)->cur - (const char *) fstate.ptr, 0, fstate.type}; \
+    truncate_path((fr), fstate.path_len);                                   \
+    if ((fr)->callback &&                                                   \
+        ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.'))     \
+      (fr)->callback((fr)->callback_data, (fr)->path, &__t);                \
+  } while (0)
+
+static int append_to_path(struct frozen *f, const char *str, int size) {
+  int n = f->path_len;
+  f->path_len +=
+      snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len + 1),
+               "%.*s", size, str);
+
+  return n;
+}
+
+static void truncate_path(struct frozen *f, int len) {
+  f->path_len = len;
+  f->path[len] = '\0';
+}
+
 static int parse_object(struct frozen *f);
 static int parse_object(struct frozen *f);
 static int parse_value(struct frozen *f);
 static int parse_value(struct frozen *f);
 
 
@@ -183,12 +227,16 @@ static int capture_len(struct frozen *f, int token_index, const char *ptr) {
 /* identifier = letter { letter | digit | '_' } */
 /* identifier = letter { letter | digit | '_' } */
 static int parse_identifier(struct frozen *f) {
 static int parse_identifier(struct frozen *f) {
   EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID);
   EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID);
-  TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING));
-  while (f->cur < f->end &&
-         (*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) {
-    f->cur++;
+  {
+    SET_STATE(f, f->cur, JSON_TYPE_STRING, "", 0);
+    TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING));
+    while (f->cur < f->end &&
+           (*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) {
+      f->cur++;
+    }
+    capture_len(f, f->num_tokens - 1, f->cur);
+    CALL_BACK(f);
   }
   }
-  capture_len(f, f->num_tokens - 1, f->cur);
   return 0;
   return 0;
 }
 }
 
 
@@ -208,20 +256,24 @@ static int get_utf8_char_len(unsigned char ch) {
 static int parse_string(struct frozen *f) {
 static int parse_string(struct frozen *f) {
   int n, ch = 0, len = 0;
   int n, ch = 0, len = 0;
   TRY(test_and_skip(f, '"'));
   TRY(test_and_skip(f, '"'));
-  TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING));
-  for (; f->cur < f->end; f->cur += len) {
-    ch = *(unsigned char *) f->cur;
-    len = get_utf8_char_len((unsigned char) ch);
-    EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
-    EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
-    if (ch == '\\') {
-      EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
-      len += n;
-    } else if (ch == '"') {
-      capture_len(f, f->num_tokens - 1, f->cur);
-      f->cur++;
-      break;
-    };
+  {
+    SET_STATE(f, f->cur, JSON_TYPE_STRING, "", 0);
+    TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING));
+    for (; f->cur < f->end; f->cur += len) {
+      ch = *(unsigned char *) f->cur;
+      len = get_utf8_char_len((unsigned char) ch);
+      EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
+      EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
+      if (ch == '\\') {
+        EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
+        len += n;
+      } else if (ch == '"') {
+        capture_len(f, f->num_tokens - 1, f->cur);
+        CALL_BACK(f);
+        f->cur++;
+        break;
+      };
+    }
   }
   }
   return ch == '"' ? 0 : JSON_STRING_INCOMPLETE;
   return ch == '"' ? 0 : JSON_STRING_INCOMPLETE;
 }
 }
@@ -229,6 +281,7 @@ static int parse_string(struct frozen *f) {
 /* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */
 /* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */
 static int parse_number(struct frozen *f) {
 static int parse_number(struct frozen *f) {
   int ch = cur(f);
   int ch = cur(f);
+  SET_STATE(f, f->cur, JSON_TYPE_NUMBER, "", 0);
   TRY(capture_ptr(f, f->cur, JSON_TYPE_NUMBER));
   TRY(capture_ptr(f, f->cur, JSON_TYPE_NUMBER));
   if (ch == '-') f->cur++;
   if (ch == '-') f->cur++;
   EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);
   EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE);
@@ -249,6 +302,7 @@ static int parse_number(struct frozen *f) {
     while (f->cur < f->end && is_digit(f->cur[0])) f->cur++;
     while (f->cur < f->end && is_digit(f->cur[0])) f->cur++;
   }
   }
   capture_len(f, f->num_tokens - 1, f->cur);
   capture_len(f, f->num_tokens - 1, f->cur);
+  CALL_BACK(f);
   return 0;
   return 0;
 }
 }
 
 
@@ -256,14 +310,18 @@ static int parse_number(struct frozen *f) {
 static int parse_array(struct frozen *f) {
 static int parse_array(struct frozen *f) {
   int ind;
   int ind;
   TRY(test_and_skip(f, '['));
   TRY(test_and_skip(f, '['));
-  TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_ARRAY));
-  ind = f->num_tokens - 1;
-  while (cur(f) != ']') {
-    TRY(parse_value(f));
-    if (cur(f) == ',') f->cur++;
+  {
+    SET_STATE(f, f->cur - 1, JSON_TYPE_ARRAY, "", 0);
+    TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_ARRAY));
+    ind = f->num_tokens - 1;
+    while (cur(f) != ']') {
+      TRY(parse_value(f));
+      if (cur(f) == ',') f->cur++;
+    }
+    TRY(test_and_skip(f, ']'));
+    capture_len(f, ind, f->cur);
+    CALL_BACK(f);
   }
   }
-  TRY(test_and_skip(f, ']'));
-  capture_len(f, ind, f->cur);
   return 0;
   return 0;
 }
 }
 
 
@@ -276,6 +334,7 @@ static int compare(const char *s, const char *str, int len) {
 static int expect(struct frozen *f, const char *s, int len, enum json_type t) {
 static int expect(struct frozen *f, const char *s, int len, enum json_type t) {
   int i, n = left(f);
   int i, n = left(f);
 
 
+  SET_STATE(f, f->cur, t, "", 0);
   TRY(capture_ptr(f, f->cur, t));
   TRY(capture_ptr(f, f->cur, t));
   for (i = 0; i < len; i++) {
   for (i = 0; i < len; i++) {
     if (i >= n) return JSON_STRING_INCOMPLETE;
     if (i >= n) return JSON_STRING_INCOMPLETE;
@@ -283,6 +342,7 @@ static int expect(struct frozen *f, const char *s, int len, enum json_type t) {
   }
   }
   f->cur += len;
   f->cur += len;
   TRY(capture_len(f, f->num_tokens - 1, f->cur));
   TRY(capture_len(f, f->num_tokens - 1, f->cur));
+  CALL_BACK(f);
 
 
   return 0;
   return 0;
 }
 }
@@ -348,9 +408,17 @@ static int parse_key(struct frozen *f) {
 
 
 /* pair = key ':' value */
 /* pair = key ':' value */
 static int parse_pair(struct frozen *f) {
 static int parse_pair(struct frozen *f) {
+  int current_path_len;
+  const char *tok;
+  skip_whitespaces(f);
+  tok = f->cur;
   TRY(parse_key(f));
   TRY(parse_key(f));
+  current_path_len =
+      append_to_path(f, *tok == '"' ? tok + 1 : tok,
+                     *tok == '"' ? f->cur - tok - 2 : f->cur - tok);
   TRY(test_and_skip(f, ':'));
   TRY(test_and_skip(f, ':'));
   TRY(parse_value(f));
   TRY(parse_value(f));
+  truncate_path(f, current_path_len);
   return 0;
   return 0;
 }
 }
 
 
@@ -358,14 +426,18 @@ static int parse_pair(struct frozen *f) {
 static int parse_object(struct frozen *f) {
 static int parse_object(struct frozen *f) {
   int ind;
   int ind;
   TRY(test_and_skip(f, '{'));
   TRY(test_and_skip(f, '{'));
-  TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_OBJECT));
-  ind = f->num_tokens - 1;
-  while (cur(f) != '}') {
-    TRY(parse_pair(f));
-    if (cur(f) == ',') f->cur++;
+  {
+    SET_STATE(f, f->cur - 1, JSON_TYPE_OBJECT, ".", 1);
+    TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_OBJECT));
+    ind = f->num_tokens - 1;
+    while (cur(f) != '}') {
+      TRY(parse_pair(f));
+      if (cur(f) == ',') f->cur++;
+    }
+    TRY(test_and_skip(f, '}'));
+    capture_len(f, ind, f->cur);
+    CALL_BACK(f);
   }
   }
-  TRY(test_and_skip(f, '}'));
-  capture_len(f, ind, f->cur);
   return 0;
   return 0;
 }
 }
 
 
@@ -558,7 +630,8 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
         /*
         /*
          * we delegate printing to the system printf.
          * we delegate printing to the system printf.
          * The goal here is to delegate all modifiers parsing to the system
          * The goal here is to delegate all modifiers parsing to the system
-         * printf, as you can see below we still have to parse the format types.
+         * printf, as you can see below we still have to parse the format
+         * types.
          */
          */
         va_copy(sub_ap, ap);
         va_copy(sub_ap, ap);
         vsnprintf(buf, sizeof(buf), fmt2, sub_ap);
         vsnprintf(buf, sizeof(buf), fmt2, sub_ap);
@@ -665,3 +738,18 @@ int cs_win_snprintf(char *str, size_t size, const char *format, ...) {
   return res;
   return res;
 }
 }
 #endif /* _WIN32 */
 #endif /* _WIN32 */
+
+int json_parse(const char *json_string, int json_string_length,
+               json_parse_callback_t callback, void *callback_data) {
+  struct frozen frozen;
+
+  memset(&frozen, 0, sizeof(frozen));
+  frozen.end = json_string + json_string_length;
+  frozen.cur = json_string;
+  frozen.callback_data = callback_data;
+  frozen.callback = callback;
+
+  TRY(doit(&frozen));
+
+  return frozen.cur - json_string;
+}

+ 11 - 0
frozen.h

@@ -56,6 +56,17 @@ int parse_json(const char *json_string, int json_string_length,
 struct json_token *parse_json2(const char *json_string, int string_length);
 struct json_token *parse_json2(const char *json_string, int string_length);
 struct json_token *find_json_token(struct json_token *toks, const char *path);
 struct json_token *find_json_token(struct json_token *toks, const char *path);
 
 
+/* Callback-based API */
+typedef void (*json_parse_callback_t)(void *callback_data, const char *path,
+                                      const struct json_token *token);
+
+/*
+ * Parse `json_string`, invoking `callback` function for each JSON token.
+ * Return number of bytes processed
+ */
+int json_parse(const char *json_string, int json_string_length,
+               json_parse_callback_t callback, void *callback_data);
+
 /*
 /*
  * JSON generation API.
  * JSON generation API.
  * struct json_out abstracts output, allowing alternative printing plugins.
  * struct json_out abstracts output, allowing alternative printing plugins.

+ 18 - 0
unit_test.c

@@ -363,6 +363,23 @@ static const char *test_system() {
   return NULL;
   return NULL;
 }
 }
 
 
+static void cb(void *data, const char *path, const struct json_token *token) {
+  char *buf = (char *) data;
+  sprintf(buf + strlen(buf), "%d->%s[%.*s] ", token->type, path, token->len,
+          token->ptr);
+}
+
+static const char *test_callback_api() {
+  const char *s = "{\"c\":[{\"a\":9,\"b\":\"x\"}]}";
+  const char *result =
+      "2->.c.a[9] 1->.c.b[x] 3->.c[{\"a\":9,\"b\":\"x\"}] "
+      "7->.c[[{\"a\":9,\"b\":\"x\"}]] 3->[{\"c\":[{\"a\":9,\"b\":\"x\"}]}] ";
+  char buf[200] = "";
+  ASSERT(json_parse(s, strlen(s), cb, buf) == (int) strlen(s));
+  ASSERT(strcmp(buf, result) == 0);
+  return NULL;
+}
+
 static const char *run_all_tests(void) {
 static const char *run_all_tests(void) {
   RUN_TEST(test_errors);
   RUN_TEST(test_errors);
   RUN_TEST(test_config);
   RUN_TEST(test_config);
@@ -370,6 +387,7 @@ static const char *run_all_tests(void) {
   RUN_TEST(test_realloc);
   RUN_TEST(test_realloc);
   RUN_TEST(test_json_printf);
   RUN_TEST(test_json_printf);
   RUN_TEST(test_system);
   RUN_TEST(test_system);
+  RUN_TEST(test_callback_api);
   return NULL;
   return NULL;
 }
 }