Explorar o código

Added generation API

Sergey Lyubka %!s(int64=11) %!d(string=hai) anos
pai
achega
ed3ae6e78f
Modificáronse 4 ficheiros con 113 adicións e 4 borrados
  1. 32 4
      README.md
  2. 37 0
      frozen.c
  3. 5 0
      frozen.h
  4. 39 0
      unit_test.c

+ 32 - 4
README.md

@@ -1,4 +1,4 @@
-JSON parser for C/C++
+JSON parser and generator for C/C++
 ===========================================
 
 # Features
@@ -101,12 +101,27 @@ Below is an illustration on how JSON string gets tokenized:
 This is a convenience function to fetch specific values from the parsed
 string. `toks` must be a valid array, successfully populated by `parse_json()`.
 A `path` is a string, an accessor to the desired element of the JSON object,
-as if it was written in Javascript. For example, if parsed JSON string is
-"{ foo : { bar: [1, 2, 3] } }", then path "foo.bar[0]" would return a token
-that points to number "1".  
+as if it was written in Javascript. For example, if parsed JSON string is  
+`"{ foo : { bar: [1, 2, 3] } }"`, then path `"foo.bar[0]"` would return a token
+that points to number `"1"`.  
 Return: pointer to the found token, or NULL on failure.
 
 
+    int json_emit_int(char *buf, int buf_len, long int value);
+    int json_emit_double(char *buf, int buf_len, double value);
+    int json_emit_quoted_str(char *buf, int buf_len, const char *str);
+    int json_emit_raw_str(char *buf, int buf_len, const char *str);
+
+These functions are used to generate JSON string. All of them accept
+a destination buffer and a value to output, and return number of bytes printed.
+Returned value can be bigger then destination buffer size, this is an
+indication of overflow. If there is no overflow, a buffer is guaranteed to
+be nul-terminated. Numbers are printed by `json_emit_double()` and
+`json_emit_int()` functions, strings are printed by `json_emit_quoted_str()`
+function. Values for `null`, `true`, `false`, and characters
+`{`, `}`, `[`, `]`, `,`, `:` are printed by
+`json_emit_raw_str()` function.
+
 ## Example: accessing configuration parameters
 
     #include "frozen.h"
@@ -132,6 +147,19 @@ Return: pointer to the found token, or NULL on failure.
     ASSERT(find_json_token(tokens, "ports[3]") == NULL);  // Outside boundaries
     ASSERT(find_json_token(tokens, "foo.bar") == NULL);   // Nonexistent
 
+## Example: generating JSON string `{"foo":[-123,true]}`
+
+    char buf[1000], *p = buf;
+
+    p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, "{");
+    p += json_emit_quoted_str(p, &buf[sizeof(buf)] - p, "foo");
+    p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ":[");
+    p += json_emit_int(p, &buf[sizeof(buf)] - p, -123);
+    p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ",true]}");
+
+    ASSERT(strcmp(buf, "{\"foo\":[-123,true]}") == 0);
+    ASSERT(p < &buf[sizeof(buf)]);
+
 # License
 
 Frozen is released under

+ 37 - 0
frozen.c

@@ -15,6 +15,7 @@
 // Alternatively, you can license this library under a commercial
 // license, as set out in <http://cesanta.com/products.html>.
 
+#include <stdio.h>
 #include "frozen.h"
 
 struct frozen {
@@ -314,3 +315,39 @@ const struct json_token *find_json_token(const struct json_token *toks,
   }
   return 0;
 }
+
+int json_emit_int(char *buf, int buf_len, long int value) {
+  return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%ld", value);
+}
+
+int json_emit_double(char *buf, int buf_len, double value) {
+  return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%g", value);
+}
+
+int json_emit_quoted_str(char *buf, int buf_len, const char *str) {
+  int i = 0, j = 0, ch;
+
+#define EMIT(x) do { if (j < buf_len) buf[j++] = x; } while (0)
+
+  EMIT('"');
+  while ((ch = str[i++]) != '\0' && j < buf_len) {
+    switch (ch) {
+      case '"':  EMIT('\\'); EMIT('"'); break;
+      case '\\': EMIT('\\'); EMIT('\\'); break;
+      case '\b': EMIT('\\'); EMIT('b'); break;
+      case '\f': EMIT('\\'); EMIT('f'); break;
+      case '\n': EMIT('\\'); EMIT('n'); break;
+      case '\r': EMIT('\\'); EMIT('r'); break;
+      case '\t': EMIT('\\'); EMIT('t'); break;
+      default: EMIT(ch);
+    }
+  }
+  EMIT('"');
+  EMIT(0);
+
+  return j == 0 ? 0 : j - 1;
+}
+
+int json_emit_raw_str(char *buf, int buf_len, const char *str) {
+  return buf_len <= 0 ? 0 : snprintf(buf, buf_len, "%s", str);
+}

+ 5 - 0
frozen.h

@@ -49,6 +49,11 @@ int parse_json(const char *json_string, int json_string_length,
 const struct json_token *find_json_token(const struct json_token *toks,
                                          const char *path);
 
+int json_emit_int(char *buf, int buf_len, long int value);
+int json_emit_double(char *buf, int buf_len, double value);
+int json_emit_quoted_str(char *buf, int buf_len, const char *str);
+int json_emit_raw_str(char *buf, int buf_len, const char *str);
+
 #ifdef __cplusplus
 }
 #endif // __cplusplus

+ 39 - 0
unit_test.c

@@ -179,9 +179,48 @@ static const char *test_config(void) {
   return NULL;
 }
 
+static const char *test_emit_overflow(void) {
+  char buf[1000];
+
+  memset(buf, 0, sizeof(buf));
+  ASSERT(json_emit_raw_str(buf, 0, "hi") == 0);
+  ASSERT(json_emit_quoted_str(buf, 0, "hi") == 0);
+  ASSERT(buf[0] == '\0');
+
+  return NULL;
+}
+
+static const char *test_emit_escapes(void) {
+  char buf[1000];
+  ASSERT(json_emit_quoted_str(buf, sizeof(buf), "\"\\\b\f\n\r\t") > 0);
+  ASSERT(strcmp(buf, "\"\\\"\\\\\\b\\f\\n\\r\\t\"") == 0);
+  return NULL;
+}
+
+static const char *test_emit(void) {
+  char buf[1000], *p = buf;
+
+  p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, "{");
+  p += json_emit_quoted_str(p, &buf[sizeof(buf)] - p, "foo");
+  p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ":[");
+  p += json_emit_int(p, &buf[sizeof(buf)] - p, -123);
+  p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ",");
+  p += json_emit_double(p, &buf[sizeof(buf)] - p, 1.23);
+  p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, ",");
+  p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, "true");
+  p += json_emit_raw_str(p, &buf[sizeof(buf)] - p, "]}");
+  ASSERT(strcmp(buf, "{\"foo\":[-123,1.23,true]}") == 0);
+  ASSERT(p < &buf[sizeof(buf)]);
+
+  return NULL;
+}
+
 static const char *run_all_tests(void) {
   RUN_TEST(test_errors);
   RUN_TEST(test_config);
+  RUN_TEST(test_emit);
+  RUN_TEST(test_emit_escapes);
+  RUN_TEST(test_emit_overflow);
   return NULL;
 }