Adds json_pack / json_unpack variadic functions.
authorGraeme Smecher <graeme.smecher@mail.mcgill.ca>
Mon, 25 Oct 2010 23:36:29 +0000 (16:36 -0700)
committerPetri Lehtinen <petri.lehtinen@inoi.fi>
Tue, 26 Oct 2010 05:59:06 +0000 (08:59 +0300)
src/Makefile.am
src/jansson.h
src/jansson_private.h
src/load.c
src/variadic.c [new file with mode: 0644]
test/.gitignore
test/suites/api/Makefile.am
test/suites/api/check-exports
test/suites/api/test_pack.c [new file with mode: 0644]
test/suites/api/test_unpack.c [new file with mode: 0644]

index 635176e..659dcb4 100644 (file)
@@ -11,7 +11,8 @@ libjansson_la_SOURCES = \
        strbuffer.h \
        utf.c \
        utf.h \
        strbuffer.h \
        utf.c \
        utf.h \
-       value.c
+       value.c \
+       variadic.c
 libjansson_la_LDFLAGS = \
        -export-symbols-regex '^json_' \
        -version-info 3:0:3
 libjansson_la_LDFLAGS = \
        -export-symbols-regex '^json_' \
        -version-info 3:0:3
index e463f81..0f1de8d 100644 (file)
@@ -85,6 +85,14 @@ void json_decref(json_t *json)
 }
 
 
 }
 
 
+/* error reporting */
+
+typedef struct json_error_t json_error_t;
+
+const char *json_error_msg(const json_error_t *error);
+int json_error_line(const json_error_t *error);
+
+
 /* getters, setters, manipulation */
 
 size_t json_object_size(const json_t *object);
 /* getters, setters, manipulation */
 
 size_t json_object_size(const json_t *object);
@@ -156,6 +164,8 @@ int json_string_set_nocheck(json_t *string, const char *value);
 int json_integer_set(json_t *integer, json_int_t value);
 int json_real_set(json_t *real, double value);
 
 int json_integer_set(json_t *integer, json_int_t value);
 int json_real_set(json_t *real, double value);
 
+json_t *json_pack(json_error_t **error, const char *fmt, ...);
+int json_unpack(json_t *root, json_error_t **error, const char *fmt, ...);
 
 /* equality */
 
 
 /* equality */
 
@@ -168,14 +178,6 @@ json_t *json_copy(json_t *value);
 json_t *json_deep_copy(json_t *value);
 
 
 json_t *json_deep_copy(json_t *value);
 
 
-/* error reporting */
-
-typedef struct json_error_t json_error_t;
-
-const char *json_error_msg(const json_error_t *error);
-int json_error_line(const json_error_t *error);
-
-
 /* loading, printing */
 
 json_t *json_loads(const char *input, size_t flags, json_error_t **error);
 /* loading, printing */
 
 json_t *json_loads(const char *input, size_t flags, json_error_t **error);
index e9102ba..2d2af62 100644 (file)
@@ -63,4 +63,11 @@ typedef struct {
 
 const object_key_t *jsonp_object_iter_fullkey(void *iter);
 
 
 const object_key_t *jsonp_object_iter_fullkey(void *iter);
 
+#define JSON_ERROR_MSG_LENGTH 160
+
+struct json_error_t {
+  char msg[JSON_ERROR_MSG_LENGTH];
+  int line;
+};
+
 #endif
 #endif
index a34fd0c..54bfab8 100644 (file)
@@ -60,13 +60,6 @@ typedef struct {
 
 /*** error reporting ***/
 
 
 /*** error reporting ***/
 
-#define JSON_ERROR_MSG_LENGTH 160
-
-struct json_error_t {
-  char msg[JSON_ERROR_MSG_LENGTH];
-  int line;
-};
-
 const char *json_error_msg(const json_error_t *error)
 {
     return error ? error->msg : NULL;
 const char *json_error_msg(const json_error_t *error)
 {
     return error ? error->msg : NULL;
diff --git a/src/variadic.c b/src/variadic.c
new file mode 100644 (file)
index 0000000..765be83
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include <jansson.h>
+#include "jansson_private.h"
+
+static void error_init(json_error_t **error)
+{
+    if(error)
+        *error = NULL;
+}
+
+static void error_set(json_error_t **error, const int line, const char *msg, ...)
+{
+    va_list ap;
+
+    if(!error || *error)
+        return;
+
+    *error = calloc(1, sizeof(json_error_t));
+    if(!*error)
+        return;
+
+    va_start(ap, msg);
+    vsnprintf((*error)->msg, JSON_ERROR_MSG_LENGTH, msg, ap);
+    va_end(ap);
+
+    va_start(ap, msg);
+    vfprintf(stderr, msg, ap);
+    va_end(ap);
+
+    (*error)->line = line;
+}
+
+json_t *json_pack(json_error_t **error, const char *fmt, ...) {
+    int fmt_length = strlen(fmt);
+    va_list ap;
+
+    /* Keep a stack of containers (lists and objects) */
+    int depth = 0;
+    json_t **stack = NULL;
+
+    /* Keep a list of objects we create in case of error */
+    int free_count = 0;
+    json_t **free_list = NULL;
+
+    json_t *cur = NULL; /* Current container */
+    json_t *root = NULL; /* root object */
+    json_t *obj = NULL;
+
+    char *key = NULL; /* Current key in an object */
+    char *s;
+
+    int line = 1;
+
+    /* Allocation provisioned for worst case */
+    stack = calloc(fmt_length, sizeof(json_t *));
+    free_list = calloc(fmt_length, sizeof(json_t *));
+
+    error_init(error);
+
+    if(!stack || !free_list)
+        goto out;
+
+    va_start(ap, fmt);
+    while(*fmt) {
+        switch(*fmt) {
+            case '\n':
+                line++;
+                break;
+
+            case ' ': /* Whitespace */
+                break;
+
+            case ',': /* Element spacer */
+                if(!root)
+                {
+                    error_set(error, line,
+                              "Unexpected COMMA precedes root element!");
+                    root = NULL;
+                    goto out;
+                }
+
+                if(!cur)
+                {
+                    error_set(error, line,
+                              "Unexpected COMMA outside a list or object!");
+                    root = NULL;
+                    goto out;
+                }
+
+                if(key)
+                {
+                    error_set(error, line, "Expected KEY, got COMMA!");
+                    root = NULL;
+                    goto out;
+                }
+                break;
+
+            case ':': /* Key/value separator */
+                if(!key)
+                {
+                    error_set(error, line, "Got key/value separator without "
+                            "a key preceding it!");
+                    root = NULL;
+                    goto out;
+                }
+
+                if(!json_is_object(cur))
+                {
+                    error_set(error, line, "Got a key/value separator "
+                            "(':') outside an object!");
+                    root = NULL;
+                    goto out;
+                }
+
+                break;
+
+            case ']': /* Close array or object */
+            case '}':
+
+                if(key)
+                {
+                    error_set(error, line, "OBJECT or ARRAY ended with an "
+                              "incomplete key/value pair!");
+                    root = NULL;
+                    goto out;
+                }
+
+                if(depth <= 0)
+                {
+                    error_set(error, line,
+                              "Too many close-brackets '%c'", *fmt);
+                    root = NULL;
+                    goto out;
+                }
+
+                if(*fmt == ']' && !json_is_array(cur))
+                {
+                    error_set(error, line,
+                              "Stray close-array ']' character");
+                    root = NULL;
+                    goto out;
+                }
+
+                if(*fmt == '}' && !json_is_object(cur))
+                {
+                    error_set(error, line,
+                              "Stray close-object '}' character");
+                    root = NULL;
+                    goto out;
+                }
+
+                cur = stack[--depth];
+                break;
+
+            case '[':
+                obj = json_array();
+                goto obj_common;
+
+            case '{':
+                obj = json_object();
+                goto obj_common;
+
+            case 's': /* string */
+                s = va_arg(ap, char*);
+
+                if(!s)
+                {
+                    error_set(error, line,
+                              "Refusing to handle a NULL string");
+                    root = NULL;
+                    goto out;
+                }
+
+                if(json_is_object(cur) && !key)
+                {
+                    /* It's a key */
+                    key = s;
+                    break;
+                }
+
+                obj = json_string(s);
+                goto obj_common;
+
+            case 'n': /* null */
+                obj = json_null();
+                goto obj_common;
+
+            case 'b': /* boolean */
+                obj = va_arg(ap, int) ?
+                    json_true() : json_false();
+                goto obj_common;
+
+            case 'i': /* integer */
+                obj = json_integer(va_arg(ap, int));
+                goto obj_common;
+
+            case 'f': /* double-precision float */
+                obj = json_real(va_arg(ap, double));
+                goto obj_common;
+
+            case 'O': /* a json_t object; increments refcount */
+                obj = va_arg(ap, json_t *);
+                json_incref(obj);
+                goto obj_common;
+
+            case 'o': /* a json_t object; doesn't increment refcount */
+                obj = va_arg(ap, json_t *);
+                goto obj_common;
+
+obj_common:     free_list[free_count++] = obj;
+
+                /* Root this object to its parent */
+                if(json_is_object(cur)) {
+                    if(!key)
+                    {
+                        error_set(error, line,
+                                "Expected key, got identifier '%c'!", *fmt);
+                        root = NULL;
+                        goto out;
+                    }
+
+                    json_object_set_new(cur, key, obj);
+                    key = NULL;
+                }
+                else if(json_is_array(cur))
+                {
+                    json_array_append_new(cur, obj);
+                }
+                else if(!root)
+                {
+                    printf("Rooting\n");
+                    root = obj;
+                }
+                else
+                {
+                    error_set(error, line, "Can't figure out where to attach "
+                              "'%c' object!", *fmt);
+                    root = NULL;
+                    goto out;
+                }
+
+                /* If it was a container ('[' or '{'), descend on the stack */
+                if(json_is_array(obj) || json_is_object(obj))
+                {
+                    stack[depth++] = cur;
+                    cur = obj;
+                }
+
+                break;
+        }
+        fmt++;
+    }
+    va_end(ap);
+
+    if(depth != 0) {
+        error_set(error, line,
+                "Missing object or array close-brackets in format string");
+        root = NULL;
+        goto out;
+    }
+
+    /* Success: don't free everything we just built! */
+    free_count = 0;
+
+out:
+    while(free_count)
+        json_decref(free_list[--free_count]);
+
+    if(free_list)
+        free(free_list);
+
+    if(stack)
+        free(stack);
+
+    return(root);
+}
+
+int json_unpack(json_t *root, json_error_t **error, const char *fmt, ...) {
+    va_list ap;
+
+    int rv=0; /* Return value */
+    int line = 1; /* Line number */
+
+    /* Keep a stack of containers (lists and objects) */
+    int depth = 0;
+    json_t **stack;
+
+    int array_index = 0;
+    char *key = NULL; /* Current key in an object */
+
+    json_t *cur = NULL; /* Current container */
+    json_t *obj = NULL;
+
+    int fmt_length = strlen(fmt);
+
+    error_init(error);
+
+    /* Allocation provisioned for worst case */
+    stack = calloc(fmt_length, sizeof(json_t *));
+    if(!stack)
+    {
+        error_set(error, line, "Out of memory!");
+        rv = -1;
+        goto out;
+    }
+
+    /* Even if we're successful, we need to know if the number of
+     * arguments provided matches the number of JSON objects.
+     * We can do this by counting the elements in every array or
+     * object we open up, and decrementing the count as we visit
+     * their children. */
+    int unvisited = 0;
+
+    va_start(ap, fmt);
+    while(*fmt)
+    {
+        switch(*fmt)
+        {
+            case ' ': /* Whitespace */
+                break;
+
+            case '\n': /* Line break */
+                line++;
+                break;
+
+            case ',': /* Element spacer */
+
+                if(!cur)
+                {
+                    error_set(error, line,
+                              "Unexpected COMMA outside a list or object!");
+                    rv = -1;
+                    goto out;
+                }
+
+                if(key)
+                {
+                    error_set(error, line, "Expected KEY, got COMMA!");
+                    rv = -1;
+                    goto out;
+                }
+                break;
+
+            case ':': /* Key/value separator */
+                if(!json_is_object(cur) || !key)
+                {
+                    error_set(error, line, "Unexpected ':'");
+                    rv = -1;
+                    goto out;
+                }
+                break;
+
+            case '[':
+            case '{':
+                /* Fetch object */
+                if(!cur)
+                {
+                    obj = root;
+                }
+                else if(json_is_object(cur))
+                {
+                    if(!key)
+                    {
+                        error_set(error, line, "Objects can't be keys");
+                        rv = -1;
+                        goto out;
+                    }
+                    obj = json_object_get(cur, key);
+                    unvisited--;
+                    key = NULL;
+                }
+                else if(json_is_array(cur))
+                {
+                    obj = json_array_get(cur, array_index);
+                    unvisited--;
+                    array_index++;
+                }
+                else
+                {
+                    assert(0);
+                }
+
+                /* Make sure we got what we expected */
+                if(*fmt=='{' && !json_is_object(obj))
+                {
+                    rv = -2;
+                    goto out;
+                }
+
+                if(*fmt=='[' && !json_is_array(obj))
+                {
+                    rv = -2;
+                    goto out;
+                }
+
+                unvisited += json_is_object(obj) ?
+                    json_object_size(obj) :
+                    json_array_size(obj);
+
+                /* Descend */
+                stack[depth++] = cur;
+                cur = obj;
+
+                key = NULL;
+
+                break;
+
+
+            case ']':
+            case '}':
+
+                if(json_is_array(cur) && *fmt!=']')
+                {
+                    error_set(error, line, "Missing ']'");
+                    rv = -1;
+                    goto out;
+                }
+
+                if(json_is_object(cur) && *fmt!='}')
+                {
+                    error_set(error, line, "Missing '}'");
+                    rv = -1;
+                    goto out;
+                }
+
+                if(key)
+                {
+                    error_set(error, line, "Unexpected '%c'", *fmt);
+                    rv = -1;
+                    goto out;
+                }
+
+                if(depth <= 0)
+                {
+                    error_set(error, line, "Unexpected '%c'", *fmt);
+                    rv = -1;
+                    goto out;
+                }
+
+                cur = stack[--depth];
+
+                break;
+
+            case 's':
+                if(!key && json_is_object(cur))
+                {
+                    /* constant string for key */
+                    key = va_arg(ap, char*);
+                    break;
+                }
+                /* fall through */
+
+            case 'i': /* integer */
+            case 'f': /* double-precision float */
+            case 'O': /* a json_t object; increments refcount */
+            case 'o': /* a json_t object; borrowed reference */
+            case 'b': /* boolean */
+            case 'n': /* null */
+
+                /* Fetch object */
+                if(!cur)
+                {
+                    obj = root;
+                }
+                else if(json_is_object(cur))
+                {
+                    if(!key)
+                    {
+                        error_set(error, line,
+                                  "Only strings may be used as keys!");
+                        rv = -1;
+                        goto out;
+                    }
+
+                    obj = json_object_get(cur, key);
+                    unvisited--;
+                    key = NULL;
+                }
+                else if(json_is_array(cur))
+                {
+                    obj = json_array_get(cur, array_index);
+                    unvisited--;
+                    array_index++;
+                }
+                else
+                {
+                    error_set(error, line,
+                              "Unsure how to retrieve JSON object '%c'",
+                              *fmt);
+                    rv = -1;
+                    goto out;
+                }
+
+                switch(*fmt)
+                {
+                    case 's':
+                        if(!json_is_string(obj))
+                        {
+                            error_set(error, line,
+                                    "Type mismatch! Object wasn't a string.");
+                            rv = -2;
+                            goto out;
+                        }
+                        *va_arg(ap, const char**) = json_string_value(obj);
+                        break;
+
+                    case 'i':
+                        if(!json_is_integer(obj))
+                        {
+                            error_set(error, line,
+                                    "Type mismatch! Object wasn't an integer.");
+                            rv = -2;
+                            goto out;
+                        }
+                        *va_arg(ap, int*) = json_integer_value(obj);
+                        break;
+
+                    case 'b':
+                        if(!json_is_boolean(obj))
+                        {
+                            error_set(error, line,
+                                    "Type mismatch! Object wasn't a boolean.");
+                            rv = -2;
+                            goto out;
+                        }
+                        *va_arg(ap, int*) = json_is_true(obj);
+                        break;
+
+                    case 'f':
+                        if(!json_is_number(obj))
+                        {
+                            error_set(error, line,
+                                    "Type mismatch! Object wasn't a real.");
+                            rv = -2;
+                            goto out;
+                        }
+                        *va_arg(ap, double*) = json_number_value(obj);
+                        break;
+
+                    case 'O':
+                        json_incref(obj);
+                        /* Fall through */
+
+                    case 'o':
+                        *va_arg(ap, json_t**) = obj;
+                        break;
+
+                    case 'n':
+                        /* Don't actually assign anything; we're just happy
+                         * the null turned up as promised in the format
+                         * string. */
+                        break;
+
+                    default:
+                        error_set(error, line,
+                                "Unknown format character '%c'", *fmt);
+                        rv = -1;
+                        goto out;
+                }
+        }
+        fmt++;
+    }
+
+    /* Return 0 if everything was matched; otherwise the number of JSON
+     * objects we didn't get to. */
+    rv = unvisited;
+
+out:
+    va_end(ap);
+
+    if(stack)
+        free(stack);
+
+    return(rv);
+}
+
+/* vim: ts=4:expandtab:sw=4
+ */
index dca3c1e..cb88169 100644 (file)
@@ -9,3 +9,5 @@ suites/api/test_number
 suites/api/test_object
 suites/api/test_simple
 suites/api/test_cpp
 suites/api/test_object
 suites/api/test_simple
 suites/api/test_cpp
+suites/api/test_pack
+suites/api/test_unpack
index 7125792..1e4a0bb 100644 (file)
@@ -8,7 +8,9 @@ check_PROGRAMS = \
        test_load \
        test_simple \
        test_number \
        test_load \
        test_simple \
        test_number \
-       test_object
+       test_object \
+       test_pack \
+       test_unpack
 
 test_array_SOURCES = test_array.c util.h
 test_copy_SOURCES = test_copy.c util.h
 
 test_array_SOURCES = test_array.c util.h
 test_copy_SOURCES = test_copy.c util.h
@@ -17,6 +19,8 @@ test_load_SOURCES = test_load.c util.h
 test_simple_SOURCES = test_simple.c util.h
 test_number_SOURCES = test_number.c util.h
 test_object_SOURCES = test_object.c util.h
 test_simple_SOURCES = test_simple.c util.h
 test_number_SOURCES = test_number.c util.h
 test_object_SOURCES = test_object.c util.h
+test_pack_SOURCES = test_pack.c util.h
+test_unpack_SOURCES = test_unpack.c util.h
 
 AM_CPPFLAGS = -I$(top_srcdir)/src
 AM_CFLAGS = -Wall -Werror
 
 AM_CPPFLAGS = -I$(top_srcdir)/src
 AM_CFLAGS = -Wall -Werror
index b1ef400..af22c28 100755 (executable)
@@ -55,6 +55,8 @@ json_load_file
 json_equal
 json_copy
 json_deep_copy
 json_equal
 json_copy
 json_deep_copy
+json_pack
+json_unpack
 EOF
 
 # The list of functions are not exported in the library because they
 EOF
 
 # The list of functions are not exported in the library because they
diff --git a/test/suites/api/test_pack.c b/test/suites/api/test_pack.c
new file mode 100644 (file)
index 0000000..f1ca388
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <string.h>
+#include <jansson.h>
+#include <stdio.h>
+#include "util.h"
+
+int main()
+{
+    json_t *value;
+    int i;
+
+    /*
+     * Simple, valid json_pack cases
+     */
+
+    /* true */
+    value = json_pack(NULL, "b", 1);
+    if(!json_is_true(value))
+            fail("json_pack boolean failed");
+    if(value->refcount != (ssize_t)-1)
+            fail("json_pack boolean refcount failed");
+    json_decref(value);
+
+    /* false */
+    value = json_pack(NULL, "b", 0);
+    if(!json_is_false(value))
+            fail("json_pack boolean failed");
+    if(value->refcount != (ssize_t)-1)
+            fail("json_pack boolean refcount failed");
+    json_decref(value);
+
+    /* null */
+    value = json_pack(NULL, "n");
+    if(!json_is_null(value))
+            fail("json_pack null failed");
+    if(value->refcount != (ssize_t)-1)
+            fail("json_pack null refcount failed");
+    json_decref(value);
+
+    /* integer */
+    value = json_pack(NULL, "i", 1);
+    if(!json_is_integer(value) || json_integer_value(value) != 1)
+            fail("json_pack integer failed");
+    if(value->refcount != (ssize_t)1)
+            fail("json_pack integer refcount failed");
+    json_decref(value);
+
+
+    /* real */
+    value = json_pack(NULL, "f", 1.0);
+    if(!json_is_real(value) || json_real_value(value) != 1.0)
+            fail("json_pack real failed");
+    if(value->refcount != (ssize_t)1)
+            fail("json_pack real refcount failed");
+    json_decref(value);
+
+    /* string */
+    value = json_pack(NULL, "s", "test");
+    if(!json_is_string(value) || strcmp("test", json_string_value(value)))
+            fail("json_pack string failed");
+    if(value->refcount != (ssize_t)1)
+            fail("json_pack string refcount failed");
+    json_decref(value);
+
+    /* empty object */
+    value = json_pack(NULL, "{}", 1.0);
+    if(!json_is_object(value) || json_object_size(value) != 0)
+            fail("json_pack empty object failed");
+    if(value->refcount != (ssize_t)1)
+            fail("json_pack empty object refcount failed");
+    json_decref(value);
+
+    /* empty list */
+    value = json_pack(NULL, "[]", 1.0);
+    if(!json_is_array(value) || json_array_size(value) != 0)
+            fail("json_pack empty list failed");
+    if(value->refcount != (ssize_t)1)
+            fail("json_pack empty list failed");
+    json_decref(value);
+
+    /* non-incref'd object */
+    value = json_pack(NULL, "o", json_integer(1));
+    if(!json_is_integer(value) || json_integer_value(value) != 1)
+            fail("json_pack object failed");
+    if(value->refcount != (ssize_t)1)
+            fail("json_pack integer refcount failed");
+    json_decref(value);
+
+    /* incref'd object */
+    value = json_pack(NULL, "O", json_integer(1));
+    if(!json_is_integer(value) || json_integer_value(value) != 1)
+            fail("json_pack object failed");
+    if(value->refcount != (ssize_t)2)
+            fail("json_pack integer refcount failed");
+    json_decref(value);
+    json_decref(value);
+
+    /* simple object */
+    value = json_pack(NULL, "{s:[]}", "foo");
+    if(!json_is_object(value) || json_object_size(value) != 1)
+            fail("json_pack object failed");
+    if(!json_is_array(json_object_get(value, "foo")))
+            fail("json_pack object failed");
+    if(json_object_get(value, "foo")->refcount != (ssize_t)1)
+            fail("json_pack object refcount failed");
+    json_decref(value);
+
+    /* simple array */
+    value = json_pack(NULL, "[i,i,i]", 0, 1, 2);
+    if(!json_is_array(value) || json_array_size(value) != 3)
+            fail("json_pack object failed");
+    for(i=0; i<3; i++)
+    {
+        if(!json_is_integer(json_array_get(value, i)) ||
+           json_integer_value(json_array_get(value, i)) != i)
+
+            fail("json_pack integer array failed");
+    }
+    json_decref(value);
+
+    /*
+     * Invalid cases
+     */
+    
+    /* mismatched open/close array/object */
+    if(json_pack(NULL, "[}"))
+        fail("json_pack failed to catch mismatched '}'");
+
+    if(json_pack(NULL, "{]"))
+        fail("json_pack failed to catch mismatched ']'");
+
+    /* missing close array */
+    if(json_pack(NULL, "["))
+        fail("json_pack failed to catch missing ']'");
+
+    /* missing close object */
+    if(json_pack(NULL, "{"))
+        fail("json_pack failed to catch missing '}'");
+
+    /* NULL string */
+    if(json_pack(NULL, "s", NULL))
+        fail("json_pack failed to catch null string");
+
+    return(0);
+}
+
+/* vim: ts=4:expandtab:sw=4
+ */
diff --git a/test/suites/api/test_unpack.c b/test/suites/api/test_unpack.c
new file mode 100644 (file)
index 0000000..ea18ff8
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <string.h>
+#include <jansson.h>
+#include <stdio.h>
+#include "util.h"
+
+int main()
+{
+    json_t *j, *j2;
+    int i1, i2, i3;
+    int rv;
+    //void* v;
+    double f;
+    char *s;
+
+    /*
+     * Simple, valid json_pack cases
+     */
+
+    /* true */
+    rv = json_unpack(json_true(), NULL, "b", &i1);
+    if(rv || !i1)
+        fail("json_unpack boolean failed");
+
+    /* false */
+    rv = json_unpack(json_false(), NULL, "b", &i1);
+    if(rv || i1)
+        fail("json_unpack boolean failed");
+
+    /* null */
+    rv = json_unpack(json_null(), NULL, "n");
+    if(rv)
+        fail("json_unpack null failed");
+
+    /* integer */
+    j = json_integer(1);
+    rv = json_unpack(j, NULL, "i", &i1);
+    if(rv || i1 != 1)
+        fail("json_unpack integer failed");
+    json_decref(j);
+
+    /* real */
+    j = json_real(1.0);
+    rv = json_unpack(j, NULL, "f", &f);
+    if(rv || f != 1.0)
+        fail("json_unpack real failed");
+    json_decref(j);
+
+    /* string */
+    j = json_string("foo");
+    rv = json_unpack(j, NULL, "s", &s);
+    if(rv || strcmp(s, "foo"))
+        fail("json_unpack string failed");
+    json_decref(j);
+
+    /* empty object */
+    j = json_object();
+    rv = json_unpack(j, NULL, "{}");
+    if(rv)
+        fail("json_unpack empty object failed");
+    json_decref(j);
+
+    /* empty list */
+    j = json_array();
+    rv = json_unpack(j, NULL, "[]");
+    if(rv)
+        fail("json_unpack empty list failed");
+    json_decref(j);
+
+    /* non-incref'd object */
+    j = json_object();
+    rv = json_unpack(j, NULL, "o", &j2);
+    if(j2 != j || j->refcount != (ssize_t)1)
+        fail("json_unpack object failed");
+    json_decref(j);
+
+    /* incref'd object */
+    j = json_object();
+    rv = json_unpack(j, NULL, "O", &j2);
+    if(j2 != j || j->refcount != (ssize_t)2)
+        fail("json_unpack object failed");
+    json_decref(j);
+    json_decref(j);
+
+    /* simple object */
+    j = json_pack(NULL, "{s:i}", "foo", 1);
+    rv = json_unpack(j, NULL, "{s:i}", "foo", &i1);
+    if(rv || i1!=1)
+        fail("json_unpack simple object failed");
+    json_decref(j);
+
+    /* simple array */
+    j = json_pack(NULL, "[iii]", 1, 2, 3);
+    rv = json_unpack(j, NULL, "[i,i,i]", &i1, &i2, &i3);
+    if(rv || i1 != 1 || i2 != 2 || i3 != 3)
+        fail("json_unpack simple array failed");
+    json_decref(j);
+
+    return 0;
+}
+
+/* vim: ts=4:expandtab:sw=4
+ */