Merge branch '2.3'
authorPetri Lehtinen <petri@digip.org>
Fri, 20 Apr 2012 18:41:25 +0000 (21:41 +0300)
committerPetri Lehtinen <petri@digip.org>
Fri, 20 Apr 2012 18:41:25 +0000 (21:41 +0300)
Conflicts:
configure.ac
doc/conf.py
src/jansson.h

configure.ac
doc/apiref.rst
doc/conf.py
src/jansson.h
src/load.c
test/.gitignore
test/suites/api/Makefile.am
test/suites/api/check-exports
test/suites/api/test_load_callback.c [new file with mode: 0644]

index 677dbef..753a09b 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ([2.60])
-AC_INIT([jansson], [2.3.1], [petri@digip.org])
+AC_INIT([jansson], [2.4-dev], [petri@digip.org])
 
 AM_INIT_AUTOMAKE([1.10 foreign])
 
index 1c3a85b..0128cc0 100644 (file)
@@ -967,6 +967,34 @@ The following functions perform the actual JSON decoding.
    filled with information about the error. *flags* is described
    above.
 
+.. type:: json_load_callback_t
+
+   A typedef for a function that's called by
+   :func:`json_load_callback()` to read a chunk of input data::
+
+       typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
+
+   *buffer* points to a buffer of *buflen* bytes, and *data* is the
+   corresponding :func:`json_load_callback()` argument passed through.
+
+   On error, the function should return ``(size_t)-1`` to abort the
+   decoding process. When there's no data left, it should return 0 to
+   report that the end of input has been reached.
+
+   .. versionadded:: 2.4
+
+.. function:: json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error)
+
+   .. refcounting:: new
+
+   Decodes the JSON text produced by repeated calls to *callback*, and
+   returns the array or object it contains, or *NULL* on error, in
+   which case *error* is filled with information about the error.
+   *data* is passed through to *callback* on each call. *flags* is
+   described above.
+
+   .. versionadded:: 2.4
+
 
 .. _apiref-pack:
 
index 701fd46..35c5238 100644 (file)
@@ -48,9 +48,9 @@ copyright = u'2009-2012, Petri Lehtinen'
 # built documents.
 #
 # The short X.Y version.
-version = '2.3.1'
+version = '2.4'
 # The full version, including alpha/beta/rc tags.
-release = version
+release = '2.4-dev'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
index cae2432..e22b68f 100644 (file)
@@ -22,10 +22,10 @@ extern "C" {
 
 #define JANSSON_MAJOR_VERSION  2
 #define JANSSON_MINOR_VERSION  3
-#define JANSSON_MICRO_VERSION  1
+#define JANSSON_MICRO_VERSION  99
 
 /* Micro version is omitted if it's 0 */
-#define JANSSON_VERSION  "2.3.1"
+#define JANSSON_VERSION  "2.4-dev"
 
 /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
    for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -232,10 +232,13 @@ json_t *json_deep_copy(json_t *value);
 #define JSON_DISABLE_EOF_CHECK 0x2
 #define JSON_DECODE_ANY        0x4
 
+typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
+
 json_t *json_loads(const char *input, size_t flags, json_error_t *error);
 json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error);
 json_t *json_loadf(FILE *input, size_t flags, json_error_t *error);
 json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
+json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error);
 
 
 /* encoding */
index 9ad4752..245244e 100644 (file)
@@ -990,3 +990,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
     fclose(fp);
     return result;
 }
+
+#define MAX_BUF_LEN 1024
+
+typedef struct
+{
+    char data[MAX_BUF_LEN];
+    size_t len;
+    size_t pos;
+    json_load_callback_t callback;
+    void *arg;
+} callback_data_t;
+
+static int callback_get(void *data)
+{
+    char c;
+    callback_data_t *stream = data;
+
+    if(stream->pos >= stream->len) {
+        stream->pos = 0;
+        stream->len = stream->callback(stream->data, MAX_BUF_LEN, stream->arg);
+        if(stream->len == 0 || stream->len == (size_t)-1)
+            return EOF;
+    }
+
+    c = stream->data[stream->pos];
+    stream->pos++;
+    return (unsigned char)c;
+}
+
+json_t *json_load_callback(json_load_callback_t callback, void *arg, size_t flags, json_error_t *error)
+{
+    lex_t lex;
+    json_t *result;
+
+    callback_data_t stream_data;
+
+    memset(&stream_data, 0, sizeof(stream_data));
+    stream_data.callback = callback;
+    stream_data.arg = arg;
+
+    jsonp_error_init(error, "<callback>");
+
+    if (callback == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    if(lex_init(&lex, (get_func)callback_get, &stream_data))
+        return NULL;
+
+    result = parse_json(&lex, flags, error);
+
+    lex_close(&lex);
+    return result;
+}
index 7b7203d..ae6c8f9 100644 (file)
@@ -14,3 +14,4 @@ suites/api/test_object
 suites/api/test_pack
 suites/api/test_simple
 suites/api/test_unpack
+suites/api/test_load_callback
index 61a216f..9e60f48 100644 (file)
@@ -8,6 +8,7 @@ check_PROGRAMS = \
        test_equal \
        test_load \
        test_loadb \
+       test_load_callback \
        test_memory_funcs \
        test_number \
        test_object \
index a7c87a9..f5f86bf 100755 (executable)
@@ -51,9 +51,10 @@ json_dumpf
 json_dump_file
 json_dump_callback
 json_loads
+json_loadb
 json_loadf
 json_load_file
-json_loadb
+json_load_callback
 json_equal
 json_copy
 json_deep_copy
diff --git a/test/suites/api/test_load_callback.c b/test/suites/api/test_load_callback.c
new file mode 100644 (file)
index 0000000..9449528
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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_source {
+    const char *buf;
+    size_t off;
+    size_t cap;
+};
+
+static const char my_str[] = "[\"A\", {\"B\": \"C\", \"e\": false}, 1, null, \"foo\"]";
+
+static size_t greedy_reader(void *buf, size_t buflen, void *arg)
+{
+    struct my_source *s = arg;
+    if (buflen > s->cap - s->off)
+        buflen = s->cap - s->off;
+    if (buflen > 0) {
+        memcpy(buf, s->buf + s->off, buflen);
+        s->off += buflen;
+        return buflen;
+    } else {
+        return 0;
+    }
+}
+
+static void run_tests()
+{
+    struct my_source s;
+    json_t *json;
+    json_error_t error;
+
+    s.off = 0;
+    s.cap = strlen(my_str);
+    s.buf = my_str;
+
+    json = json_load_callback(greedy_reader, &s, 0, &error);
+
+    if (!json)
+        fail("json_load_callback failed on a valid callback");
+    json_decref(json);
+
+    s.off = 0;
+    s.cap = strlen(my_str) - 1;
+    s.buf = my_str;
+
+    json = json_load_callback(greedy_reader, &s, 0, &error);
+    if (json) {
+        json_decref(json);
+        fail("json_load_callback should have failed on an incomplete stream, but it didn't");
+    }
+    if (strcmp(error.source, "<callback>") != 0) {
+        fail("json_load_callback returned an invalid error source");
+    }
+    if (strcmp(error.text, "']' expected near end of file") != 0) {
+        fail("json_load_callback returned an invalid error message for an unclosed top-level array");
+    }
+
+    json = json_load_callback(NULL, NULL, 0, &error);
+    if (json) {
+        json_decref(json);
+        fail("json_load_callback should have failed on NULL load callback, but it didn't");
+    }
+    if (strcmp(error.text, "wrong arguments") != 0) {
+        fail("json_load_callback returned an invalid error message for a NULL load callback");
+    }
+}