Merge branch 'issue-29'
authorPetri Lehtinen <petri@digip.org>
Thu, 30 Jun 2011 18:48:09 +0000 (21:48 +0300)
committerPetri Lehtinen <petri@digip.org>
Thu, 30 Jun 2011 18:48:33 +0000 (21:48 +0300)
Fixes GH-29.

doc/apiref.rst
src/dump.c
src/jansson.h
src/strbuffer.c
src/strbuffer.h
test/.gitignore
test/suites/api/Makefile.am
test/suites/api/check-exports
test/suites/api/test_dump_callback.c [new file with mode: 0644]

index 2c57d2e..efafd12 100644 (file)
@@ -765,6 +765,26 @@ is in UTF-8.
    *path* already exists, it is overwritten. *flags* is described
    above. Returns 0 on success and -1 on error.
 
+.. type:: json_dump_callback_t
+
+   A typedef for a function that's called by
+   :func:`json_dump_callback()`::
+
+       typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
+
+   *buffer* points to a buffer containing a chunk of output, *size* is
+   the length of the buffer, and *data* is the corresponding
+   :func:`json_dump_callback()` argument passed through.
+
+   On error, the function should return -1 to stop the encoding
+   process. On success, it should return 0.
+
+.. function:: int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
+
+   Call *callback* repeatedly, passing a chunk of the JSON
+   representation of *root* each time. *flags* is described above.
+   Returns 0 on success and -1 on error.
+
 
 .. _apiref-decoding:
 
index f32f667..344670a 100644 (file)
@@ -19,8 +19,6 @@
 #define MAX_INTEGER_STR_LENGTH  100
 #define MAX_REAL_STR_LENGTH     100
 
-typedef int (*dump_func)(const char *buffer, int size, void *data);
-
 struct string
 {
     char *buffer;
@@ -28,12 +26,12 @@ struct string
     int size;
 };
 
-static int dump_to_strbuffer(const char *buffer, int size, void *data)
+static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
 {
     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
 }
 
-static int dump_to_file(const char *buffer, int size, void *data)
+static int dump_to_file(const char *buffer, size_t size, void *data)
 {
     FILE *dest = (FILE *)data;
     if(fwrite(buffer, size, 1, dest) != 1)
@@ -44,7 +42,7 @@ static int dump_to_file(const char *buffer, int size, void *data)
 /* 32 spaces (the maximum indentation size) */
 static char whitespace[] = "                                ";
 
-static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
+static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
 {
     if(JSON_INDENT(flags) > 0)
     {
@@ -66,7 +64,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
     return 0;
 }
 
-static int dump_string(const char *str, int ascii, dump_func dump, void *data)
+static int dump_string(const char *str, int ascii, json_dump_callback_t dump, void *data)
 {
     const char *pos, *end;
     int32_t codepoint;
@@ -166,7 +164,7 @@ static int object_key_compare_serials(const void *key1, const void *key2)
 }
 
 static int do_dump(const json_t *json, size_t flags, int depth,
-                   dump_func dump, void *data)
+                   json_dump_callback_t dump, void *data)
 {
     int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
 
@@ -463,3 +461,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
     fclose(output);
     return result;
 }
+
+int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
+{
+    if(!(flags & JSON_ENCODE_ANY)) {
+        if(!json_is_array(json) && !json_is_object(json))
+           return -1;
+    }
+
+    return do_dump(json, flags, 0, callback, data);
+}
index 3abb4cf..8275e4f 100644 (file)
@@ -234,10 +234,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
 #define JSON_PRESERVE_ORDER 0x100
 #define JSON_ENCODE_ANY     0x200
 
+typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
+
 char *json_dumps(const json_t *json, size_t flags);
 int json_dumpf(const json_t *json, FILE *output, size_t flags);
 int json_dump_file(const json_t *json, const char *path, size_t flags);
-
+int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
 
 /* custom memory allocation */
 
index 1b20e2b..b9190c2 100644 (file)
@@ -13,6 +13,7 @@
 
 #define STRBUFFER_MIN_SIZE  16
 #define STRBUFFER_FACTOR    2
+#define STRBUFFER_SIZE_MAX  ((size_t)-1)
 
 int strbuffer_init(strbuffer_t *strbuff)
 {
@@ -64,13 +65,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
     return strbuffer_append_bytes(strbuff, &byte, 1);
 }
 
-int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
+int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
 {
-    if(strbuff->length + size >= strbuff->size)
+    if(size >= strbuff->size - strbuff->length)
     {
         size_t new_size;
         char *new_value;
 
+        /* avoid integer overflow */
+        if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
+            || size > STRBUFFER_SIZE_MAX - 1
+            || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
+            return -1;
+
         new_size = max(strbuff->size * STRBUFFER_FACTOR,
                        strbuff->length + size + 1);
 
index b21ef8b..9764251 100644 (file)
@@ -10,8 +10,8 @@
 
 typedef struct {
     char *value;
-    int length;   /* bytes used */
-    int size;     /* bytes allocated */
+    size_t length;   /* bytes used */
+    size_t size;     /* bytes allocated */
 } strbuffer_t;
 
 int strbuffer_init(strbuffer_t *strbuff);
@@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff);
 
 int strbuffer_append(strbuffer_t *strbuff, const char *string);
 int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
-int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
+int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
 
 char strbuffer_pop(strbuffer_t *strbuff);
 
index 278e5f5..7b7203d 100644 (file)
@@ -4,6 +4,7 @@ suites/api/test_array
 suites/api/test_copy
 suites/api/test_cpp
 suites/api/test_dump
+suites/api/test_dump_callback
 suites/api/test_equal
 suites/api/test_load
 suites/api/test_loadb
index 58c31c6..5f96505 100644 (file)
@@ -4,6 +4,7 @@ check_PROGRAMS = \
        test_array \
        test_copy \
        test_dump \
+       test_dump_callback \
        test_equal \
        test_load \
        test_loadb \
@@ -17,6 +18,7 @@ check_PROGRAMS = \
 test_array_SOURCES = test_array.c util.h
 test_copy_SOURCES = test_copy.c util.h
 test_dump_SOURCES = test_dump.c util.h
+test_dump_callback_SOURCES = test_dump_callback.c util.h
 test_load_SOURCES = test_load.c util.h
 test_loadb_SOURCES = test_loadb.c util.h
 test_memory_funcs_SOURCES = test_memory_funcs.c util.h
index 6df5a48..802d683 100755 (executable)
@@ -47,6 +47,7 @@ json_object_iter_set_new
 json_dumps
 json_dumpf
 json_dump_file
+json_dump_callback
 json_loads
 json_loadf
 json_load_file
diff --git a/test/suites/api/test_dump_callback.c b/test/suites/api/test_dump_callback.c
new file mode 100644 (file)
index 0000000..cb71e1b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <jansson.h>
+#include <string.h>
+#include <stdlib.h>
+#include "util.h"
+
+struct my_sink {
+    char *buf;
+    size_t off;
+    size_t cap;
+};
+
+static int my_writer(const char *buffer, size_t len, void *data) {
+    struct my_sink *s = data;
+    if (len > s->cap - s->off) {
+        return -1;
+    }
+    memcpy(s->buf + s->off, buffer, len);
+    s->off += len;
+    return 0;
+}
+
+int main(void)
+{
+    struct my_sink s;
+    json_t *json;
+    const char str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
+    char *dumped_to_string;
+
+    json = json_loads(str, 0, NULL);
+    if(!json) {
+        fail("json_loads failed");
+    }
+
+    dumped_to_string = json_dumps(json, 0);
+    if (!dumped_to_string) {
+        json_decref(json);
+        fail("json_dumps failed");
+    }
+
+    s.off = 0;
+    s.cap = strlen(dumped_to_string);
+    s.buf = malloc(s.cap);
+    if (!s.buf) {
+        json_decref(json);
+        free(dumped_to_string);
+        fail("malloc failed");
+    }
+
+    if (json_dump_callback(json, my_writer, &s, 0) == -1) {
+        json_decref(json);
+        free(dumped_to_string);
+        free(s.buf);
+        fail("json_dump_callback failed on an exact-length sink buffer");
+    }
+
+    if (strncmp(dumped_to_string, s.buf, s.off) != 0) {
+        json_decref(json);
+        free(dumped_to_string);
+        free(s.buf);
+        fail("json_dump_callback and json_dumps did not produce identical output");
+    }
+
+    s.off = 1;
+    if (json_dump_callback(json, my_writer, &s, 0) != -1) {
+        json_decref(json);
+        free(dumped_to_string);
+        free(s.buf);
+        fail("json_dump_callback succeeded on a short buffer when it should have failed");
+    }
+
+    json_decref(json);
+    free(dumped_to_string);
+    free(s.buf);
+    return EXIT_SUCCESS;
+}
+