Merge branch '1.0'
authorPetri Lehtinen <petri@digip.org>
Sun, 11 Oct 2009 18:51:54 +0000 (21:51 +0300)
committerPetri Lehtinen <petri@digip.org>
Sun, 11 Oct 2009 18:51:54 +0000 (21:51 +0300)
Conflicts:
configure.ac
doc/conf.py

24 files changed:
configure.ac
doc/apiref.rst
doc/conf.py
doc/gettingstarted.rst [new file with mode: 0644]
doc/index.rst
src/Makefile.am
src/dump.c
src/hashtable.c
src/hashtable.h
src/jansson.h
src/load.c
src/value.c
test/.gitignore
test/run-test
test/split-testfile.py
test/test-invalid
test/test-valid
test/testdata/invalid-strip [new file with mode: 0644]
test/testdata/valid-strip [new file with mode: 0644]
test/testprogs/Makefile.am
test/testprogs/test_array.c
test/testprogs/test_object.c
test/testprogs/test_simple.c [new file with mode: 0644]
test/testprogs/util.h

index 009e0fb..c2de72f 100644 (file)
@@ -1,5 +1,5 @@
 AC_PREREQ([2.59])
-AC_INIT([jansson], [1.0.4], [petri@digip.org])
+AC_INIT([jansson], [1.0.4+], [petri@digip.org])
 
 AM_INIT_AUTOMAKE([1.10 foreign])
 
index 903b48c..1a6a77b 100644 (file)
@@ -41,6 +41,12 @@ Objects of :ctype:`json_t` are always used through a pointer. There
 are APIs for querying the type, manipulating the reference count, and
 for constructing and manipulating values of different types.
 
+Unless noted otherwise, all API functions return an error value if an
+error occurs. Depending on the function's signature, the error value
+is either *NULL* or -1. Invalid arguments or invalid input are
+apparent sources for errors. Memory allocation and I/O operations may
+also cause errors.
+
 
 Type
 ----
@@ -80,8 +86,8 @@ functions:
 .. cfunction:: int json_typeof(const json_t *json)
 
    Return the type of the JSON value (a :ctype:`json_type` cast to
-   :ctype:`int`). This function is actually implemented as a macro for
-   speed.
+   :ctype:`int`). *json* MUST NOT be *NULL*. This function is actually
+   implemented as a macro for speed.
 
 .. cfunction:: json_is_object(const json_t *json)
                json_is_array(const json_t *json)
@@ -93,17 +99,18 @@ functions:
                json_is_null(const json_t *json)
 
    These functions (actually macros) return true (non-zero) for values
-   of the given type, and false (zero) for values of other types.
+   of the given type, and false (zero) for values of other types and
+   for *NULL*.
 
 .. cfunction:: json_is_number(const json_t *json)
 
    Returns true for values of types :const:`JSON_INTEGER` and
-   :const:`JSON_REAL`, and false for other types.
+   :const:`JSON_REAL`, and false for other types and for *NULL*.
 
 .. cfunction:: json_is_boolean(const json_t *json)
 
    Returns true for types :const:`JSON_TRUE` and :const:`JSON_FALSE`,
-   and false for values of other types.
+   and false for values of other types and for *NULL*.
 
 
 Reference Count
@@ -121,7 +128,8 @@ The following functions are used to manipulate the reference count.
 
 .. cfunction:: json_t *json_incref(json_t *json)
 
-   Increment the reference count of *json*.
+   Increment the reference count of *json* if it's not non-*NULL*.
+   Returns *json*.
 
 .. cfunction:: void json_decref(json_t *json)
 
@@ -139,7 +147,7 @@ the value is no longer needed, :cfunc:`json_decref` should be called
 to release the reference.
 
 Normally, all functions accepting a JSON value as an argument will
-manage the reference, i.e. increase and decrease the reference count
+nmanage the reference, i.e. increase and decrease the reference count
 as needed. However, some functions **steal** the reference, i.e. they
 have the same result as if the user called :cfunc:`json_decref()` on
 the argument right after calling the function. These are usually
@@ -154,26 +162,26 @@ argument.
 True, False and Null
 ====================
 
+These values are implemented as singletons, so each of these functions
+returns the same value each time.
+
 .. cfunction:: json_t *json_true(void)
 
    .. refcounting:: new
 
-   Returns a value of the type :const:`JSON_TRUE`, or *NULL* on
-   error.
+   Returns the JSON true value.
 
 .. cfunction:: json_t *json_false(void)
 
    .. refcounting:: new
 
-   Returns a value of the type :const:`JSON_FALSE`, or *NULL* on
-   error.
+   Returns the JSON false value.
 
 .. cfunction:: json_t *json_null(void)
 
    .. refcounting:: new
 
-   Returns a value of the type :const:`JSON_NULL`, or *NULL* on
-   error.
+   Returns the JSON null value.
 
 
 String
@@ -183,13 +191,14 @@ String
 
    .. refcounting:: new
 
-   Returns a new value of the type :const:`JSON_STRING`, or *NULL* on
-   error. *value* must be a valid UTF-8 encoded Unicode string.
+   Returns a new JSON string, or *NULL* on error. *value* must be a
+   valid UTF-8 encoded Unicode string.
 
 .. cfunction:: const char *json_string_value(const json_t *json)
 
-   Returns the associated value of a :const:`JSON_STRING` value as a
-   null terminated UTF-8 encoded string.
+   Returns the associated value of the JSON string *json* as a null
+   terminated UTF-8 encoded string, or *NULL* if *json* is not a JSON
+   string.
 
 
 Number
@@ -199,33 +208,32 @@ Number
 
    .. refcounting:: new
 
-   Returns a new value of the type :const:`JSON_INTEGER`, or *NULL* on
-   error.
+   Returns a new JSON integer, or *NULL* on error.
 
 .. cfunction:: int json_integer_value(const json_t *json)
 
-   Returns the associated integer value of values of the type
-   :const:`JSON_INTEGER`, or 0 for values of other types.
+   Returns the associated value the JSON integer *json*. If *json* is
+   *NULL* or not a JSON integer, 0 is returned.
 
 .. cfunction:: json_t *json_real(double value)
 
    .. refcounting:: new
 
-   Returns a new value of the type :const:`JSON_REAL`, or *NULL* on
-   error.
+   Returns a new JSON real, or *NULL* on error.
 
 .. cfunction:: double json_real_value(const json_t *json)
 
-   Returns the associated real value of values of the type
-   :const:`JSON_INTEGER`, or 0 for values of other types.
+   Returns the associated value of the JSON real *json*. If *json* is
+   *NULL* or not a JSON real, 0.0 is returned.
 
 In addition to the functions above, there's a common query function
 for integers and reals:
 
 .. cfunction:: double json_number_value(const json_t *json)
 
-   Returns the value of either ``JSON_INTEGER`` or ``JSON_REAL``, cast
-   to double regardless of the actual type.
+   Returns the associated value of the JSON integer or JSON real
+   *json*, cast to double regardless of the actual type. If *json* is
+   neither JSON real nor JSON integer, 0.0 is returned.
 
 
 Array
@@ -237,33 +245,90 @@ A JSON array is an ordered collection of other JSON values.
 
    .. refcounting:: new
 
-   Returns a new value of the type :const:`JSON_ARRAY`, or *NULL* on
-   error. Initially, the array is empty.
+   Returns a new JSON array, or *NULL* on error. Initially, the array
+   is empty.
 
 .. cfunction:: unsigned int json_array_size(const json_t *array)
 
-   Returns the number of elements in *array*.
+   Returns the number of elements in *array*, or 0 if *array* is NULL
+   or not a JSON array.
 
 .. cfunction:: json_t *json_array_get(const json_t *array, unsigned int index)
 
    .. refcounting:: borrow
 
-   Returns the element in *array* at position *index*, or *NULL* if
-   *index* is out of range. The valid range for *index* is from 0 to
-   the return value of :cfunc:`json_array_size()` minus 1.
+   Returns the element in *array* at position *index*. The valid range
+   for *index* is from 0 to the return value of
+   :cfunc:`json_array_size()` minus 1. If *array* is not a JSON array,
+   if *array* is *NULL*, or if *index* is out of range, *NULL* is
+   returned.
 
 .. cfunction:: int json_array_set(json_t *array, unsigned int index, json_t *value)
 
    Replaces the element in *array* at position *index* with *value*.
-   Returns 0 on success, or -1 if *index* is out of range. The valid
-   range for *index* is from 0 to the return value of
-   :cfunc:`json_array_size()` minus 1.
+   The valid range for *index* is from 0 to the return value of
+   :cfunc:`json_array_size()` minus 1. Returns 0 on success and -1 on
+   error.
+
+.. cfunction:: int json_array_set_new(json_t *array, unsigned int index, json_t *value)
+
+   Like :cfunc:`json_array_set()` but steals the reference to *value*.
+   This is useful when *value* is newly created and not used after
+   the call.
+
+   .. versionadded:: 1.1
 
 .. cfunction:: int json_array_append(json_t *array, json_t *value)
 
    Appends *value* to the end of *array*, growing the size of *array*
    by 1. Returns 0 on success and -1 on error.
 
+.. cfunction:: int json_array_append_new(json_t *array, json_t *value)
+
+   Like :cfunc:`json_array_append()` but steals the reference to
+   *value*. This is useful when *value* is newly created and not used
+   after the call.
+
+   .. versionadded:: 1.1
+
+.. cfunction:: int json_array_insert(json_t *array, unsigned int index, json_t *value)
+
+   Inserts *value* to *array* at position *index*, shifting the
+   elements at *index* and after it one position towards the end of
+   the array. Returns 0 on success and -1 on error.
+
+   .. versionadded:: 1.1
+
+.. cfunction:: int json_array_insert_new(json_t *array, unsigned int index, json_t *value)
+
+   Like :cfunc:`json_array_insert()` but steals the reference to
+   *value*. This is useful when *value* is newly created and not used
+   after the call.
+
+   .. versionadded:: 1.1
+
+.. cfunction:: int json_array_remove(json_t *array, unsigned int index)
+
+   Removes the element in *array* at position *index*, shifting the
+   elements after *index* one position towards the start of the array.
+   Returns 0 on success and -1 on error.
+
+   .. versionadded:: 1.1
+
+.. cfunction:: int json_array_clear(json_t *array)
+
+   Removes all elements from *array*. Returns 0 on sucess and -1 on
+   error.
+
+   .. versionadded:: 1.1
+
+.. cfunction:: int json_array_extend(json_t *array, json_t *other_array)
+
+   Appends all elements in *other_array* to the end of *array*.
+   Returns 0 on success and -1 on error.
+
+   .. versionadded:: 1.1
+
 
 Object
 ======
@@ -275,8 +340,15 @@ Unicode string and the value is any JSON value.
 
    .. refcounting:: new
 
-   Returns a new value of the type :const:`JSON_OBJECT`, or *NULL* on
-   error. Initially, the object is empty.
+   Returns a new JSON object, or *NULL* on error. Initially, the
+   object is empty.
+
+.. cfunction:: unsigned int json_object_size(const json_t *object)
+
+   Returns the number of elements in *object*, or 0 if *object* is not
+   a JSON object.
+
+   .. versionadded:: 1.1
 
 .. cfunction:: json_t *json_object_get(const json_t *object, const char *key)
 
@@ -288,9 +360,17 @@ Unicode string and the value is any JSON value.
 .. cfunction:: int json_object_set(json_t *object, const char *key, json_t *value)
 
    Set the value of *key* to *value* in *object*. *key* must be a
-   valid terminated UTF-8 encoded Unicode string. If there already is
-   a value for *key*, it is replaced by the new value. Returns 0 on
-   success and -1 on error.
+   valid null terminated UTF-8 encoded Unicode string. If there
+   already is a value for *key*, it is replaced by the new value.
+   Returns 0 on success and -1 on error.
+
+.. cfunction:: int json_object_set_new(json_t *object, const char *key, json_t *value)
+
+   Like :cfunc:`json_object_set()` but steals the reference to
+   *value*. This is useful when *value* is newly created and not used
+   after the call.
+
+   .. versionadded:: 1.1
 
 .. cfunction:: int json_object_del(json_t *object, const char *key)
 
@@ -298,6 +378,21 @@ Unicode string and the value is any JSON value.
    -1 if *key* was not found.
 
 
+.. cfunction:: int json_object_clear(json_t *object)
+
+   Remove all elements from *object*. Returns 0 on success and -1 if
+   *object* is not a JSON object.
+
+   .. versionadded:: 1.1
+
+.. cfunction:: int json_object_update(json_t *object, json_t *other)
+
+   Update *object* with the key-value pairs from *other*, overwriting
+   existing keys. Returns 0 on success or -1 on error.
+
+   .. versionadded:: 1.1
+
+
 The following functions implement an iteration protocol for objects:
 
 .. cfunction:: void *json_object_iter(json_t *object)
@@ -321,6 +416,20 @@ The following functions implement an iteration protocol for objects:
 
    Extract the associated value from *iter*.
 
+The iteration protocol can be used for example as follows::
+
+   /* obj is a JSON object */
+   const char *key;
+   json_t *value;
+   void *iter = json_object_iter(obj);
+   while(iter)
+   {
+       key = json_object_iter_key(iter);
+       value = json_object_iter_value(iter);
+       /* use key and value ... */
+       iter = json_object_iter_next(obj, iter);
+   }
+
 
 Encoding
 ========
@@ -343,18 +452,18 @@ can be ORed together to obtain *flags*.
 The following functions perform the actual JSON encoding. The result
 is in UTF-8.
 
-.. cfunction:: char *json_dumps(const json_t *root, uint32_t flags)
+.. cfunction:: char *json_dumps(const json_t *root, unsigned long flags)
 
    Returns the JSON representation of *root* as a string, or *NULL* on
    error. *flags* is described above. The return value must be freed
    by the caller using :cfunc:`free()`.
 
-.. cfunction:: int json_dumpf(const json_t *root, FILE *output, uint32_t flags)
+.. cfunction:: int json_dumpf(const json_t *root, FILE *output, unsigned long flags)
 
    Write the JSON representation of *root* to the stream *output*.
    *flags* is described above. Returns 0 on success and -1 on error.
 
-.. cfunction:: int json_dump_file(const json_t *json, const char *path, uint32_t flags)
+.. cfunction:: int json_dump_file(const json_t *json, const char *path, unsigned long flags)
 
    Write the JSON representation of *root* to the file *path*. If
    *path* already exists, it is overwritten. *flags* is described
index 355c72f..16d9c80 100644 (file)
@@ -52,7 +52,7 @@ copyright = u'2009, Petri Lehtinen'
 # The short X.Y version.
 version = '1.0'
 # The full version, including alpha/beta/rc tags.
-release = '1.0.4'
+release = '1.0.4+'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/doc/gettingstarted.rst b/doc/gettingstarted.rst
new file mode 100644 (file)
index 0000000..1181588
--- /dev/null
@@ -0,0 +1,91 @@
+***************
+Getting Started
+***************
+
+.. highlight:: c
+
+Compiling and Installing Jansson
+================================
+
+This chapter explains how to compile and install the library itself.
+
+
+Compiling and Installing from a Source Tarball
+----------------------------------------------
+
+Unpack the source tarball and change to the source directory:
+
+.. parsed-literal::
+
+    bunzip2 -c jansson-|release|.tar.bz2 | tar xf -
+    cd jansson-|release|
+
+The source uses GNU Autotools (autoconf_, automake_, libtool_), so
+compiling and installing is extremely simple::
+
+    ./configure
+    make
+    make check
+    make install
+
+To change the destination directory (``/usr/local`` by default), use
+the ``--prefix=DIR`` argument to ``./configure``. See ``./configure
+--help`` for the list of all possible installation options. (There are
+no options to customize the resulting Jansson binary.)
+
+The command ``make check`` runs the test suite distributed with
+Jansson. This step is not strictly necessary, but it may find possible
+problems that Jansson has on your platform. If any problems are found,
+please report them.
+
+.. _autoconf: http://www.gnu.org/software/autoconf/
+.. _automake: http://www.gnu.org/software/automake/
+.. _libtool: http://www.gnu.org/software/libtool/
+
+
+Compiling and Installing from Git
+---------------------------------
+
+If you obtained the source from a Git repository (or any other source
+control system), there's no ``./configure`` script as it's not kept in
+version control. To create the script, Autotools needs to be
+bootstrapped. There are many ways to do this, but the easiest one is
+to use ``autoreconf``::
+
+    autoreconf -vi
+
+This command creates the ``./configure`` script, which can then be
+used as described in the previous section.
+
+
+Installing Prebuilt Binary Packages
+-----------------------------------
+
+Binary ``.deb`` packages for Ubuntu are available in the `Jansson
+PPA`_ at Launchpad_. Follow the instructions in the PPA ("Read about
+installing" link) to take the PPA into use. Then install the -dev
+package::
+
+  apt-get install libjansson-dev
+
+.. _Jansson PPA: http://launchpad.net/~petri/+archive/ppa
+.. _Launchpad: http://launchpad.net/
+
+
+
+Compiling Programs Using Jansson
+================================
+
+Jansson involves one C header file, :file:`jansson.h`, so it's enough
+to put the line
+
+::
+
+    #include <jansson.h>
+
+in the beginning of every source file that uses Jansson.
+
+There's also just one library to link with, ``libjansson``. Compile and
+link the program as follows::
+
+    cc -o prog prog.c -ljansson
index d36760f..97ff155 100644 (file)
@@ -10,6 +10,7 @@ This is the documentation for Jansson_ |release|, last updated |today|.
 .. toctree::
    :maxdepth: 2
 
+   gettingstarted
    apiref
 
 
index 9c4d3f8..36f8467 100644 (file)
@@ -15,4 +15,4 @@ libjansson_la_SOURCES = \
        value.c
 libjansson_la_LDFLAGS = -version-info 0:4:0
 
-AM_CFLAGS = -Wall -Wextra -Werror -std=c99
+AM_CFLAGS = -Wall -Wextra -Werror
index 28c00b9..93717ab 100644 (file)
@@ -9,7 +9,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
 #include <jansson.h>
 #include "strbuffer.h"
@@ -36,22 +35,23 @@ static int dump_to_file(const char *buffer, int size, void *data)
     return 0;
 }
 
-static int dump_indent(uint32_t flags, int depth, dump_func dump, void *data)
+/* 256 spaces (the maximum indentation size) */
+static char whitespace[] = "                                                                                                                                                                                                                                                                ";
+
+static int dump_indent(unsigned long flags, int depth, dump_func dump, void *data)
 {
     if(JSON_INDENT(flags) > 0)
     {
-        char *ws_buffer;
-        int ws_count = JSON_INDENT(flags) * depth;
+        int i, ws_count = JSON_INDENT(flags);
 
         if(dump("\n", 1, data))
             return -1;
 
-        if(ws_count == 0)
-            return 0;
-
-        ws_buffer = alloca(ws_count);
-        memset(ws_buffer, ' ', ws_count);
-        return dump(ws_buffer, ws_count, data);
+        for(i = 0; i < depth; i++)
+        {
+            if(dump(whitespace, ws_count, data))
+                return -1;
+        }
     }
     return 0;
 }
@@ -111,7 +111,7 @@ static int dump_string(const char *str, dump_func dump, void *data)
     return dump("\"", 1, data);
 }
 
-static int do_dump(const json_t *json, uint32_t flags, int depth,
+static int do_dump(const json_t *json, unsigned long flags, int depth,
                    dump_func dump, void *data)
 {
     switch(json_typeof(json)) {
@@ -232,7 +232,7 @@ static int do_dump(const json_t *json, uint32_t flags, int depth,
 }
 
 
-char *json_dumps(const json_t *json, uint32_t flags)
+char *json_dumps(const json_t *json, unsigned long flags)
 {
     strbuffer_t strbuff;
     char *result;
@@ -255,7 +255,7 @@ char *json_dumps(const json_t *json, uint32_t flags)
     return result;
 }
 
-int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
+int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
 {
     if(!json_is_array(json) && !json_is_object(json))
         return -1;
@@ -265,7 +265,7 @@ int json_dumpf(const json_t *json, FILE *output, uint32_t flags)
     return dump_to_file("\n", 1, (void *)output);
 }
 
-int json_dump_file(const json_t *json, const char *path, uint32_t flags)
+int json_dump_file(const json_t *json, const char *path, unsigned long flags)
 {
     int result;
 
index afb3304..05dc167 100644 (file)
@@ -133,6 +133,23 @@ static int hashtable_do_del(hashtable_t *hashtable,
     return 0;
 }
 
+static void hashtable_do_clear(hashtable_t *hashtable)
+{
+    list_t *list, *next;
+    pair_t *pair;
+
+    for(list = hashtable->list.next; list != &hashtable->list; list = next)
+    {
+        next = list->next;
+        pair = list_to_pair(list);
+        if(hashtable->free_key)
+            hashtable->free_key(pair->key);
+        if(hashtable->free_value)
+            hashtable->free_value(pair->value);
+        free(pair);
+    }
+}
+
 static int hashtable_do_rehash(hashtable_t *hashtable)
 {
     list_t *list, *next;
@@ -220,19 +237,7 @@ int hashtable_init(hashtable_t *hashtable,
 
 void hashtable_close(hashtable_t *hashtable)
 {
-    list_t *list, *next;
-    pair_t *pair;
-    for(list = hashtable->list.next; list != &hashtable->list; list = next)
-    {
-        next = list->next;
-        pair = list_to_pair(list);
-        if(hashtable->free_key)
-            hashtable->free_key(pair->key);
-        if(hashtable->free_value)
-            hashtable->free_value(pair->value);
-        free(pair);
-    }
-
+    hashtable_do_clear(hashtable);
     free(hashtable->buckets);
 }
 
@@ -292,6 +297,22 @@ int hashtable_del(hashtable_t *hashtable, const void *key)
     return hashtable_do_del(hashtable, key, hash);
 }
 
+void hashtable_clear(hashtable_t *hashtable)
+{
+    unsigned int i;
+
+    hashtable_do_clear(hashtable);
+
+    for(i = 0; i < num_buckets(hashtable); i++)
+    {
+        hashtable->buckets[i].first = hashtable->buckets[i].last =
+            &hashtable->list;
+    }
+
+    list_init(&hashtable->list);
+    hashtable->size = 0;
+}
+
 void *hashtable_iter(hashtable_t *hashtable)
 {
     return hashtable_iter_next(hashtable, &hashtable->list);
index 76666a4..d01b7e7 100644 (file)
@@ -135,6 +135,15 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
 int hashtable_del(hashtable_t *hashtable, const void *key);
 
 /**
+ * hashtable_clear - Clear hashtable
+ *
+ * @hashtable: The hashtable object
+ *
+ * Removes all items from the hashtable.
+ */
+void hashtable_clear(hashtable_t *hashtable);
+
+/**
  * hashtable_iter - Iterate over hashtable
  *
  * @hashtable: The hashtable object
index 02b20f5..6751998 100644 (file)
@@ -54,7 +54,7 @@ json_t *json_null(void);
 
 static inline json_t *json_incref(json_t *json)
 {
-    if(json)
+    if(json && json->refcount != (unsigned int)-1)
         ++json->refcount;
     return json;
 }
@@ -64,25 +64,56 @@ void json_delete(json_t *json);
 
 static inline void json_decref(json_t *json)
 {
-    if(json && --json->refcount == 0)
+    if(json && json->refcount != (unsigned int)-1 && --json->refcount == 0)
         json_delete(json);
 }
 
 
 /* getters, setters, manipulation */
 
+unsigned int json_object_size(const json_t *object);
 json_t *json_object_get(const json_t *object, const char *key);
-int json_object_set(json_t *object, const char *key, json_t *value);
+int json_object_set_new(json_t *object, const char *key, json_t *value);
 int json_object_del(json_t *object, const char *key);
+int json_object_clear(json_t *object);
+int json_object_update(json_t *object, json_t *other);
 void *json_object_iter(json_t *object);
 void *json_object_iter_next(json_t *object, void *iter);
 const char *json_object_iter_key(void *iter);
 json_t *json_object_iter_value(void *iter);
 
+static inline
+int json_object_set(json_t *object, const char *key, json_t *value)
+{
+    return json_object_set_new(object, key, json_incref(value));
+}
+
 unsigned int json_array_size(const json_t *array);
 json_t *json_array_get(const json_t *array, unsigned int index);
-int json_array_set(json_t *array, unsigned int index, json_t *value);
-int json_array_append(json_t *array, json_t *value);
+int json_array_set_new(json_t *array, unsigned int index, json_t *value);
+int json_array_append_new(json_t *array, json_t *value);
+int json_array_insert_new(json_t *array, unsigned int index, json_t *value);
+int json_array_remove(json_t *array, unsigned int index);
+int json_array_clear(json_t *array);
+int json_array_extend(json_t *array, json_t *other);
+
+static inline
+int json_array_set(json_t *array, unsigned int index, json_t *value)
+{
+    return json_array_set_new(array, index, json_incref(value));
+}
+
+static inline
+int json_array_append(json_t *array, json_t *value)
+{
+    return json_array_append_new(array, json_incref(value));
+}
+
+static inline
+int json_array_insert(json_t *array, unsigned int index, json_t *value)
+{
+    return json_array_insert_new(array, index, json_incref(value));
+}
 
 const char *json_string_value(const json_t *json);
 int json_integer_value(const json_t *json);
@@ -105,8 +136,8 @@ json_t *json_load_file(const char *path, json_error_t *error);
 
 #define JSON_INDENT(n)   (n & 0xFF)
 
-char *json_dumps(const json_t *json, uint32_t flags);
-int json_dumpf(const json_t *json, FILE *output, uint32_t flags);
-int json_dump_file(const json_t *json, const char *path, uint32_t flags);
+char *json_dumps(const json_t *json, unsigned long flags);
+int json_dumpf(const json_t *json, FILE *output, unsigned long flags);
+int json_dump_file(const json_t *json, const char *path, unsigned long flags);
 
 #endif
index 3f41795..cdffa11 100644 (file)
@@ -13,7 +13,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
-#include <unistd.h>
 #include <assert.h>
 
 #include <jansson.h>
@@ -572,6 +571,17 @@ out:
     return lex->token;
 }
 
+static char *lex_steal_string(lex_t *lex)
+{
+    char *result = NULL;
+    if(lex->token == TOKEN_STRING)
+    {
+        result = lex->value.string;
+        lex->value.string = NULL;
+    }
+    return result;
+}
+
 static int lex_init(lex_t *lex, get_func get, eof_func eof, void *data)
 {
     stream_init(&lex->stream, get, eof, data);
@@ -615,7 +625,7 @@ static json_t *parse_object(lex_t *lex, json_error_t *error)
             goto error;
         }
 
-        key = strdup(lex->value.string);
+        key = lex_steal_string(lex);
         if(!key)
             return NULL;
 
index fda0b03..b06cb4f 100644 (file)
@@ -107,6 +107,17 @@ static void json_delete_object(json_object_t *object)
     free(object);
 }
 
+unsigned int json_object_size(const json_t *json)
+{
+    json_object_t *object;
+
+    if(!json_is_object(json))
+        return -1;
+
+    object = json_to_object(json);
+    return object->hashtable.size;
+}
+
 json_t *json_object_get(const json_t *json, const char *key)
 {
     json_object_t *object;
@@ -118,23 +129,43 @@ json_t *json_object_get(const json_t *json, const char *key)
     return hashtable_get(&object->hashtable, key);
 }
 
-int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
+int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
 {
     json_object_t *object;
 
-    if(!json_is_object(json))
+    if(!key || !value)
         return -1;
 
+    if(!json_is_object(json))
+    {
+        json_decref(value);
+        return -1;
+    }
     object = json_to_object(json);
-    return hashtable_set(&object->hashtable, strdup(key), json_incref(value));
+
+    if(hashtable_set(&object->hashtable, strdup(key), value))
+    {
+        json_decref(value);
+        return -1;
+    }
+
+    return 0;
+}
+
+int json_object_set_nocheck(json_t *json, const char *key, json_t *value)
+{
+    return json_object_set_new_nocheck(json, key, json_incref(value));
 }
 
-int json_object_set(json_t *json, const char *key, json_t *value)
+int json_object_set_new(json_t *json, const char *key, json_t *value)
 {
-    if(!utf8_check_string(key, -1))
+    if(!key || !utf8_check_string(key, -1))
+    {
+        json_decref(value);
         return -1;
+    }
 
-    return json_object_set_nocheck(json, key, value);
+    return json_object_set_new_nocheck(json, key, value);
 }
 
 int json_object_del(json_t *json, const char *key)
@@ -148,6 +179,43 @@ int json_object_del(json_t *json, const char *key)
     return hashtable_del(&object->hashtable, key);
 }
 
+int json_object_clear(json_t *json)
+{
+    json_object_t *object;
+
+    if(!json_is_object(json))
+        return -1;
+
+    object = json_to_object(json);
+    hashtable_clear(&object->hashtable);
+
+    return 0;
+}
+
+int json_object_update(json_t *object, json_t *other)
+{
+    void *iter;
+
+    if(!json_is_object(object) || !json_is_object(other))
+        return -1;
+
+    iter = json_object_iter(other);
+    while(iter) {
+        const char *key;
+        json_t *value;
+
+        key = json_object_iter_key(iter);
+        value = json_object_iter_value(iter);
+
+        if(json_object_set(object, key, value))
+            return -1;
+
+        iter = json_object_iter_next(other, iter);
+    }
+
+    return 0;
+}
+
 void *json_object_iter(json_t *json)
 {
     json_object_t *object;
@@ -197,8 +265,13 @@ json_t *json_array(void)
     json_init(&array->json, JSON_ARRAY);
 
     array->entries = 0;
-    array->size = 0;
-    array->table = NULL;
+    array->size = 8;
+
+    array->table = malloc(array->size * sizeof(json_t *));
+    if(!array->table) {
+        free(array);
+        return NULL;
+    }
 
     return &array->json;
 }
@@ -235,59 +308,223 @@ json_t *json_array_get(const json_t *json, unsigned int index)
     return array->table[index];
 }
 
-int json_array_set(json_t *json, unsigned int index, json_t *value)
+int json_array_set_new(json_t *json, unsigned int index, json_t *value)
 {
     json_array_t *array;
+
+    if(!value)
+        return -1;
+
     if(!json_is_array(json))
+    {
+        json_decref(value);
         return -1;
+    }
     array = json_to_array(json);
 
     if(index >= array->entries)
+    {
+        json_decref(value);
         return -1;
+    }
 
     json_decref(array->table[index]);
-    array->table[index] = json_incref(value);
+    array->table[index] = value;
 
     return 0;
 }
 
-int json_array_append(json_t *json, json_t *value)
+static void array_move(json_array_t *array, unsigned int dest,
+                       unsigned int src, unsigned int count)
+{
+    memmove(&array->table[dest], &array->table[src], count * sizeof(json_t *));
+}
+
+static void array_copy(json_t **dest, unsigned int dpos,
+                       json_t **src, unsigned int spos,
+                       unsigned int count)
+{
+    memcpy(&dest[dpos], &src[spos], count * sizeof(json_t *));
+}
+
+static json_t **json_array_grow(json_array_t *array,
+                                unsigned int amount,
+                                int copy)
+{
+    unsigned int new_size;
+    json_t **old_table, **new_table;
+
+    if(array->entries + amount <= array->size)
+        return array->table;
+
+    old_table = array->table;
+
+    new_size = max(array->size + amount, array->size * 2);
+    new_table = malloc(new_size * sizeof(json_t *));
+    if(!new_table)
+        return NULL;
+
+    array->size = new_size;
+    array->table = new_table;
+
+    if(copy) {
+        array_copy(array->table, 0, old_table, 0, array->entries);
+        free(old_table);
+        return array->table;
+    }
+
+    return old_table;
+}
+
+int json_array_append_new(json_t *json, json_t *value)
 {
     json_array_t *array;
+
+    if(!value)
+        return -1;
+
     if(!json_is_array(json))
+    {
+        json_decref(value);
         return -1;
+    }
     array = json_to_array(json);
 
-    if(array->entries == array->size) {
-        array->size = max(8, array->size * 2);
-        array->table = realloc(array->table, array->size * sizeof(json_t *));
-        if(!array->table)
-            return -1;
+    if(!json_array_grow(array, 1, 1)) {
+        json_decref(value);
+        return -1;
+    }
+
+    array->table[array->entries] = value;
+    array->entries++;
+
+    return 0;
+}
+
+int json_array_insert_new(json_t *json, unsigned int index, json_t *value)
+{
+    json_array_t *array;
+    json_t **old_table;
+
+    if(!value)
+        return -1;
+
+    if(!json_is_array(json)) {
+        json_decref(value);
+        return -1;
+    }
+    array = json_to_array(json);
+
+    if(index > array->entries) {
+        json_decref(value);
+        return -1;
+    }
+
+    old_table = json_array_grow(array, 1, 0);
+    if(!old_table) {
+        json_decref(value);
+        return -1;
     }
 
-    array->table[array->entries] = json_incref(value);
+    if(old_table != array->table) {
+        array_copy(array->table, 0, old_table, 0, index);
+        array_copy(array->table, index + 1, old_table, index,
+                   array->entries - index);
+        free(old_table);
+    }
+    else
+        array_move(array, index + 1, index, array->entries - index);
+
+    array->table[index] = value;
     array->entries++;
 
     return 0;
 }
 
+int json_array_remove(json_t *json, unsigned int index)
+{
+    json_array_t *array;
+
+    if(!json_is_array(json))
+        return -1;
+    array = json_to_array(json);
+
+    if(index >= array->entries)
+        return -1;
+
+    json_decref(array->table[index]);
+
+    array_move(array, index, index + 1, array->entries - index);
+    array->entries--;
+
+    return 0;
+}
+
+int json_array_clear(json_t *json)
+{
+    json_array_t *array;
+    unsigned int i;
+
+    if(!json_is_array(json))
+        return -1;
+    array = json_to_array(json);
+
+    for(i = 0; i < array->entries; i++)
+        json_decref(array->table[i]);
+
+    array->entries = 0;
+    return 0;
+}
+
+int json_array_extend(json_t *json, json_t *other_json)
+{
+    json_array_t *array, *other;
+    unsigned int i;
+
+    if(!json_is_array(json) || !json_is_array(other_json))
+        return -1;
+    array = json_to_array(json);
+    other = json_to_array(other_json);
+
+    if(!json_array_grow(array, other->entries, 1))
+        return -1;
+
+    for(i = 0; i < other->entries; i++)
+        json_incref(other->table[i]);
+
+    array_copy(array->table, array->entries, other->table, 0, other->entries);
+
+    array->entries += other->entries;
+    return 0;
+}
+
 
 /*** string ***/
 
 json_t *json_string_nocheck(const char *value)
 {
-    json_string_t *string = malloc(sizeof(json_string_t));
+    json_string_t *string;
+
+    if(!value)
+        return NULL;
+
+    string = malloc(sizeof(json_string_t));
     if(!string)
        return NULL;
     json_init(&string->json, JSON_STRING);
 
     string->value = strdup(value);
+    if(!string->value) {
+        free(string);
+        return NULL;
+    }
+
     return &string->json;
 }
 
 json_t *json_string(const char *value)
 {
-    if(!utf8_check_string(value, -1))
+    if(!value || !utf8_check_string(value, -1))
         return NULL;
 
     return json_string_nocheck(value);
@@ -381,9 +618,9 @@ json_t *json_true(void)
 {
     static json_t the_true = {
         .type = JSON_TRUE,
-        .refcount = 1
+        .refcount = (unsigned int)1
     };
-    return json_incref(&the_true);
+    return &the_true;
 }
 
 
@@ -391,9 +628,9 @@ json_t *json_false(void)
 {
     static json_t the_false = {
         .type = JSON_FALSE,
-        .refcount = 1
+        .refcount = (unsigned int)1
     };
-    return json_incref(&the_false);
+    return &the_false;
 }
 
 
@@ -401,9 +638,9 @@ json_t *json_null(void)
 {
     static json_t the_null = {
         .type = JSON_NULL,
-        .refcount = 1
+        .refcount = (unsigned int)1
     };
-    return json_incref(&the_null);
+    return &the_null;
 }
 
 
index a31f16f..09dfb4d 100644 (file)
@@ -5,3 +5,4 @@ testlogs
 testprogs/test_array
 testprogs/test_number
 testprogs/test_object
+testprogs/test_simple
index a3ff738..ffe653c 100644 (file)
@@ -44,7 +44,10 @@ for testfile in $TESTFILES; do
     tmpdir="testlogs/`basename $testfile`"
     rm -rf $tmpdir
     mkdir -p $tmpdir
-    ${srcdir}/split-testfile.py $testfile $tmpdir | while read name; do
+    if echo "$testfile" | grep -q -E -e '-strip$'; then
+        opts="--strip"
+    fi
+    ${srcdir}/split-testfile.py $opts $testfile $tmpdir | while read name; do
         run_test loadf_dumpf $tmpdir/$name
         run_test loads_dumps $tmpdir/$name
         run_test load_file_dump_file $tmpdir/$name
index 0191302..97a489e 100755 (executable)
@@ -7,6 +7,13 @@
 
 import os
 import sys
+from optparse import OptionParser
+
+def strip_file(filename):
+    with open(filename) as fobj:
+        data = fobj.read()
+    with open(filename, 'w') as fobj:
+        fobj.write(data.strip())
 
 def open_files(outdir, i, name):
     basename = '%02d_%s' % (i, name)
@@ -16,12 +23,17 @@ def open_files(outdir, i, name):
     return open(input_path, 'w'), open(output_path, 'w')
 
 def main():
-    if len(sys.argv) != 3:
-        print 'usage: %s input-file output-directory' % sys.argv[0]
+    parser = OptionParser('usage: %prog [options] inputfile outputdir')
+    parser.add_option('--strip', help='strip whitespace from input',
+                      action='store_true', default=False)
+    options, args = parser.parse_args()
+
+    if len(args) != 2:
+        parser.print_help()
         return 2
 
-    infile = os.path.normpath(sys.argv[1])
-    outdir = os.path.normpath(sys.argv[2])
+    infile = os.path.normpath(args[0])
+    outdir = os.path.normpath(args[1])
 
     if not os.path.exists(outdir):
         print >>sys.stderr, 'output directory %r does not exist!' % outdir
@@ -37,6 +49,8 @@ def main():
             if input is not None and output is not None:
                 input.close()
                 output.close()
+                if options.strip:
+                    strip_file(input.name)
             input, output = open_files(outdir, n, line[5:line.find(' ====\n')])
             current = input
         elif line == '====\n':
index 60c4cca..b83c2e7 100755 (executable)
@@ -5,7 +5,7 @@
 # Jansson is free software; you can redistribute it and/or modify
 # it under the terms of the MIT license. See LICENSE for details.
 
-TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-unicode"
+TESTFILES="${srcdir}/testdata/invalid ${srcdir}/testdata/invalid-strip ${srcdir}/testdata/invalid-unicode"
 
 run_test() {
     local prog=$1
index 55489ea..46d4edf 100755 (executable)
@@ -5,7 +5,7 @@
 # Jansson is free software; you can redistribute it and/or modify
 # it under the terms of the MIT license. See LICENSE for details.
 
-TESTFILES="${srcdir}/testdata/valid"
+TESTFILES="${srcdir}/testdata/valid ${srcdir}/testdata/valid-strip"
 
 run_test() {
     local prog=$1
diff --git a/test/testdata/invalid-strip b/test/testdata/invalid-strip
new file mode 100644 (file)
index 0000000..8b4a574
--- /dev/null
@@ -0,0 +1,220 @@
+==== empty ====
+====
+1
+'[' or '{' expected near end of file
+==== null ====
+null
+====
+1
+'[' or '{' expected near 'null'
+==== lone-open-brace ====
+{
+====
+1
+string or '}' expected near end of file
+==== lone-open-bracket ====
+[
+====
+1
+']' expected near end of file
+==== bracket-comma ====
+[,
+====
+1
+unexpected token near ','
+==== bracket-one-comma ====
+[1,
+====
+1
+']' expected near end of file
+==== unterminated-string ====
+["a
+====
+1
+premature end of input near '"a'
+==== unterminated-array ====
+["a"
+====
+1
+']' expected near end of file
+==== apostrophe ====
+['
+====
+1
+invalid token near '''
+==== brace-comma ====
+{,
+====
+1
+string or '}' expected near ','
+==== unterminated-empty-key ====
+{"
+====
+1
+premature end of input near '"'
+==== unterminated-key ====
+{"a
+====
+1
+premature end of input near '"a'
+==== object-no-colon ====
+{"a"
+====
+1
+':' expected near end of file
+==== object-apostrophes ====
+{'a'
+====
+1
+string or '}' expected near '''
+==== object-no-value ====
+{"a":
+====
+1
+unexpected token near end of file
+==== object-unterminated-value ====
+{"a":"a
+====
+1
+premature end of input near '"a'
+==== object-garbage-at-end ====
+{"a":"a" 123}
+====
+1
+'}' expected near '123'
+==== unterminated-object-and-array ====
+{[
+====
+1
+string or '}' expected near '['
+==== unterminated-array-and-object ====
+[{
+====
+1
+string or '}' expected near end of file
+==== object-in-unterminated-array ====
+[{}
+====
+1
+']' expected near end of file
+==== extra-comma-in-array ====
+[1,]
+====
+1
+unexpected token near ']'
+==== extra-command-in-multiline-array ====
+[1,
+2,
+3,
+4,
+5,
+]
+====
+6
+unexpected token near ']'
+==== real-truncated-at-point ====
+[1.]
+====
+1
+invalid token near '1.'
+==== real-truncated-at-e ====
+[1e]
+====
+1
+invalid token near '1e'
+==== real-garbage-after-e ====
+[1ea]
+====
+1
+invalid token near '1e'
+==== real-positive-overflow ====
+[123123e100000]
+====
+1
+real number overflow near '123123e100000'
+==== real-negative-overflow ====
+[-123123e100000]
+====
+1
+real number overflow near '-123123e100000'
+==== real-underflow ====
+[123e-10000000]
+====
+1
+real number underflow near '123e-10000000'
+==== integer-starting-with-zero ====
+[012]
+====
+1
+invalid token near '0'
+==== negative-integer-starting-with-zero ====
+[-012]
+====
+1
+invalid token near '-0'
+==== too-big-positive-integer ====
+[123123123123123]
+====
+1
+too big integer near '123123123123123'
+==== too-big-negative-integer ====
+[-123123123123123]
+====
+1
+too big negative integer near '-123123123123123'
+==== invalid-identifier ====
+[troo
+====
+1
+invalid token near 'troo'
+==== invalid-escap ====
+["\a <-- invalid escape"]
+====
+1
+invalid escape near '"\'
+==== tab-character-in-string ====
+["      <-- tab character"]
+====
+1
+control character 0x9 near '"'
+==== null-byte-in-string ====
+["\u0000 (null byte not allowed)"]
+====
+1
+\u0000 is not allowed
+==== truncated-unicode-surrogate ====
+["\uDADA (first surrogate without the second)"]
+====
+1
+invalid Unicode '\uDADA'
+==== invalid-second-surrogate ====
+["\uD888\u3210 (first surrogate and invalid second surrogate)"]
+====
+1
+invalid Unicode '\uD888\u3210'
+==== lone-second-surrogate ====
+["\uDFAA (second surrogate on it's own)"]
+====
+1
+invalid Unicode '\uDFAA'
+==== unicode-identifier ====
+====
+1
+'[' or '{' expected near 'å'
+==== ascii-unicode-identifier ====
+aå
+====
+1
+'[' or '{' expected near 'a'
+==== garbage-at-the-end ====
+[1,2,3]foo
+====
+1
+end of file expected near 'foo'
+==== garbage-after-newline ====
+[1,2,3]
+foo
+====
+2
+end of file expected near 'foo'
diff --git a/test/testdata/valid-strip b/test/testdata/valid-strip
new file mode 100644 (file)
index 0000000..50cfc63
--- /dev/null
@@ -0,0 +1,68 @@
+==== empty-string ====
+[""]
+==== short-string ====
+["a"]
+==== simple-ascii-string ====
+["abcdefghijklmnopqrstuvwxyz1234567890 "]
+==== utf-8-string ====
+["€þıœəßð some utf-8 ĸʒ×ŋµåäö𝄞"]
+==== string-escapes ====
+["\"\\\/\b\f\n\r\t"]
+==== one-byte-utf-8 ====
+["\u002c one-byte UTF-8"]
+==== two-byte-utf-8 ====
+["\u0123 two-byte UTF-8"]
+==== three-byte-utf-8 ====
+["\u0821 three-byte UTF-8"]
+==== utf-surrogate-four-byte-encoding ====
+["\uD834\uDD1E surrogate, four-byte UTF-8"]
+==== escaped-utf-control-char ====
+["\u0012 escaped control character"]
+==== simple-int-0 ====
+[0]
+==== simple-int-1 ====
+[1]
+==== simple-int-123 ====
+[123]
+==== negative-zero ====
+[-0]
+==== negative-one ====
+[-1]
+==== negative-int ====
+[-123]
+==== simple-real ====
+[123.456789]
+==== real-exponent ====
+[123e45]
+==== real-capital-e ====
+[1E22]
+==== real-positive-exponent ====
+[1e+2]
+==== real-negative-exponent ====
+[1e-2]
+==== real-capital-e-positive-exponent ====
+[1E+2]
+==== real-capital-e-negative-exponent ====
+[1E-2]
+==== real-fraction-exponent ====
+[123.456e78]
+==== true ====
+[true]
+==== false ====
+[false]
+==== null ====
+[null]
+==== empty-array ====
+[]
+==== empty-object-in-array ====
+[{}]
+==== complex-array ====
+[1,2,3,4,
+"a", "b", "c",
+{"foo": "bar", "core": "dump"},
+true, false, true, true, null, false
+]
+==== empty-object ====
+{}
+==== simple-object ====
+{"a":[]}
index 57f0ded..400a998 100644 (file)
@@ -1,6 +1,7 @@
-check_PROGRAMS = test_array test_number test_object
+check_PROGRAMS = test_array test_simple test_number test_object
 
 test_array_SOURCES = test_array.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
 
index 0ebc3d5..53f6f3d 100644 (file)
@@ -8,7 +8,7 @@
 #include <jansson.h>
 #include "util.h"
 
-int main()
+static void test_misc(void)
 {
     json_t *array, *five, *seven, *value;
     int i;
@@ -19,14 +19,15 @@ int main()
 
     if(!array)
         fail("unable to create array");
-    if(!five)
-        fail("unable to create integer");
-    if(!seven)
+    if(!five || !seven)
         fail("unable to create integer");
 
     if(json_array_size(array) != 0)
         fail("empty array has nonzero size");
 
+    if(!json_array_append(array, NULL))
+        fail("able to append NULL");
+
     if(json_array_append(array, five))
         fail("unable to append");
 
@@ -54,6 +55,9 @@ int main()
     if(json_array_set(array, 0, seven))
         fail("unable to set value");
 
+    if(!json_array_set(array, 0, NULL))
+        fail("able to set NULL");
+
     if(json_array_size(array) != 2)
         fail("wrong array size");
 
@@ -85,9 +89,265 @@ int main()
             fail("got wrong value");
     }
 
+    if(json_array_set_new(array, 15, json_integer(123)))
+        fail("unable to set new value");
+
+    value = json_array_get(array, 15);
+    if(!json_is_integer(value) || json_integer_value(value) != 123)
+      fail("json_array_set_new works incorrectly");
+
+    if(!json_array_set_new(array, 15, NULL))
+        fail("able to set_new NULL value");
+
+    if(json_array_append_new(array, json_integer(321)))
+        fail("unable to append new value");
+
+    value = json_array_get(array, json_array_size(array) - 1);
+    if(!json_is_integer(value) || json_integer_value(value) != 321)
+      fail("json_array_append_new works incorrectly");
+
+    if(!json_array_append_new(array, NULL))
+        fail("able to append_new NULL value");
+
     json_decref(five);
     json_decref(seven);
     json_decref(array);
+}
+
+static void test_insert(void)
+{
+    json_t *array, *five, *seven, *eleven, *value;
+    int i;
+
+    array = json_array();
+    five = json_integer(5);
+    seven = json_integer(7);
+    eleven = json_integer(11);
+
+    if(!array)
+        fail("unable to create array");
+    if(!five || !seven || !eleven)
+        fail("unable to create integer");
+
+
+    if(!json_array_insert(array, 1, five))
+        fail("able to insert value out of bounds");
+
+
+    if(json_array_insert(array, 0, five))
+        fail("unable to insert value in an empty array");
+
+    if(json_array_get(array, 0) != five)
+        fail("json_array_insert works incorrectly");
+
+    if(json_array_size(array) != 1)
+        fail("array size is invalid after insertion");
+
+
+    if(json_array_insert(array, 1, seven))
+        fail("unable to insert value at the end of an array");
+
+    if(json_array_get(array, 0) != five)
+        fail("json_array_insert works incorrectly");
+
+    if(json_array_get(array, 1) != seven)
+        fail("json_array_insert works incorrectly");
+
+    if(json_array_size(array) != 2)
+        fail("array size is invalid after insertion");
+
+
+    if(json_array_insert(array, 1, eleven))
+        fail("unable to insert value in the middle of an array");
+
+    if(json_array_get(array, 0) != five)
+        fail("json_array_insert works incorrectly");
+
+    if(json_array_get(array, 1) != eleven)
+        fail("json_array_insert works incorrectly");
+
+    if(json_array_get(array, 2) != seven)
+        fail("json_array_insert works incorrectly");
+
+    if(json_array_size(array) != 3)
+        fail("array size is invalid after insertion");
+
+
+    if(json_array_insert_new(array, 2, json_integer(123)))
+        fail("unable to insert value in the middle of an array");
+
+    value = json_array_get(array, 2);
+    if(!json_is_integer(value) || json_integer_value(value) != 123)
+        fail("json_array_insert_new works incorrectly");
+
+    if(json_array_size(array) != 4)
+        fail("array size is invalid after insertion");
+
+
+    for(i = 0; i < 20; i++) {
+        if(json_array_insert(array, 0, seven))
+            fail("unable to insert value at the begining of an array");
+    }
+
+    for(i = 0; i < 20; i++) {
+        if(json_array_get(array, i) != seven)
+            fail("json_aray_insert works incorrectly");
+    }
+
+    if(json_array_size(array) != 24)
+        fail("array size is invalid after loop insertion");
+
+    json_decref(five);
+    json_decref(seven);
+    json_decref(eleven);
+    json_decref(array);
+}
+
+static void test_remove(void)
+{
+    json_t *array, *five, *seven;
+
+    array = json_array();
+    five = json_integer(5);
+    seven = json_integer(7);
+
+    if(!array)
+        fail("unable to create array");
+    if(!five)
+        fail("unable to create integer");
+    if(!seven)
+        fail("unable to create integer");
+
+
+    if(!json_array_remove(array, 0))
+        fail("able to remove an unexisting index");
+
+
+    if(json_array_append(array, five))
+        fail("unable to append");
+
+    if(!json_array_remove(array, 1))
+        fail("able to remove an unexisting index");
+
+    if(json_array_remove(array, 0))
+        fail("unable to remove");
+
+    if(json_array_size(array) != 0)
+        fail("array size is invalid after removing");
+
+
+    if(json_array_append(array, five) ||
+       json_array_append(array, seven) ||
+       json_array_append(array, five) ||
+       json_array_append(array, seven))
+        fail("unable to append");
+
+    if(json_array_remove(array, 2))
+        fail("unable to remove");
+
+    if(json_array_size(array) != 3)
+        fail("array size is invalid after removing");
+
+    if(json_array_get(array, 0) != five ||
+       json_array_get(array, 1) != seven ||
+       json_array_get(array, 2) != seven)
+        fail("remove works incorrectly");
+
+    json_decref(five);
+    json_decref(seven);
+    json_decref(array);
+}
+
+static void test_clear(void)
+{
+    json_t *array, *five, *seven;
+    int i;
+
+    array = json_array();
+    five = json_integer(5);
+    seven = json_integer(7);
+
+    if(!array)
+        fail("unable to create array");
+    if(!five || !seven)
+        fail("unable to create integer");
+
+    for(i = 0; i < 10; i++) {
+        if(json_array_append(array, five))
+            fail("unable to append");
+    }
+    for(i = 0; i < 10; i++) {
+        if(json_array_append(array, seven))
+            fail("unable to append");
+    }
+
+    if(json_array_size(array) != 20)
+        fail("array size is invalid after appending");
+
+    if(json_array_clear(array))
+        fail("unable to clear");
+
+    if(json_array_size(array) != 0)
+        fail("array size is invalid after clearing");
+
+    json_decref(five);
+    json_decref(seven);
+    json_decref(array);
+}
+
+static void test_extend(void)
+{
+    json_t *array1, *array2, *five, *seven;
+    int i;
+
+    array1 = json_array();
+    array2 = json_array();
+    five = json_integer(5);
+    seven = json_integer(7);
+
+    if(!array1 || !array2)
+        fail("unable to create array");
+    if(!five || !seven)
+        fail("unable to create integer");
+
+    for(i = 0; i < 10; i++) {
+        if(json_array_append(array1, five))
+            fail("unable to append");
+    }
+    for(i = 0; i < 10; i++) {
+        if(json_array_append(array2, seven))
+            fail("unable to append");
+    }
+
+    if(json_array_size(array1) != 10 || json_array_size(array2) != 10)
+        fail("array size is invalid after appending");
+
+    if(json_array_extend(array1, array2))
+        fail("unable to extend");
+
+    for(i = 0; i < 10; i++) {
+        if(json_array_get(array1, i) != five)
+            fail("invalid array contents after extending");
+    }
+    for(i = 10; i < 20; i++) {
+        if(json_array_get(array1, i) != seven)
+            fail("invalid array contents after extending");
+    }
+
+    json_decref(five);
+    json_decref(seven);
+    json_decref(array1);
+    json_decref(array2);
+}
+
+
+int main()
+{
+    test_misc();
+    test_insert();
+    test_remove();
+    test_clear();
+    test_extend();
 
     return 0;
 }
index 9f72374..3be49e5 100644 (file)
  */
 
 #include <jansson.h>
+#include <string.h>
 #include "util.h"
 
-int main()
+static void test_clear()
+{
+    json_t *object, *ten;
+
+    object = json_object();
+    ten = json_integer(10);
+
+    if(!object)
+        fail("unable to create object");
+    if(!ten)
+        fail("unable to create integer");
+
+    if(json_object_set(object, "a", ten) ||
+       json_object_set(object, "b", ten) ||
+       json_object_set(object, "c", ten) ||
+       json_object_set(object, "d", ten) ||
+       json_object_set(object, "e", ten))
+        fail("unable to set value");
+
+    if(json_object_size(object) != 5)
+        fail("invalid size");
+
+    json_object_clear(object);
+
+    if(json_object_size(object) != 0)
+        fail("invalid size after clear");
+
+    json_decref(ten);
+    json_decref(object);
+}
+
+static void test_update()
+{
+    json_t *object, *other, *nine, *ten;
+
+    object = json_object();
+    other = json_object();
+
+    nine = json_integer(9);
+    ten = json_integer(10);
+
+    if(!object || !other)
+        fail("unable to create object");
+    if(!nine || !ten)
+        fail("unable to create integer");
+
+
+    /* update an empty object with an empty object */
+
+    if(json_object_update(object, other))
+        fail("unable to update an emtpy object with an empty object");
+
+    if(json_object_size(object) != 0)
+        fail("invalid size after update");
+
+    if(json_object_size(other) != 0)
+        fail("invalid size for updater after update");
+
+
+    /* update an empty object with a nonempty object */
+
+    if(json_object_set(other, "a", ten) ||
+       json_object_set(other, "b", ten) ||
+       json_object_set(other, "c", ten) ||
+       json_object_set(other, "d", ten) ||
+       json_object_set(other, "e", ten))
+        fail("unable to set value");
+
+    if(json_object_update(object, other))
+        fail("unable to update an empty object");
+
+    if(json_object_size(object) != 5)
+        fail("invalid size after update");
+
+    if(json_object_get(object, "a") != ten ||
+       json_object_get(object, "b") != ten ||
+       json_object_get(object, "c") != ten ||
+       json_object_get(object, "d") != ten ||
+       json_object_get(object, "e") != ten)
+        fail("update works incorrectly");
+
+
+    /* perform the same update again */
+
+    if(json_object_update(object, other))
+        fail("unable to update an empty object");
+
+    if(json_object_size(object) != 5)
+        fail("invalid size after update");
+
+    if(json_object_get(object, "a") != ten ||
+       json_object_get(object, "b") != ten ||
+       json_object_get(object, "c") != ten ||
+       json_object_get(object, "d") != ten ||
+       json_object_get(object, "e") != ten)
+        fail("update works incorrectly");
+
+
+    /* update a nonempty object with a nonempty object with both old
+       and new keys */
+
+    if(json_object_clear(other))
+        fail("clear failed");
+
+    if(json_object_set(other, "a", nine) ||
+       json_object_set(other, "b", nine) ||
+       json_object_set(other, "f", nine) ||
+       json_object_set(other, "g", nine) ||
+       json_object_set(other, "h", nine))
+        fail("unable to set value");
+
+    if(json_object_update(object, other))
+        fail("unable to update a nonempty object");
+
+    if(json_object_size(object) != 8)
+        fail("invalid size after update");
+
+    if(json_object_get(object, "a") != nine ||
+       json_object_get(object, "b") != nine ||
+       json_object_get(object, "f") != nine ||
+       json_object_get(object, "g") != nine ||
+       json_object_get(object, "h") != nine)
+        fail("update works incorrectly");
+
+    json_decref(nine);
+    json_decref(ten);
+    json_decref(other);
+    json_decref(object);
+}
+
+static void test_misc()
 {
     json_t *object, *string, *other_string, *value;
+    void *iter;
 
     object = json_object();
     string = json_string("test");
@@ -18,9 +150,7 @@ int main()
 
     if(!object)
         fail("unable to create object");
-    if(!string)
-        fail("unable to create string");
-    if(!other_string)
+    if(!string || !other_string)
         fail("unable to create string");
 
     if(json_object_get(object, "a"))
@@ -29,6 +159,23 @@ int main()
     if(json_object_set(object, "a", string))
         fail("unable to set value");
 
+    if(!json_object_set(object, NULL, string))
+        fail("able to set NULL key");
+
+    if(!json_object_set(object, "a", NULL))
+        fail("able to set NULL value");
+
+    iter = json_object_iter(object);
+    if(!iter)
+        fail("unable to get iterator");
+
+    if(strcmp(json_object_iter_key(iter), "a"))
+        fail("iterating failed: wrong key");
+    if(json_object_iter_value(iter) != string)
+        fail("iterating failed: wrong value");
+    if(json_object_iter_next(object, iter) != NULL)
+        fail("able to iterate over the end");
+
     /* invalid UTF-8 in key */
     if(!json_object_set(object, "a\xefz", string))
         fail("able to set invalid unicode key");
@@ -39,7 +186,7 @@ int main()
     if(value != string)
         fail("got different value than what was added");
 
-    /* "a", "lp" and "px" collide with a five-bucket hashtable */
+    /* "a", "lp" and "px" collide in a five-bucket hashtable */
     if(json_object_set(object, "b", string) ||
        json_object_set(object, "lp", string) ||
        json_object_set(object, "px", string))
@@ -73,7 +220,7 @@ int main()
         fail("unable to delete an existing key");
 
 
-    /* add many keys to  rehashing */
+    /* add many keys to initiate rehashing */
 
     if(json_object_set(object, "a", string))
         fail("unable to set value");
@@ -93,9 +240,30 @@ int main()
     if(json_object_set(object, "e", string))
         fail("unable to set value");
 
+
+    if(json_object_set_new(object, "foo", json_integer(123)))
+        fail("unable to set new value");
+
+    value = json_object_get(object, "foo");
+    if(!json_is_integer(value) || json_integer_value(value) != 123)
+      fail("json_object_set_new works incorrectly");
+
+    if(!json_object_set_new(object, NULL, json_integer(432)))
+        fail("able to set_new NULL key");
+
+    if(!json_object_set_new(object, "foo", NULL))
+        fail("able to set_new NULL value");
+
     json_decref(string);
     json_decref(other_string);
     json_decref(object);
+}
+
+int main()
+{
+    test_misc();
+    test_clear();
+    test_update();
 
     return 0;
 }
diff --git a/test/testprogs/test_simple.c b/test/testprogs/test_simple.c
new file mode 100644 (file)
index 0000000..4491ed2
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009 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 <string.h>
+#include <jansson.h>
+#include "util.h"
+
+/* Call the simple functions not covered by other tests of the public API */
+int main()
+{
+    json_t *value;
+
+    value = json_integer(1);
+    if(json_typeof(value) != JSON_INTEGER)
+        fail("json_typeof failed");
+
+    if(json_is_object(value))
+        fail("json_is_object failed");
+
+    if(json_is_array(value))
+        fail("json_is_array failed");
+
+    if(json_is_string(value))
+        fail("json_is_string failed");
+
+    if(!json_is_integer(value))
+        fail("json_is_integer failed");
+
+    if(json_is_real(value))
+        fail("json_is_real failed");
+
+    if(!json_is_number(value))
+        fail("json_is_number failed");
+
+    if(json_is_true(value))
+        fail("json_is_true failed");
+
+    if(json_is_false(value))
+        fail("json_is_false failed");
+
+    if(json_is_boolean(value))
+        fail("json_is_boolean failed");
+
+    if(json_is_null(value))
+        fail("json_is_null failed");
+
+    json_decref(value);
+
+
+    value = json_string("foo");
+    if(!value)
+        fail("json_string failed");
+    if(strcmp(json_string_value(value), "foo"))
+        fail("invalid string value");
+    json_decref(value);
+
+    value = json_string(NULL);
+    if(value)
+        fail("json_string(NULL) failed");
+
+    value = json_integer(123);
+    if(!value)
+        fail("json_integer failed");
+    if(json_integer_value(value) != 123)
+        fail("invalid integer value");
+    if(json_number_value(value) != 123.0)
+        fail("invalid number value");
+    json_decref(value);
+
+    value = json_real(123.123);
+    if(!value)
+        fail("json_real failed");
+    if(json_real_value(value) != 123.123)
+        fail("invalid integer value");
+    if(json_number_value(value) != 123.123)
+        fail("invalid number value");
+    json_decref(value);
+
+    value = json_true();
+    if(!value)
+        fail("json_true failed");
+    json_decref(value);
+
+    value = json_false();
+    if(!value)
+        fail("json_false failed");
+    json_decref(value);
+
+    value = json_null();
+    if(!value)
+        fail("json_null failed");
+    json_decref(value);
+
+    return 0;
+}
index 6e518a2..ec8e0e4 100644 (file)
@@ -8,10 +8,13 @@
 #ifndef TESTPROGS_UTIL_H
 #define TESTPROGS_UTIL_H
 
+#include <stdlib.h>
+
 #define fail(msg)                                                \
     do {                                                         \
-        fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \
-        return 1;                                                \
+        fprintf(stderr, "%s:%s:%d: %s\n",                        \
+                __FILE__, __FUNCTION__, __LINE__, msg);          \
+        exit(1);                                                 \
     } while(0)
 
 #endif