]> granicus.if.org Git - jq/commitdiff
Support rendering a JSON value to a string buffer.
authorStephen Dolan <mu@netsoc.tcd.ie>
Mon, 17 Sep 2012 19:08:55 +0000 (20:08 +0100)
committerStephen Dolan <mu@netsoc.tcd.ie>
Mon, 17 Sep 2012 19:11:12 +0000 (20:11 +0100)
c/jv.c
c/jv.h
c/jv_print.c
c/main.c

diff --git a/c/jv.c b/c/jv.c
index 595484e74ad488dd35461875846b58199306e29f..51070636f50538efe9cd8d5968d25b5edae85899 100644 (file)
--- a/c/jv.c
+++ b/c/jv.c
@@ -544,7 +544,6 @@ jv jv_string_append_str(jv a, const char* str) {
   return jv_string_append_buf(a, str, strlen(str));
 }
                         
-                        
 jv jv_string_fmt(const char* fmt, ...) {
   int size = 1024;
   while (1) {
diff --git a/c/jv.h b/c/jv.h
index 295369e83dd76df173a2f66a3a34639d2c88cea1..e67d614a1acd8bde6942f76d67807fa442f11ecd 100644 (file)
--- a/c/jv.h
+++ b/c/jv.h
@@ -105,7 +105,8 @@ jv jv_object_iter_value(jv, int);
 int jv_get_refcnt(jv);
 
 enum { JV_PRINT_PRETTY = 1, JV_PRINT_ASCII = 2 };
-void jv_dump(jv, int);
+void jv_dump(jv, int flags);
+jv jv_dump_string(jv, int flags);
 
 jv jv_parse(const char* string);
 jv jv_parse_sized(const char* string, int length);
index f1ec92fb3b126b6754edaa53d8e6c079dc679ab5..64bf178acc8a5c284d622feba5d474d7b970bfbd 100644 (file)
@@ -1,47 +1,70 @@
 #include "jv.h"
 #include <stdio.h>
 #include <float.h>
-#include <math.h>
+#include <string.h>
 
 #include "jv_dtoa.h"
 #include "jv_unicode.h"
 
-static void jv_dump_string(jv str, int ascii_only) {
+static void put_buf(const char* s, int len, FILE* fout, jv* strout) {
+  if (strout) {
+    *strout = jv_string_append_buf(*strout, s, len);
+  } else {
+    fwrite(s, 1, len, fout);
+  }
+}
+
+static void put_char(char c, FILE* fout, jv* strout) {
+  put_buf(&c, 1, fout, strout);
+}
+
+static void put_str(const char* s, FILE* fout, jv* strout) {
+  put_buf(s, strlen(s), fout, strout);
+}
+
+static void put_space(int n, FILE* fout, jv* strout) {
+  while (n--) {
+    put_char(' ', fout, strout);
+  }
+}
+
+static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S) {
   assert(jv_get_kind(str) == JV_KIND_STRING);
   const char* i = jv_string_value(str);
   const char* end = i + jv_string_length(jv_copy(str));
   int c = 0;
+  char buf[32];
   while ((i = jvp_utf8_next(i, end, &c))) {
     assert(c != -1);
     int unicode_escape = 0;
     if (0x20 <= c && c <= 0x7E) {
       // printable ASCII
       if (c == '"' || c == '\\') {
-        putchar('\\');
+        put_char('\\', F, S);
       }
-      putchar(c);
+      put_char(c, F, S);
     } else if (c < 0x20 || c == 0x7F) {
       // ASCII control character
       switch (c) {
       case '\b':
-        putchar('\\');
-        putchar('b');
+        put_char('\\', F, S);
+        put_char('b', F, S);
         break;
       case '\t':
-        putchar('\\');
-        putchar('t');
+        put_char('\\', F, S);
+        put_char('t', F, S);
         break;
       case '\r':
-        putchar('\\');
-        putchar('r');
+        put_char('\\', F, S);
+        put_char('r', F, S);
         break;
       case '\n':
-        putchar('\\');
-        putchar('n');
+        put_char('\\', F, S);
+        put_char('n', F, S);
         break;
       case '\f':
-        putchar('\\');
-        putchar('f');
+        put_char('\\', F, S);
+        put_char('f', F, S);
         break;
       default:
         unicode_escape = 1;
@@ -52,13 +75,14 @@ static void jv_dump_string(jv str, int ascii_only) {
     }
     if (unicode_escape) {
       if (c <= 0xffff) {
-        printf("\\u%04x", c);
+        sprintf(buf, "\\u%04x", c);
       } else {
         c -= 0x10000;
-        printf("\\u%04x\\u%04x", 
-               0xD800 | ((c & 0xffc00) >> 10),
-               0xDC00 | (c & 0x003ff));
+        sprintf(buf, "\\u%04x\\u%04x", 
+                0xD800 | ((c & 0xffc00) >> 10),
+                0xDC00 | (c & 0x003ff));
       }
+      put_str(buf, F, S);
     }
   }
   assert(c != -1);
@@ -66,78 +90,97 @@ static void jv_dump_string(jv str, int ascii_only) {
 
 enum { INDENT = 2 };
 
-static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent) {
+static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FILE* F, jv* S) {
   char buf[JVP_DTOA_FMT_MAX_LEN];
   switch (jv_get_kind(x)) {
   case JV_KIND_INVALID:
     assert(0 && "Invalid value");
     break;
   case JV_KIND_NULL:
-    printf("null");
+    put_str("null", F, S);
     break;
   case JV_KIND_FALSE:
-    printf("false");
+    put_str("false", F, S);
     break;
   case JV_KIND_TRUE:
-    printf("true");
+    put_str("true", F, S);
     break;
   case JV_KIND_NUMBER: {
     double d = jv_number_value(x);
     if (d != d) {
       // JSON doesn't have NaN, so we'll render it as "null"
-      printf("null");
+      put_str("null", F, S);
     } else {
       // Normalise infinities to something we can print in valid JSON
       if (d > DBL_MAX) d = DBL_MAX;
       if (d < -DBL_MAX) d = -DBL_MAX;
-      printf("%s", jvp_dtoa_fmt(C, buf, d));
+      put_str(jvp_dtoa_fmt(C, buf, d), F, S);
     }
     break;
   }
   case JV_KIND_STRING:
-    // FIXME: all sorts of broken
-    putchar('"');
-    jv_dump_string(x, 0);
-    putchar('"');
+    put_char('"', F, S);
+    jvp_dump_string(x, 0, F, S);
+    put_char('"', F, S);
     break;
   case JV_KIND_ARRAY: {
     if (jv_array_length(jv_copy(x)) == 0) {
-      printf("[]");
+      put_str("[]", F, S);
       break;
     }
-    printf("[");
-    if (flags & JV_PRINT_PRETTY) printf("\n%*s", indent+INDENT, "");
+    put_str("[", F, S);
+    if (flags & JV_PRINT_PRETTY) {
+      put_char('\n', F, S);
+      put_space(indent+INDENT, F, S);
+    }
     for (int i=0; i<jv_array_length(jv_copy(x)); i++) {
       if (i!=0) {
-        if (flags & JV_PRINT_PRETTY) printf(",\n%*s", indent+INDENT, "");
-        else printf(", ");
+        if (flags & JV_PRINT_PRETTY) {
+          put_str(",\n", F, S);
+          put_space(indent+INDENT, F, S);
+        } else {
+          put_str(", ", F, S);
+        }
       }
-      jv_dump_term(C, jv_array_get(jv_copy(x), i), flags, indent + INDENT);
+      jv_dump_term(C, jv_array_get(jv_copy(x), i), flags, indent + INDENT, F, S);
+    }
+    if (flags & JV_PRINT_PRETTY) {
+      put_char('\n', F, S);
+      put_space(indent, F, S);
     }
-    if (flags & JV_PRINT_PRETTY) printf("\n%*s", indent, "");
-    printf("]");
+    put_char(']', F, S);
     break;
   }
   case JV_KIND_OBJECT: {
     if (jv_object_length(jv_copy(x)) == 0) {
-      printf("{}");
+      put_str("{}", F, S);
       break;
     }
-    printf("{");
-    if (flags & JV_PRINT_PRETTY) printf("\n%*s", indent+INDENT, "");
+    put_char('{', F, S);
+    if (flags & JV_PRINT_PRETTY) {
+      put_char('\n', F, S);
+      put_space(indent+INDENT, F, S);
+    }
     int first = 1;
     jv_object_foreach(i, x) {
       if (!first) {
-        if (flags & JV_PRINT_PRETTY) printf(",\n%*s", indent+INDENT, "");
-        else printf(", ");
+        if (flags & JV_PRINT_PRETTY){
+          put_str(",\n", F, S);
+          put_space(indent+INDENT, F, S);
+        } else {
+          put_str(", ", F, S);
+        }
       }
       first = 0;
-      jv_dump_term(C, jv_object_iter_key(x, i), flags, indent + INDENT);
-      printf(": ");
-      jv_dump_term(C, jv_object_iter_value(x, i), flags, indent + INDENT);
+      jv_dump_term(C, jv_object_iter_key(x, i), flags, indent + INDENT, F, S);
+      put_str(": ", F, S);
+      jv_dump_term(C, jv_object_iter_value(x, i), flags, indent + INDENT, F, S);
     }
-    if (flags & JV_PRINT_PRETTY) printf("\n%*s", indent, "");
-    printf("}");
+    if (flags & JV_PRINT_PRETTY) {
+      put_char('\n', F, S);
+      put_space(indent, F, S);
+    }
+    put_char('}', F, S);
   }
   }
   jv_free(x);
@@ -146,6 +189,15 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent) {
 void jv_dump(jv x, int flags) {
   struct dtoa_context C;
   jvp_dtoa_context_init(&C);
-  jv_dump_term(&C, x, flags, 0);
+  jv_dump_term(&C, x, flags, 0, stdout, 0);
+  jvp_dtoa_context_free(&C);
+}
+
+jv jv_dump_string(jv x, int flags) {
+  struct dtoa_context C;
+  jvp_dtoa_context_init(&C);
+  jv s = jv_string("");
+  jv_dump_term(&C, x, flags, 0, 0, &s);
   jvp_dtoa_context_free(&C);
+  return s;
 }
index f87a51128a2622e419937d571c084880041cf38b..5f19cf86df04cc659b4d79d8418ae2d48c7ddeca 100644 (file)
--- a/c/main.c
+++ b/c/main.c
@@ -77,6 +77,11 @@ void run_tests() {
         printf("\n");
         pass = 0;
       }
+      jv as_string = jv_dump_string(jv_copy(expected), rand());
+      jv reparsed = jv_parse_sized(jv_string_value(as_string), jv_string_length(jv_copy(as_string)));
+      assert(jv_equal(jv_copy(expected), jv_copy(reparsed)));
+      jv_free(as_string);
+      jv_free(reparsed);
       jv_free(expected);
       jv_free(actual);
     }