Browse Source

Update included jansson to v2.4

Con Kolivas 12 years ago
parent
commit
647dc35fe6

+ 8 - 8
compat/jansson/Makefile.am

@@ -1,21 +1,21 @@
+EXTRA_DIST = jansson.def
+
+include_HEADERS = jansson.h jansson_config.h
 
 
 noinst_LIBRARIES	= libjansson.a
 noinst_LIBRARIES	= libjansson.a
 
 
 libjansson_a_SOURCES	= \
 libjansson_a_SOURCES	= \
-			  config.h		\
-			  jansson_config.h	\
 			  dump.c		\
 			  dump.c		\
+			  error.c		\
 			  hashtable.c		\
 			  hashtable.c		\
 			  hashtable.h		\
 			  hashtable.h		\
-			  jansson.h		\
 			  jansson_private.h	\
 			  jansson_private.h	\
 			  load.c		\
 			  load.c		\
+			  memory.c		\
+			  pack_unpack.c		\
 			  strbuffer.c		\
 			  strbuffer.c		\
 			  strbuffer.h		\
 			  strbuffer.h		\
+			  strconv.c 		\
 			  utf.c			\
 			  utf.c			\
 			  utf.h			\
 			  utf.h			\
-			  util.h		\
-			  value.c		\
-			  memory.c		\
-			  error.c
-
+			  value.c

+ 0 - 73
compat/jansson/config.h

@@ -1,73 +0,0 @@
-/* config.h.  Generated from config.h.in by configure.  */
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#define HAVE_DLFCN_H 1
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#define HAVE_INTTYPES_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#define HAVE_STDINT_H 1
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-#define HAVE_STRINGS_H 1
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#define HAVE_UNISTD_H 1
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#define LT_OBJDIR ".libs/"
-
-/* Name of package */
-#define PACKAGE "jansson"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT "petri@digip.org"
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "jansson"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "jansson 1.3"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "jansson"
-
-/* Define to the home page for this package. */
-#define PACKAGE_URL ""
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "1.3"
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "1.3"
-
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-/* #undef inline */
-#endif
-
-/* Define to the type of a signed integer type of width exactly 32 bits if
-   such a type exists and the standard includes do not define it. */
-/* #undef int32_t */

+ 51 - 66
compat/jansson/dump.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -11,7 +11,7 @@
 #include <string.h>
 #include <string.h>
 #include <assert.h>
 #include <assert.h>
 
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "jansson_private.h"
 #include "strbuffer.h"
 #include "strbuffer.h"
 #include "utf.h"
 #include "utf.h"
@@ -19,21 +19,17 @@
 #define MAX_INTEGER_STR_LENGTH  100
 #define MAX_INTEGER_STR_LENGTH  100
 #define MAX_REAL_STR_LENGTH     100
 #define MAX_REAL_STR_LENGTH     100
 
 
-typedef int (*dump_func)(const char *buffer, int size, void *data);
-
-struct string
-{
-    char *buffer;
-    int length;
-    int size;
+struct object_key {
+    size_t serial;
+    const char *key;
 };
 };
 
 
-static int dump_to_strbuffer(const char *buffer, int size, void *data)
+static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
 {
 {
     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
     return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
 }
 }
 
 
-static int dump_to_file(const char *buffer, int size, void *data)
+static int dump_to_file(const char *buffer, size_t size, void *data)
 {
 {
     FILE *dest = (FILE *)data;
     FILE *dest = (FILE *)data;
     if(fwrite(buffer, size, 1, dest) != 1)
     if(fwrite(buffer, size, 1, dest) != 1)
@@ -44,7 +40,7 @@ static int dump_to_file(const char *buffer, int size, void *data)
 /* 32 spaces (the maximum indentation size) */
 /* 32 spaces (the maximum indentation size) */
 static char whitespace[] = "                                ";
 static char whitespace[] = "                                ";
 
 
-static int dump_indent(size_t flags, int depth, int space, dump_func dump, void *data)
+static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
 {
 {
     if(JSON_INDENT(flags) > 0)
     if(JSON_INDENT(flags) > 0)
     {
     {
@@ -66,7 +62,7 @@ static int dump_indent(size_t flags, int depth, int space, dump_func dump, void
     return 0;
     return 0;
 }
 }
 
 
-static int dump_string(const char *str, int ascii, dump_func dump, void *data)
+static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags)
 {
 {
     const char *pos, *end;
     const char *pos, *end;
     int32_t codepoint;
     int32_t codepoint;
@@ -91,8 +87,12 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
             if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
             if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
                 break;
                 break;
 
 
+            /* slash */
+            if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
+                break;
+
             /* non-ASCII */
             /* non-ASCII */
-            if(ascii && codepoint > 0x7F)
+            if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
                 break;
                 break;
 
 
             pos = end;
             pos = end;
@@ -106,7 +106,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
         if(end == pos)
         if(end == pos)
             break;
             break;
 
 
-        /* handle \, ", and control codes */
+        /* handle \, /, ", and control codes */
         length = 2;
         length = 2;
         switch(codepoint)
         switch(codepoint)
         {
         {
@@ -117,6 +117,7 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
             case '\n': text = "\\n"; break;
             case '\n': text = "\\n"; break;
             case '\r': text = "\\r"; break;
             case '\r': text = "\\r"; break;
             case '\t': text = "\\t"; break;
             case '\t': text = "\\t"; break;
+            case '/':  text = "\\/"; break;
             default:
             default:
             {
             {
                 /* codepoint is in BMP */
                 /* codepoint is in BMP */
@@ -155,21 +156,21 @@ static int dump_string(const char *str, int ascii, dump_func dump, void *data)
 
 
 static int object_key_compare_keys(const void *key1, const void *key2)
 static int object_key_compare_keys(const void *key1, const void *key2)
 {
 {
-    return strcmp((*(const object_key_t **)key1)->key,
-                  (*(const object_key_t **)key2)->key);
+    return strcmp(((const struct object_key *)key1)->key,
+                  ((const struct object_key *)key2)->key);
 }
 }
 
 
 static int object_key_compare_serials(const void *key1, const void *key2)
 static int object_key_compare_serials(const void *key1, const void *key2)
 {
 {
-    return (*(const object_key_t **)key1)->serial -
-           (*(const object_key_t **)key2)->serial;
+    size_t a = ((const struct object_key *)key1)->serial;
+    size_t b = ((const struct object_key *)key2)->serial;
+
+    return a < b ? -1 : a == b ? 0 : 1;
 }
 }
 
 
 static int do_dump(const json_t *json, size_t flags, int depth,
 static int do_dump(const json_t *json, size_t flags, int depth,
-                   dump_func dump, void *data)
+                   json_dump_callback_t dump, void *data)
 {
 {
-    int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
-
     switch(json_typeof(json)) {
     switch(json_typeof(json)) {
         case JSON_NULL:
         case JSON_NULL:
             return dump("null", 4, data);
             return dump("null", 4, data);
@@ -188,7 +189,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
             size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
             size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
                             "%" JSON_INTEGER_FORMAT,
                             "%" JSON_INTEGER_FORMAT,
                             json_integer_value(json));
                             json_integer_value(json));
-            if(size >= MAX_INTEGER_STR_LENGTH)
+            if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
                 return -1;
                 return -1;
 
 
             return dump(buffer, size, data);
             return dump(buffer, size, data);
@@ -198,31 +199,17 @@ static int do_dump(const json_t *json, size_t flags, int depth,
         {
         {
             char buffer[MAX_REAL_STR_LENGTH];
             char buffer[MAX_REAL_STR_LENGTH];
             int size;
             int size;
+            double value = json_real_value(json);
 
 
-            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
-                            json_real_value(json));
-            if(size >= MAX_REAL_STR_LENGTH)
+            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value);
+            if(size < 0)
                 return -1;
                 return -1;
 
 
-            /* Make sure there's a dot or 'e' in the output. Otherwise
-               a real is converted to an integer when decoding */
-            if(strchr(buffer, '.') == NULL &&
-               strchr(buffer, 'e') == NULL)
-            {
-                if(size + 2 >= MAX_REAL_STR_LENGTH) {
-                    /* No space to append ".0" */
-                    return -1;
-                }
-                buffer[size] = '.';
-                buffer[size + 1] = '0';
-                size += 2;
-            }
-
             return dump(buffer, size, data);
             return dump(buffer, size, data);
         }
         }
 
 
         case JSON_STRING:
         case JSON_STRING:
-            return dump_string(json_string_value(json), ascii, dump, data);
+            return dump_string(json_string_value(json), dump, data, flags);
 
 
         case JSON_ARRAY:
         case JSON_ARRAY:
         {
         {
@@ -308,19 +295,20 @@ static int do_dump(const json_t *json, size_t flags, int depth,
 
 
             if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
             if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
             {
             {
-                const object_key_t **keys;
+                struct object_key *keys;
                 size_t size, i;
                 size_t size, i;
                 int (*cmp_func)(const void *, const void *);
                 int (*cmp_func)(const void *, const void *);
 
 
                 size = json_object_size(json);
                 size = json_object_size(json);
-                keys = jsonp_malloc(size * sizeof(object_key_t *));
+                keys = jsonp_malloc(size * sizeof(struct object_key));
                 if(!keys)
                 if(!keys)
                     goto object_error;
                     goto object_error;
 
 
                 i = 0;
                 i = 0;
                 while(iter)
                 while(iter)
                 {
                 {
-                    keys[i] = jsonp_object_iter_fullkey(iter);
+                    keys[i].serial = hashtable_iter_serial(iter);
+                    keys[i].key = json_object_iter_key(iter);
                     iter = json_object_iter_next((json_t *)json, iter);
                     iter = json_object_iter_next((json_t *)json, iter);
                     i++;
                     i++;
                 }
                 }
@@ -331,18 +319,18 @@ static int do_dump(const json_t *json, size_t flags, int depth,
                 else
                 else
                     cmp_func = object_key_compare_serials;
                     cmp_func = object_key_compare_serials;
 
 
-                qsort(keys, size, sizeof(object_key_t *), cmp_func);
+                qsort(keys, size, sizeof(struct object_key), cmp_func);
 
 
                 for(i = 0; i < size; i++)
                 for(i = 0; i < size; i++)
                 {
                 {
                     const char *key;
                     const char *key;
                     json_t *value;
                     json_t *value;
 
 
-                    key = keys[i]->key;
+                    key = keys[i].key;
                     value = json_object_get(json, key);
                     value = json_object_get(json, key);
                     assert(value);
                     assert(value);
 
 
-                    dump_string(key, ascii, dump, data);
+                    dump_string(key, dump, data, flags);
                     if(dump(separator, separator_length, data) ||
                     if(dump(separator, separator_length, data) ||
                        do_dump(value, flags, depth + 1, dump, data))
                        do_dump(value, flags, depth + 1, dump, data))
                     {
                     {
@@ -379,7 +367,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
                 {
                 {
                     void *next = json_object_iter_next((json_t *)json, iter);
                     void *next = json_object_iter_next((json_t *)json, iter);
 
 
-                    dump_string(json_object_iter_key(iter), ascii, dump, data);
+                    dump_string(json_object_iter_key(iter), dump, data, flags);
                     if(dump(separator, separator_length, data) ||
                     if(dump(separator, separator_length, data) ||
                        do_dump(json_object_iter_value(iter), flags, depth + 1,
                        do_dump(json_object_iter_value(iter), flags, depth + 1,
                                dump, data))
                                dump, data))
@@ -415,39 +403,26 @@ static int do_dump(const json_t *json, size_t flags, int depth,
     }
     }
 }
 }
 
 
-
 char *json_dumps(const json_t *json, size_t flags)
 char *json_dumps(const json_t *json, size_t flags)
 {
 {
     strbuffer_t strbuff;
     strbuffer_t strbuff;
     char *result;
     char *result;
 
 
-    if(!(flags & JSON_ENCODE_ANY)) {
-        if(!json_is_array(json) && !json_is_object(json))
-           return NULL;
-    }
-
     if(strbuffer_init(&strbuff))
     if(strbuffer_init(&strbuff))
         return NULL;
         return NULL;
 
 
-    if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
-        strbuffer_close(&strbuff);
-        return NULL;
-    }
+    if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
+        result = NULL;
+    else
+        result = jsonp_strdup(strbuffer_value(&strbuff));
 
 
-    result = jsonp_strdup(strbuffer_value(&strbuff));
     strbuffer_close(&strbuff);
     strbuffer_close(&strbuff);
-
     return result;
     return result;
 }
 }
 
 
 int json_dumpf(const json_t *json, FILE *output, size_t flags)
 int json_dumpf(const json_t *json, FILE *output, size_t flags)
 {
 {
-    if(!(flags & JSON_ENCODE_ANY)) {
-        if(!json_is_array(json) && !json_is_object(json))
-           return -1;
-    }
-
-    return do_dump(json, flags, 0, dump_to_file, (void *)output);
+    return json_dump_callback(json, dump_to_file, (void *)output, flags);
 }
 }
 
 
 int json_dump_file(const json_t *json, const char *path, size_t flags)
 int json_dump_file(const json_t *json, const char *path, size_t flags)
@@ -463,3 +438,13 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
     fclose(output);
     fclose(output);
     return result;
     return result;
 }
 }
+
+int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
+{
+    if(!(flags & JSON_ENCODE_ANY)) {
+        if(!json_is_array(json) && !json_is_object(json))
+           return -1;
+    }
+
+    return do_dump(json, flags, 0, callback, data);
+}

+ 1 - 0
compat/jansson/error.c

@@ -59,4 +59,5 @@ void jsonp_error_vset(json_error_t *error, int line, int column,
     error->position = position;
     error->position = position;
 
 
     vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
     vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+    error->text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
 }
 }

+ 51 - 63
compat/jansson/hashtable.c

@@ -1,11 +1,12 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * This library is free software; you can redistribute it and/or modify
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
  */
  */
 
 
 #include <stdlib.h>
 #include <stdlib.h>
+#include <string.h>
 #include <jansson_config.h>   /* for JSON_INLINE */
 #include <jansson_config.h>   /* for JSON_INLINE */
 #include "jansson_private.h"  /* for container_of() */
 #include "jansson_private.h"  /* for container_of() */
 #include "hashtable.h"
 #include "hashtable.h"
@@ -16,6 +17,23 @@ typedef struct hashtable_bucket bucket_t;
 
 
 #define list_to_pair(list_)  container_of(list_, pair_t, list)
 #define list_to_pair(list_)  container_of(list_, pair_t, list)
 
 
+/* From http://www.cse.yorku.ca/~oz/hash.html */
+static size_t hash_str(const void *ptr)
+{
+    const char *str = (const char *)ptr;
+
+    size_t hash = 5381;
+    size_t c;
+
+    while((c = (size_t)*str))
+    {
+        hash = ((hash << 5) + hash) + c;
+        str++;
+    }
+
+    return hash;
+}
+
 static JSON_INLINE void list_init(list_t *list)
 static JSON_INLINE void list_init(list_t *list)
 {
 {
     list->next = list;
     list->next = list;
@@ -62,7 +80,6 @@ static size_t primes[] = {
     12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
     12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
     805306457, 1610612741
     805306457, 1610612741
 };
 };
-static const size_t num_primes = sizeof(primes) / sizeof(size_t);
 
 
 static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
 static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
 {
 {
@@ -71,7 +88,7 @@ static JSON_INLINE size_t num_buckets(hashtable_t *hashtable)
 
 
 
 
 static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
 static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
-                                   const void *key, size_t hash)
+                                   const char *key, size_t hash)
 {
 {
     list_t *list;
     list_t *list;
     pair_t *pair;
     pair_t *pair;
@@ -83,7 +100,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
     while(1)
     while(1)
     {
     {
         pair = list_to_pair(list);
         pair = list_to_pair(list);
-        if(pair->hash == hash && hashtable->cmp_keys(pair->key, key))
+        if(pair->hash == hash && strcmp(pair->key, key) == 0)
             return pair;
             return pair;
 
 
         if(list == bucket->last)
         if(list == bucket->last)
@@ -97,7 +114,7 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
 
 
 /* returns 0 on success, -1 if key was not found */
 /* returns 0 on success, -1 if key was not found */
 static int hashtable_do_del(hashtable_t *hashtable,
 static int hashtable_do_del(hashtable_t *hashtable,
-                            const void *key, size_t hash)
+                            const char *key, size_t hash)
 {
 {
     pair_t *pair;
     pair_t *pair;
     bucket_t *bucket;
     bucket_t *bucket;
@@ -120,11 +137,7 @@ static int hashtable_do_del(hashtable_t *hashtable,
         bucket->last = pair->list.prev;
         bucket->last = pair->list.prev;
 
 
     list_remove(&pair->list);
     list_remove(&pair->list);
-
-    if(hashtable->free_key)
-        hashtable->free_key(pair->key);
-    if(hashtable->free_value)
-        hashtable->free_value(pair->value);
+    json_decref(pair->value);
 
 
     jsonp_free(pair);
     jsonp_free(pair);
     hashtable->size--;
     hashtable->size--;
@@ -141,10 +154,7 @@ static void hashtable_do_clear(hashtable_t *hashtable)
     {
     {
         next = list->next;
         next = list->next;
         pair = list_to_pair(list);
         pair = list_to_pair(list);
-        if(hashtable->free_key)
-            hashtable->free_key(pair->key);
-        if(hashtable->free_value)
-            hashtable->free_value(pair->value);
+        json_decref(pair->value);
         jsonp_free(pair);
         jsonp_free(pair);
     }
     }
 }
 }
@@ -184,31 +194,7 @@ static int hashtable_do_rehash(hashtable_t *hashtable)
 }
 }
 
 
 
 
-hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                              free_fn free_key, free_fn free_value)
-{
-    hashtable_t *hashtable = jsonp_malloc(sizeof(hashtable_t));
-    if(!hashtable)
-        return NULL;
-
-    if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
-    {
-        jsonp_free(hashtable);
-        return NULL;
-    }
-
-    return hashtable;
-}
-
-void hashtable_destroy(hashtable_t *hashtable)
-{
-    hashtable_close(hashtable);
-    jsonp_free(hashtable);
-}
-
-int hashtable_init(hashtable_t *hashtable,
-                   key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                   free_fn free_key, free_fn free_value)
+int hashtable_init(hashtable_t *hashtable)
 {
 {
     size_t i;
     size_t i;
 
 
@@ -220,11 +206,6 @@ int hashtable_init(hashtable_t *hashtable,
 
 
     list_init(&hashtable->list);
     list_init(&hashtable->list);
 
 
-    hashtable->hash_key = hash_key;
-    hashtable->cmp_keys = cmp_keys;
-    hashtable->free_key = free_key;
-    hashtable->free_value = free_value;
-
     for(i = 0; i < num_buckets(hashtable); i++)
     for(i = 0; i < num_buckets(hashtable); i++)
     {
     {
         hashtable->buckets[i].first = hashtable->buckets[i].last =
         hashtable->buckets[i].first = hashtable->buckets[i].last =
@@ -240,7 +221,9 @@ void hashtable_close(hashtable_t *hashtable)
     jsonp_free(hashtable->buckets);
     jsonp_free(hashtable->buckets);
 }
 }
 
 
-int hashtable_set(hashtable_t *hashtable, void *key, void *value)
+int hashtable_set(hashtable_t *hashtable,
+                  const char *key, size_t serial,
+                  json_t *value)
 {
 {
     pair_t *pair;
     pair_t *pair;
     bucket_t *bucket;
     bucket_t *bucket;
@@ -251,28 +234,29 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
         if(hashtable_do_rehash(hashtable))
         if(hashtable_do_rehash(hashtable))
             return -1;
             return -1;
 
 
-    hash = hashtable->hash_key(key);
+    hash = hash_str(key);
     index = hash % num_buckets(hashtable);
     index = hash % num_buckets(hashtable);
     bucket = &hashtable->buckets[index];
     bucket = &hashtable->buckets[index];
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
 
 
     if(pair)
     if(pair)
     {
     {
-        if(hashtable->free_key)
-            hashtable->free_key(key);
-        if(hashtable->free_value)
-            hashtable->free_value(pair->value);
+        json_decref(pair->value);
         pair->value = value;
         pair->value = value;
     }
     }
     else
     else
     {
     {
-        pair = jsonp_malloc(sizeof(pair_t));
+        /* offsetof(...) returns the size of pair_t without the last,
+           flexible member. This way, the correct amount is
+           allocated. */
+        pair = jsonp_malloc(offsetof(pair_t, key) + strlen(key) + 1);
         if(!pair)
         if(!pair)
             return -1;
             return -1;
 
 
-        pair->key = key;
-        pair->value = value;
         pair->hash = hash;
         pair->hash = hash;
+        pair->serial = serial;
+        strcpy(pair->key, key);
+        pair->value = value;
         list_init(&pair->list);
         list_init(&pair->list);
 
 
         insert_to_bucket(hashtable, bucket, &pair->list);
         insert_to_bucket(hashtable, bucket, &pair->list);
@@ -282,13 +266,13 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value)
     return 0;
     return 0;
 }
 }
 
 
-void *hashtable_get(hashtable_t *hashtable, const void *key)
+void *hashtable_get(hashtable_t *hashtable, const char *key)
 {
 {
     pair_t *pair;
     pair_t *pair;
     size_t hash;
     size_t hash;
     bucket_t *bucket;
     bucket_t *bucket;
 
 
-    hash = hashtable->hash_key(key);
+    hash = hash_str(key);
     bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
     bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
 
 
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
@@ -298,9 +282,9 @@ void *hashtable_get(hashtable_t *hashtable, const void *key)
     return pair->value;
     return pair->value;
 }
 }
 
 
-int hashtable_del(hashtable_t *hashtable, const void *key)
+int hashtable_del(hashtable_t *hashtable, const char *key)
 {
 {
-    size_t hash = hashtable->hash_key(key);
+    size_t hash = hash_str(key);
     return hashtable_do_del(hashtable, key, hash);
     return hashtable_do_del(hashtable, key, hash);
 }
 }
 
 
@@ -325,13 +309,13 @@ void *hashtable_iter(hashtable_t *hashtable)
     return hashtable_iter_next(hashtable, &hashtable->list);
     return hashtable_iter_next(hashtable, &hashtable->list);
 }
 }
 
 
-void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
+void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
 {
 {
     pair_t *pair;
     pair_t *pair;
     size_t hash;
     size_t hash;
     bucket_t *bucket;
     bucket_t *bucket;
 
 
-    hash = hashtable->hash_key(key);
+    hash = hash_str(key);
     bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
     bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
 
 
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
     pair = hashtable_find_pair(hashtable, bucket, key, hash);
@@ -355,18 +339,22 @@ void *hashtable_iter_key(void *iter)
     return pair->key;
     return pair->key;
 }
 }
 
 
+size_t hashtable_iter_serial(void *iter)
+{
+    pair_t *pair = list_to_pair((list_t *)iter);
+    return pair->serial;
+}
+
 void *hashtable_iter_value(void *iter)
 void *hashtable_iter_value(void *iter)
 {
 {
     pair_t *pair = list_to_pair((list_t *)iter);
     pair_t *pair = list_to_pair((list_t *)iter);
     return pair->value;
     return pair->value;
 }
 }
 
 
-void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
+void hashtable_iter_set(void *iter, json_t *value)
 {
 {
     pair_t *pair = list_to_pair((list_t *)iter);
     pair_t *pair = list_to_pair((list_t *)iter);
 
 
-    if(hashtable->free_value)
-        hashtable->free_value(pair->value);
-
+    json_decref(pair->value);
     pair->value = value;
     pair->value = value;
 }
 }

+ 28 - 55
compat/jansson/hashtable.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * This library is free software; you can redistribute it and/or modify
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -8,20 +8,20 @@
 #ifndef HASHTABLE_H
 #ifndef HASHTABLE_H
 #define HASHTABLE_H
 #define HASHTABLE_H
 
 
-typedef size_t (*key_hash_fn)(const void *key);
-typedef int (*key_cmp_fn)(const void *key1, const void *key2);
-typedef void (*free_fn)(void *key);
-
 struct hashtable_list {
 struct hashtable_list {
     struct hashtable_list *prev;
     struct hashtable_list *prev;
     struct hashtable_list *next;
     struct hashtable_list *next;
 };
 };
 
 
+/* "pair" may be a bit confusing a name, but think of it as a
+   key-value pair. In this case, it just encodes some extra data,
+   too */
 struct hashtable_pair {
 struct hashtable_pair {
-    void *key;
-    void *value;
     size_t hash;
     size_t hash;
     struct hashtable_list list;
     struct hashtable_list list;
+    json_t *value;
+    size_t serial;
+    char key[1];
 };
 };
 
 
 struct hashtable_bucket {
 struct hashtable_bucket {
@@ -34,56 +34,23 @@ typedef struct hashtable {
     struct hashtable_bucket *buckets;
     struct hashtable_bucket *buckets;
     size_t num_buckets;  /* index to primes[] */
     size_t num_buckets;  /* index to primes[] */
     struct hashtable_list list;
     struct hashtable_list list;
-
-    key_hash_fn hash_key;
-    key_cmp_fn cmp_keys;  /* returns non-zero for equal keys */
-    free_fn free_key;
-    free_fn free_value;
 } hashtable_t;
 } hashtable_t;
 
 
-/**
- * hashtable_create - Create a hashtable object
- *
- * @hash_key: The key hashing function
- * @cmp_keys: The key compare function. Returns non-zero for equal and
- *     zero for unequal unequal keys
- * @free_key: If non-NULL, called for a key that is no longer referenced.
- * @free_value: If non-NULL, called for a value that is no longer referenced.
- *
- * Returns a new hashtable object that should be freed with
- * hashtable_destroy when it's no longer used, or NULL on failure (out
- * of memory).
- */
-hashtable_t *hashtable_create(key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                              free_fn free_key, free_fn free_value);
 
 
-/**
- * hashtable_destroy - Destroy a hashtable object
- *
- * @hashtable: The hashtable
- *
- * Destroys a hashtable created with hashtable_create().
- */
-void hashtable_destroy(hashtable_t *hashtable);
+#define hashtable_key_to_iter(key_) \
+    (&(container_of(key_, struct hashtable_pair, key)->list))
 
 
 /**
 /**
  * hashtable_init - Initialize a hashtable object
  * hashtable_init - Initialize a hashtable object
  *
  *
  * @hashtable: The (statically allocated) hashtable object
  * @hashtable: The (statically allocated) hashtable object
- * @hash_key: The key hashing function
- * @cmp_keys: The key compare function. Returns non-zero for equal and
- *     zero for unequal unequal keys
- * @free_key: If non-NULL, called for a key that is no longer referenced.
- * @free_value: If non-NULL, called for a value that is no longer referenced.
  *
  *
  * Initializes a statically allocated hashtable object. The object
  * Initializes a statically allocated hashtable object. The object
  * should be cleared with hashtable_close when it's no longer used.
  * should be cleared with hashtable_close when it's no longer used.
  *
  *
  * Returns 0 on success, -1 on error (out of memory).
  * Returns 0 on success, -1 on error (out of memory).
  */
  */
-int hashtable_init(hashtable_t *hashtable,
-                   key_hash_fn hash_key, key_cmp_fn cmp_keys,
-                   free_fn free_key, free_fn free_value);
+int hashtable_init(hashtable_t *hashtable);
 
 
 /**
 /**
  * hashtable_close - Release all resources used by a hashtable object
  * hashtable_close - Release all resources used by a hashtable object
@@ -99,20 +66,19 @@ void hashtable_close(hashtable_t *hashtable);
  *
  *
  * @hashtable: The hashtable object
  * @hashtable: The hashtable object
  * @key: The key
  * @key: The key
+ * @serial: For addition order of keys
  * @value: The value
  * @value: The value
  *
  *
  * If a value with the given key already exists, its value is replaced
  * If a value with the given key already exists, its value is replaced
- * with the new value.
- *
- * Key and value are "stealed" in the sense that hashtable frees them
- * automatically when they are no longer used. The freeing is
- * accomplished by calling free_key and free_value functions that were
- * supplied to hashtable_new. In case one or both of the free
- * functions is NULL, the corresponding item is not "stealed".
+ * with the new value. Value is "stealed" in the sense that hashtable
+ * doesn't increment its refcount but decreases the refcount when the
+ * value is no longer needed.
  *
  *
  * Returns 0 on success, -1 on failure (out of memory).
  * Returns 0 on success, -1 on failure (out of memory).
  */
  */
-int hashtable_set(hashtable_t *hashtable, void *key, void *value);
+int hashtable_set(hashtable_t *hashtable,
+                  const char *key, size_t serial,
+                  json_t *value);
 
 
 /**
 /**
  * hashtable_get - Get a value associated with a key
  * hashtable_get - Get a value associated with a key
@@ -122,7 +88,7 @@ int hashtable_set(hashtable_t *hashtable, void *key, void *value);
  *
  *
  * Returns value if it is found, or NULL otherwise.
  * Returns value if it is found, or NULL otherwise.
  */
  */
-void *hashtable_get(hashtable_t *hashtable, const void *key);
+void *hashtable_get(hashtable_t *hashtable, const char *key);
 
 
 /**
 /**
  * hashtable_del - Remove a value from the hashtable
  * hashtable_del - Remove a value from the hashtable
@@ -132,7 +98,7 @@ void *hashtable_get(hashtable_t *hashtable, const void *key);
  *
  *
  * Returns 0 on success, or -1 if the key was not found.
  * Returns 0 on success, or -1 if the key was not found.
  */
  */
-int hashtable_del(hashtable_t *hashtable, const void *key);
+int hashtable_del(hashtable_t *hashtable, const char *key);
 
 
 /**
 /**
  * hashtable_clear - Clear hashtable
  * hashtable_clear - Clear hashtable
@@ -169,7 +135,7 @@ void *hashtable_iter(hashtable_t *hashtable);
  * Like hashtable_iter() but returns an iterator pointing to a
  * Like hashtable_iter() but returns an iterator pointing to a
  * specific key.
  * specific key.
  */
  */
-void *hashtable_iter_at(hashtable_t *hashtable, const void *key);
+void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
 
 
 /**
 /**
  * hashtable_iter_next - Advance an iterator
  * hashtable_iter_next - Advance an iterator
@@ -189,6 +155,13 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
  */
  */
 void *hashtable_iter_key(void *iter);
 void *hashtable_iter_key(void *iter);
 
 
+/**
+ * hashtable_iter_serial - Retrieve the serial number pointed to by an iterator
+ *
+ * @iter: The iterator
+ */
+size_t hashtable_iter_serial(void *iter);
+
 /**
 /**
  * hashtable_iter_value - Retrieve the value pointed by an iterator
  * hashtable_iter_value - Retrieve the value pointed by an iterator
  *
  *
@@ -202,6 +175,6 @@ void *hashtable_iter_value(void *iter);
  * @iter: The iterator
  * @iter: The iterator
  * @value: The value to set
  * @value: The value to set
  */
  */
-void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value);
+void hashtable_iter_set(void *iter, json_t *value);
 
 
 #endif
 #endif

+ 65 - 0
compat/jansson/jansson.def

@@ -0,0 +1,65 @@
+LIBRARY "jansson"
+
+EXPORTS
+    json_delete
+    json_true
+    json_false
+    json_null
+    json_string
+    json_string_nocheck
+    json_string_value
+    json_string_set
+    json_string_set_nocheck
+    json_integer
+    json_integer_value
+    json_integer_set
+    json_real
+    json_real_value
+    json_real_set
+    json_number_value
+    json_array
+    json_array_size
+    json_array_get
+    json_array_set_new
+    json_array_append_new
+    json_array_insert_new
+    json_array_remove
+    json_array_clear
+    json_array_extend
+    json_object
+    json_object_size
+    json_object_get
+    json_object_set_new
+    json_object_set_new_nocheck
+    json_object_del
+    json_object_clear
+    json_object_update
+    json_object_update_existing
+    json_object_update_missing
+    json_object_iter
+    json_object_iter_at
+    json_object_iter_next
+    json_object_iter_key
+    json_object_iter_value
+    json_object_iter_set_new
+    json_object_key_to_iter
+    json_dumps
+    json_dumpf
+    json_dump_file
+    json_dump_callback
+    json_loads
+    json_loadb
+    json_loadf
+    json_load_file
+    json_load_callback
+    json_equal
+    json_copy
+    json_deep_copy
+    json_pack
+    json_pack_ex
+    json_vpack_ex
+    json_unpack
+    json_unpack_ex
+    json_vunpack_ex
+    json_set_alloc_funcs
+

+ 25 - 5
compat/jansson/jansson.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -21,11 +21,11 @@ extern "C" {
 /* version */
 /* version */
 
 
 #define JANSSON_MAJOR_VERSION  2
 #define JANSSON_MAJOR_VERSION  2
-#define JANSSON_MINOR_VERSION  1
+#define JANSSON_MINOR_VERSION  4
 #define JANSSON_MICRO_VERSION  0
 #define JANSSON_MICRO_VERSION  0
 
 
 /* Micro version is omitted if it's 0 */
 /* Micro version is omitted if it's 0 */
-#define JANSSON_VERSION  "2.1"
+#define JANSSON_VERSION  "2.4"
 
 
 /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
 /* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
    for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
    for numeric comparisons, e.g. #if JANSSON_VERSION_HEX >= ... */
@@ -52,8 +52,12 @@ typedef struct {
     size_t refcount;
     size_t refcount;
 } json_t;
 } json_t;
 
 
-#if JSON_INTEGER_IS_LONG_LONG && (!defined(WIN32))
+#if JSON_INTEGER_IS_LONG_LONG
+#ifdef _WIN32
+#define JSON_INTEGER_FORMAT "I64d"
+#else
 #define JSON_INTEGER_FORMAT "lld"
 #define JSON_INTEGER_FORMAT "lld"
+#endif
 typedef long long json_int_t;
 typedef long long json_int_t;
 #else
 #else
 #define JSON_INTEGER_FORMAT "ld"
 #define JSON_INTEGER_FORMAT "ld"
@@ -82,6 +86,7 @@ json_t *json_integer(json_int_t value);
 json_t *json_real(double value);
 json_t *json_real(double value);
 json_t *json_true(void);
 json_t *json_true(void);
 json_t *json_false(void);
 json_t *json_false(void);
+#define json_boolean(val)      ((val) ? json_true() : json_false())
 json_t *json_null(void);
 json_t *json_null(void);
 
 
 static JSON_INLINE
 static JSON_INLINE
@@ -126,13 +131,21 @@ int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
 int json_object_del(json_t *object, const char *key);
 int json_object_del(json_t *object, const char *key);
 int json_object_clear(json_t *object);
 int json_object_clear(json_t *object);
 int json_object_update(json_t *object, json_t *other);
 int json_object_update(json_t *object, json_t *other);
+int json_object_update_existing(json_t *object, json_t *other);
+int json_object_update_missing(json_t *object, json_t *other);
 void *json_object_iter(json_t *object);
 void *json_object_iter(json_t *object);
 void *json_object_iter_at(json_t *object, const char *key);
 void *json_object_iter_at(json_t *object, const char *key);
+void *json_object_key_to_iter(const char *key);
 void *json_object_iter_next(json_t *object, void *iter);
 void *json_object_iter_next(json_t *object, void *iter);
 const char *json_object_iter_key(void *iter);
 const char *json_object_iter_key(void *iter);
 json_t *json_object_iter_value(void *iter);
 json_t *json_object_iter_value(void *iter);
 int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
 int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
 
 
+#define json_object_foreach(object, key, value) \
+    for(key = json_object_iter_key(json_object_iter(object)); \
+        key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
+        key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
+
 static JSON_INLINE
 static JSON_INLINE
 int json_object_set(json_t *object, const char *key, json_t *value)
 int json_object_set(json_t *object, const char *key, json_t *value)
 {
 {
@@ -218,11 +231,15 @@ json_t *json_deep_copy(json_t *value);
 
 
 #define JSON_REJECT_DUPLICATES 0x1
 #define JSON_REJECT_DUPLICATES 0x1
 #define JSON_DISABLE_EOF_CHECK 0x2
 #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_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_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_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_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 */
 /* encoding */
@@ -233,11 +250,14 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error);
 #define JSON_SORT_KEYS      0x80
 #define JSON_SORT_KEYS      0x80
 #define JSON_PRESERVE_ORDER 0x100
 #define JSON_PRESERVE_ORDER 0x100
 #define JSON_ENCODE_ANY     0x200
 #define JSON_ENCODE_ANY     0x200
+#define JSON_ESCAPE_SLASH   0x400
+
+typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
 
 
 char *json_dumps(const json_t *json, size_t flags);
 char *json_dumps(const json_t *json, size_t flags);
 int json_dumpf(const json_t *json, FILE *output, size_t flags);
 int json_dumpf(const json_t *json, FILE *output, size_t flags);
 int json_dump_file(const json_t *json, const char *path, size_t flags);
 int json_dump_file(const json_t *json, const char *path, size_t flags);
-
+int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
 
 
 /* custom memory allocation */
 /* custom memory allocation */
 
 

+ 8 - 3
compat/jansson/jansson_config.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -27,8 +27,13 @@
 #define JSON_INLINE inline
 #define JSON_INLINE inline
 #endif
 #endif
 
 
-/* If your compiler supports the `long long` type,
-   JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
+/* If your compiler supports the `long long` type and the strtoll()
+   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+   otherwise to 0. */
 #define JSON_INTEGER_IS_LONG_LONG 1
 #define JSON_INTEGER_IS_LONG_LONG 1
 
 
+/* If locale.h and localeconv() are available, define to 1,
+   otherwise to 0. */
+#define JSON_HAVE_LOCALECONV 1
+
 #endif
 #endif

+ 8 - 3
compat/jansson/jansson_config.h.in

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2010-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2010-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -27,8 +27,13 @@
 #define JSON_INLINE @json_inline@
 #define JSON_INLINE @json_inline@
 #endif
 #endif
 
 
-/* If your compiler supports the `long long` type,
-   JSON_INTEGER_IS_LONG_LONG is defined to 1, otherwise to 0. */
+/* If your compiler supports the `long long` type and the strtoll()
+   library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
+   otherwise to 0. */
 #define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
 #define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
 
 
+/* If locale.h and localeconv() are available, define to 1,
+   otherwise to 0. */
+#define JSON_HAVE_LOCALECONV @json_have_localeconv@
+
 #endif
 #endif

+ 12 - 11
compat/jansson/jansson_private.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -11,6 +11,7 @@
 #include <stddef.h>
 #include <stddef.h>
 #include "jansson.h"
 #include "jansson.h"
 #include "hashtable.h"
 #include "hashtable.h"
+#include "strbuffer.h"
 
 
 #define container_of(ptr_, type_, member_)  \
 #define container_of(ptr_, type_, member_)  \
     ((type_ *)((char *)ptr_ - offsetof(type_, member_)))
     ((type_ *)((char *)ptr_ - offsetof(type_, member_)))
@@ -66,16 +67,6 @@ typedef struct {
 #define json_to_real(json_)   container_of(json_, json_real_t, json)
 #define json_to_real(json_)   container_of(json_, json_real_t, json)
 #define json_to_integer(json_) container_of(json_, json_integer_t, json)
 #define json_to_integer(json_) container_of(json_, json_integer_t, json)
 
 
-size_t jsonp_hash_str(const void *ptr);
-int jsonp_str_equal(const void *ptr1, const void *ptr2);
-
-typedef struct {
-    size_t serial;
-    char key[1];
-} object_key_t;
-
-const object_key_t *jsonp_object_iter_fullkey(void *iter);
-
 void jsonp_error_init(json_error_t *error, const char *source);
 void jsonp_error_init(json_error_t *error, const char *source);
 void jsonp_error_set_source(json_error_t *error, const char *source);
 void jsonp_error_set_source(json_error_t *error, const char *source);
 void jsonp_error_set(json_error_t *error, int line, int column,
 void jsonp_error_set(json_error_t *error, int line, int column,
@@ -83,9 +74,19 @@ void jsonp_error_set(json_error_t *error, int line, int column,
 void jsonp_error_vset(json_error_t *error, int line, int column,
 void jsonp_error_vset(json_error_t *error, int line, int column,
                       size_t position, const char *msg, va_list ap);
                       size_t position, const char *msg, va_list ap);
 
 
+/* Locale independent string<->double conversions */
+int jsonp_strtod(strbuffer_t *strbuffer, double *out);
+int jsonp_dtostr(char *buffer, size_t size, double value);
+
 /* Wrappers for custom memory functions */
 /* Wrappers for custom memory functions */
 void* jsonp_malloc(size_t size);
 void* jsonp_malloc(size_t size);
 void jsonp_free(void *ptr);
 void jsonp_free(void *ptr);
 char *jsonp_strdup(const char *str);
 char *jsonp_strdup(const char *str);
 
 
+/* Windows compatibility */
+#ifdef _WIN32
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
 #endif
 #endif

+ 129 - 35
compat/jansson/load.c

@@ -1,12 +1,11 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
  */
  */
 
 
 #define _GNU_SOURCE
 #define _GNU_SOURCE
-#include <ctype.h>
 #include <errno.h>
 #include <errno.h>
 #include <limits.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdio.h>
@@ -14,7 +13,7 @@
 #include <string.h>
 #include <string.h>
 #include <assert.h>
 #include <assert.h>
 
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "jansson_private.h"
 #include "strbuffer.h"
 #include "strbuffer.h"
 #include "utf.h"
 #include "utf.h"
@@ -32,6 +31,14 @@
 #define TOKEN_FALSE          260
 #define TOKEN_FALSE          260
 #define TOKEN_NULL           261
 #define TOKEN_NULL           261
 
 
+/* Locale independent versions of isxxx() functions */
+#define l_isupper(c)  ('A' <= (c) && (c) <= 'Z')
+#define l_islower(c)  ('a' <= (c) && (c) <= 'z')
+#define l_isalpha(c)  (l_isupper(c) || l_islower(c))
+#define l_isdigit(c)  ('0' <= (c) && (c) <= '9')
+#define l_isxdigit(c) \
+    (l_isdigit(c) || 'A' <= (c) || (c) <= 'F' || 'a' <= (c) || (c) <= 'f')
+
 /* Read one byte from stream, convert to unsigned char, then int, and
 /* Read one byte from stream, convert to unsigned char, then int, and
    return. return EOF on end of file. This corresponds to the
    return. return EOF on end of file. This corresponds to the
    behaviour of fgetc(). */
    behaviour of fgetc(). */
@@ -41,7 +48,7 @@ typedef struct {
     get_func get;
     get_func get;
     void *data;
     void *data;
     char buffer[5];
     char buffer[5];
-    int buffer_pos;
+    size_t buffer_pos;
     int state;
     int state;
     int line;
     int line;
     int column, last_column;
     int column, last_column;
@@ -69,6 +76,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
 {
 {
     va_list ap;
     va_list ap;
     char msg_text[JSON_ERROR_TEXT_LENGTH];
     char msg_text[JSON_ERROR_TEXT_LENGTH];
+    char msg_with_context[JSON_ERROR_TEXT_LENGTH];
 
 
     int line = -1, col = -1;
     int line = -1, col = -1;
     size_t pos = 0;
     size_t pos = 0;
@@ -79,12 +87,12 @@ static void error_set(json_error_t *error, const lex_t *lex,
 
 
     va_start(ap, msg);
     va_start(ap, msg);
     vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
     vsnprintf(msg_text, JSON_ERROR_TEXT_LENGTH, msg, ap);
+    msg_text[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
     va_end(ap);
     va_end(ap);
 
 
     if(lex)
     if(lex)
     {
     {
         const char *saved_text = strbuffer_value(&lex->saved_text);
         const char *saved_text = strbuffer_value(&lex->saved_text);
-        char msg_with_context[JSON_ERROR_TEXT_LENGTH];
 
 
         line = lex->stream.line;
         line = lex->stream.line;
         col = lex->stream.column;
         col = lex->stream.column;
@@ -95,6 +103,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
             if(lex->saved_text.length <= 20) {
             if(lex->saved_text.length <= 20) {
                 snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                 snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                          "%s near '%s'", msg_text, saved_text);
                          "%s near '%s'", msg_text, saved_text);
+                msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
                 result = msg_with_context;
                 result = msg_with_context;
             }
             }
         }
         }
@@ -107,6 +116,7 @@ static void error_set(json_error_t *error, const lex_t *lex,
             else {
             else {
                 snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                 snprintf(msg_with_context, JSON_ERROR_TEXT_LENGTH,
                          "%s near end of file", msg_text);
                          "%s near end of file", msg_text);
+                msg_with_context[JSON_ERROR_TEXT_LENGTH - 1] = '\0';
                 result = msg_with_context;
                 result = msg_with_context;
             }
             }
         }
         }
@@ -268,11 +278,11 @@ static int32_t decode_unicode_escape(const char *str)
     for(i = 1; i <= 4; i++) {
     for(i = 1; i <= 4; i++) {
         char c = str[i];
         char c = str[i];
         value <<= 4;
         value <<= 4;
-        if(isdigit(c))
+        if(l_isdigit(c))
             value += c - '0';
             value += c - '0';
-        else if(islower(c))
+        else if(l_islower(c))
             value += c - 'a' + 10;
             value += c - 'a' + 10;
-        else if(isupper(c))
+        else if(l_isupper(c))
             value += c - 'A' + 10;
             value += c - 'A' + 10;
         else
         else
             assert(0);
             assert(0);
@@ -317,7 +327,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
             if(c == 'u') {
             if(c == 'u') {
                 c = lex_get_save(lex, error);
                 c = lex_get_save(lex, error);
                 for(i = 0; i < 4; i++) {
                 for(i = 0; i < 4; i++) {
-                    if(!isxdigit(c)) {
+                    if(!l_isxdigit(c)) {
                         error_set(error, lex, "invalid escape");
                         error_set(error, lex, "invalid escape");
                         goto out;
                         goto out;
                     }
                     }
@@ -437,7 +447,11 @@ out:
 }
 }
 
 
 #if JSON_INTEGER_IS_LONG_LONG
 #if JSON_INTEGER_IS_LONG_LONG
+#ifdef _MSC_VER // Microsoft Visual Studio
+#define json_strtoint     _strtoi64
+#else
 #define json_strtoint     strtoll
 #define json_strtoint     strtoll
+#endif
 #else
 #else
 #define json_strtoint     strtol
 #define json_strtoint     strtol
 #endif
 #endif
@@ -455,14 +469,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 
 
     if(c == '0') {
     if(c == '0') {
         c = lex_get_save(lex, error);
         c = lex_get_save(lex, error);
-        if(isdigit(c)) {
+        if(l_isdigit(c)) {
             lex_unget_unsave(lex, c);
             lex_unget_unsave(lex, c);
             goto out;
             goto out;
         }
         }
     }
     }
-    else if(isdigit(c)) {
+    else if(l_isdigit(c)) {
         c = lex_get_save(lex, error);
         c = lex_get_save(lex, error);
-        while(isdigit(c))
+        while(l_isdigit(c))
             c = lex_get_save(lex, error);
             c = lex_get_save(lex, error);
     }
     }
     else {
     else {
@@ -496,14 +510,14 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
 
 
     if(c == '.') {
     if(c == '.') {
         c = lex_get(lex, error);
         c = lex_get(lex, error);
-        if(!isdigit(c)) {
+        if(!l_isdigit(c)) {
             lex_unget(lex, c);
             lex_unget(lex, c);
             goto out;
             goto out;
         }
         }
         lex_save(lex, c);
         lex_save(lex, c);
 
 
         c = lex_get_save(lex, error);
         c = lex_get_save(lex, error);
-        while(isdigit(c))
+        while(l_isdigit(c))
             c = lex_get_save(lex, error);
             c = lex_get_save(lex, error);
     }
     }
 
 
@@ -512,24 +526,19 @@ static int lex_scan_number(lex_t *lex, int c, json_error_t *error)
         if(c == '+' || c == '-')
         if(c == '+' || c == '-')
             c = lex_get_save(lex, error);
             c = lex_get_save(lex, error);
 
 
-        if(!isdigit(c)) {
+        if(!l_isdigit(c)) {
             lex_unget_unsave(lex, c);
             lex_unget_unsave(lex, c);
             goto out;
             goto out;
         }
         }
 
 
         c = lex_get_save(lex, error);
         c = lex_get_save(lex, error);
-        while(isdigit(c))
+        while(l_isdigit(c))
             c = lex_get_save(lex, error);
             c = lex_get_save(lex, error);
     }
     }
 
 
     lex_unget_unsave(lex, c);
     lex_unget_unsave(lex, c);
 
 
-    saved_text = strbuffer_value(&lex->saved_text);
-    errno = 0;
-    value = strtod(saved_text, &end);
-    assert(end == saved_text + lex->saved_text.length);
-
-    if(errno == ERANGE && value != 0) {
+    if(jsonp_strtod(&lex->saved_text, &value)) {
         error_set(error, lex, "real number overflow");
         error_set(error, lex, "real number overflow");
         goto out;
         goto out;
     }
     }
@@ -575,17 +584,17 @@ static int lex_scan(lex_t *lex, json_error_t *error)
     else if(c == '"')
     else if(c == '"')
         lex_scan_string(lex, error);
         lex_scan_string(lex, error);
 
 
-    else if(isdigit(c) || c == '-') {
+    else if(l_isdigit(c) || c == '-') {
         if(lex_scan_number(lex, c, error))
         if(lex_scan_number(lex, c, error))
             goto out;
             goto out;
     }
     }
 
 
-    else if(isupper(c) || islower(c)) {
+    else if(l_isalpha(c)) {
         /* eat up the whole identifier for clearer error messages */
         /* eat up the whole identifier for clearer error messages */
         const char *saved_text;
         const char *saved_text;
 
 
         c = lex_get_save(lex, error);
         c = lex_get_save(lex, error);
-        while(isupper(c) || islower(c))
+        while(l_isalpha(c))
             c = lex_get_save(lex, error);
             c = lex_get_save(lex, error);
         lex_unget_unsave(lex, c);
         lex_unget_unsave(lex, c);
 
 
@@ -818,9 +827,11 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
     json_t *result;
     json_t *result;
 
 
     lex_scan(lex, error);
     lex_scan(lex, error);
-    if(lex->token != '[' && lex->token != '{') {
-        error_set(error, lex, "'[' or '{' expected");
-        return NULL;
+    if(!(flags & JSON_DECODE_ANY)) {
+        if(lex->token != '[' && lex->token != '{') {
+            error_set(error, lex, "'[' or '{' expected");
+            return NULL;
+        }
     }
     }
 
 
     result = parse_value(lex, flags, error);
     result = parse_value(lex, flags, error);
@@ -832,10 +843,15 @@ static json_t *parse_json(lex_t *lex, size_t flags, json_error_t *error)
         if(lex->token != TOKEN_EOF) {
         if(lex->token != TOKEN_EOF) {
             error_set(error, lex, "end of file expected");
             error_set(error, lex, "end of file expected");
             json_decref(result);
             json_decref(result);
-            result = NULL;
+            return NULL;
         }
         }
     }
     }
 
 
+    if(error) {
+        /* Save the position even though there was no error */
+        error->position = lex->stream.position;
+    }
+
     return result;
     return result;
 }
 }
 
 
@@ -865,13 +881,19 @@ json_t *json_loads(const char *string, size_t flags, json_error_t *error)
     json_t *result;
     json_t *result;
     string_data_t stream_data;
     string_data_t stream_data;
 
 
+    jsonp_error_init(error, "<string>");
+
+    if (string == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
     stream_data.data = string;
     stream_data.data = string;
     stream_data.pos = 0;
     stream_data.pos = 0;
 
 
     if(lex_init(&lex, string_get, (void *)&stream_data))
     if(lex_init(&lex, string_get, (void *)&stream_data))
         return NULL;
         return NULL;
 
 
-    jsonp_error_init(error, "<string>");
     result = parse_json(&lex, flags, error);
     result = parse_json(&lex, flags, error);
 
 
     lex_close(&lex);
     lex_close(&lex);
@@ -903,6 +925,13 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
     json_t *result;
     json_t *result;
     buffer_data_t stream_data;
     buffer_data_t stream_data;
 
 
+    jsonp_error_init(error, "<buffer>");
+
+    if (buffer == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
     stream_data.data = buffer;
     stream_data.data = buffer;
     stream_data.pos = 0;
     stream_data.pos = 0;
     stream_data.len = buflen;
     stream_data.len = buflen;
@@ -910,7 +939,6 @@ json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t
     if(lex_init(&lex, buffer_get, (void *)&stream_data))
     if(lex_init(&lex, buffer_get, (void *)&stream_data))
         return NULL;
         return NULL;
 
 
-    jsonp_error_init(error, "<buffer>");
     result = parse_json(&lex, flags, error);
     result = parse_json(&lex, flags, error);
 
 
     lex_close(&lex);
     lex_close(&lex);
@@ -923,15 +951,21 @@ json_t *json_loadf(FILE *input, size_t flags, json_error_t *error)
     const char *source;
     const char *source;
     json_t *result;
     json_t *result;
 
 
-    if(lex_init(&lex, (get_func)fgetc, input))
-        return NULL;
-
     if(input == stdin)
     if(input == stdin)
         source = "<stdin>";
         source = "<stdin>";
     else
     else
         source = "<stream>";
         source = "<stream>";
 
 
     jsonp_error_init(error, source);
     jsonp_error_init(error, source);
+
+    if (input == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    if(lex_init(&lex, (get_func)fgetc, input))
+        return NULL;
+
     result = parse_json(&lex, flags, error);
     result = parse_json(&lex, flags, error);
 
 
     lex_close(&lex);
     lex_close(&lex);
@@ -945,7 +979,12 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
 
 
     jsonp_error_init(error, path);
     jsonp_error_init(error, path);
 
 
-    fp = fopen(path, "r");
+    if (path == NULL) {
+        error_set(error, NULL, "wrong arguments");
+        return NULL;
+    }
+
+    fp = fopen(path, "rb");
     if(!fp)
     if(!fp)
     {
     {
         error_set(error, NULL, "unable to open %s: %s",
         error_set(error, NULL, "unable to open %s: %s",
@@ -958,3 +997,58 @@ json_t *json_load_file(const char *path, size_t flags, json_error_t *error)
     fclose(fp);
     fclose(fp);
     return result;
     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;
+}

+ 3 - 3
compat/jansson/memory.c

@@ -1,6 +1,6 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
- * Copyright (c) 2011 Basile Starynkevitch  <basile@starynkevitch.net>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify it
  * Jansson is free software; you can redistribute it and/or modify it
  * under the terms of the MIT license. See LICENSE for details.
  * under the terms of the MIT license. See LICENSE for details.
@@ -9,7 +9,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 
 
-#include <jansson.h>
+#include "jansson.h"
 #include "jansson_private.h"
 #include "jansson_private.h"
 
 
 /* memory function pointers */
 /* memory function pointers */

+ 647 - 0
compat/jansson/pack_unpack.c

@@ -0,0 +1,647 @@
+/*
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2011-2012 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 "jansson_private.h"
+#include "utf.h"
+
+typedef struct {
+    const char *start;
+    const char *fmt;
+    char token;
+    json_error_t *error;
+    size_t flags;
+    int line;
+    int column;
+} scanner_t;
+
+static const char *type_names[] = {
+    "object",
+    "array",
+    "string",
+    "integer",
+    "real",
+    "true",
+    "false",
+    "null"
+};
+
+#define type_name(x) type_names[json_typeof(x)]
+
+static const char *unpack_value_starters = "{[siIbfFOon";
+
+
+static void scanner_init(scanner_t *s, json_error_t *error,
+                         size_t flags, const char *fmt)
+{
+    s->error = error;
+    s->flags = flags;
+    s->fmt = s->start = fmt;
+    s->line = 1;
+    s->column = 0;
+}
+
+static void next_token(scanner_t *s)
+{
+    const char *t = s->fmt;
+    s->column++;
+
+    /* skip space and ignored chars */
+    while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
+        if(*t == '\n') {
+            s->line++;
+            s->column = 1;
+        }
+        else
+            s->column++;
+
+        t++;
+    }
+
+    s->token = *t;
+
+    t++;
+    s->fmt = t;
+}
+
+static void set_error(scanner_t *s, const char *source, const char *fmt, ...)
+{
+    va_list ap;
+    size_t pos;
+    va_start(ap, fmt);
+
+    pos = (size_t)(s->fmt - s->start);
+    jsonp_error_vset(s->error, s->line, s->column, pos, fmt, ap);
+
+    jsonp_error_set_source(s->error, source);
+
+    va_end(ap);
+}
+
+static json_t *pack(scanner_t *s, va_list *ap);
+
+static json_t *pack_object(scanner_t *s, va_list *ap)
+{
+    json_t *object = json_object();
+    next_token(s);
+
+    while(s->token != '}') {
+        const char *key;
+        json_t *value;
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            goto error;
+        }
+
+        if(s->token != 's') {
+            set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
+            goto error;
+        }
+
+        key = va_arg(*ap, const char *);
+        if(!key) {
+            set_error(s, "<args>", "NULL object key");
+            goto error;
+        }
+
+        if(!utf8_check_string(key, -1)) {
+            set_error(s, "<args>", "Invalid UTF-8 in object key");
+            goto error;
+        }
+
+        next_token(s);
+
+        value = pack(s, ap);
+        if(!value)
+            goto error;
+
+        if(json_object_set_new_nocheck(object, key, value)) {
+            set_error(s, "<internal>", "Unable to add key \"%s\"", key);
+            goto error;
+        }
+
+        next_token(s);
+    }
+
+    return object;
+
+error:
+    json_decref(object);
+    return NULL;
+}
+
+static json_t *pack_array(scanner_t *s, va_list *ap)
+{
+    json_t *array = json_array();
+    next_token(s);
+
+    while(s->token != ']') {
+        json_t *value;
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            goto error;
+        }
+
+        value = pack(s, ap);
+        if(!value)
+            goto error;
+
+        if(json_array_append_new(array, value)) {
+            set_error(s, "<internal>", "Unable to append to array");
+            goto error;
+        }
+
+        next_token(s);
+    }
+    return array;
+
+error:
+    json_decref(array);
+    return NULL;
+}
+
+static json_t *pack(scanner_t *s, va_list *ap)
+{
+    switch(s->token) {
+        case '{':
+            return pack_object(s, ap);
+
+        case '[':
+            return pack_array(s, ap);
+
+        case 's': /* string */
+        {
+            const char *str = va_arg(*ap, const char *);
+            if(!str) {
+                set_error(s, "<args>", "NULL string argument");
+                return NULL;
+            }
+            if(!utf8_check_string(str, -1)) {
+                set_error(s, "<args>", "Invalid UTF-8 string");
+                return NULL;
+            }
+            return json_string_nocheck(str);
+        }
+
+        case 'n': /* null */
+            return json_null();
+
+        case 'b': /* boolean */
+            return va_arg(*ap, int) ? json_true() : json_false();
+
+        case 'i': /* integer from int */
+            return json_integer(va_arg(*ap, int));
+
+        case 'I': /* integer from json_int_t */
+            return json_integer(va_arg(*ap, json_int_t));
+
+        case 'f': /* real */
+            return json_real(va_arg(*ap, double));
+
+        case 'O': /* a json_t object; increments refcount */
+            return json_incref(va_arg(*ap, json_t *));
+
+        case 'o': /* a json_t object; doesn't increment refcount */
+            return va_arg(*ap, json_t *);
+
+        default:
+            set_error(s, "<format>", "Unexpected format character '%c'",
+                      s->token);
+            return NULL;
+    }
+}
+
+static int unpack(scanner_t *s, json_t *root, va_list *ap);
+
+static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
+{
+    int ret = -1;
+    int strict = 0;
+
+    /* Use a set (emulated by a hashtable) to check that all object
+       keys are accessed. Checking that the correct number of keys
+       were accessed is not enough, as the same key can be unpacked
+       multiple times.
+    */
+    hashtable_t key_set;
+
+    if(hashtable_init(&key_set)) {
+        set_error(s, "<internal>", "Out of memory");
+        return -1;
+    }
+
+    if(root && !json_is_object(root)) {
+        set_error(s, "<validation>", "Expected object, got %s",
+                  type_name(root));
+        goto out;
+    }
+    next_token(s);
+
+    while(s->token != '}') {
+        const char *key;
+        json_t *value;
+        int opt = 0;
+
+        if(strict != 0) {
+            set_error(s, "<format>", "Expected '}' after '%c', got '%c'",
+                      (strict == 1 ? '!' : '*'), s->token);
+            goto out;
+        }
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            goto out;
+        }
+
+        if(s->token == '!' || s->token == '*') {
+            strict = (s->token == '!' ? 1 : -1);
+            next_token(s);
+            continue;
+        }
+
+        if(s->token != 's') {
+            set_error(s, "<format>", "Expected format 's', got '%c'", s->token);
+            goto out;
+        }
+
+        key = va_arg(*ap, const char *);
+        if(!key) {
+            set_error(s, "<args>", "NULL object key");
+            goto out;
+        }
+
+        next_token(s);
+
+        if(s->token == '?') {
+            opt = 1;
+            next_token(s);
+        }
+
+        if(!root) {
+            /* skipping */
+            value = NULL;
+        }
+        else {
+            value = json_object_get(root, key);
+            if(!value && !opt) {
+                set_error(s, "<validation>", "Object item not found: %s", key);
+                goto out;
+            }
+        }
+
+        if(unpack(s, value, ap))
+            goto out;
+
+        hashtable_set(&key_set, key, 0, json_null());
+        next_token(s);
+    }
+
+    if(strict == 0 && (s->flags & JSON_STRICT))
+        strict = 1;
+
+    if(root && strict == 1 && key_set.size != json_object_size(root)) {
+        long diff = (long)json_object_size(root) - (long)key_set.size;
+        set_error(s, "<validation>", "%li object item(s) left unpacked", diff);
+        goto out;
+    }
+
+    ret = 0;
+
+out:
+    hashtable_close(&key_set);
+    return ret;
+}
+
+static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
+{
+    size_t i = 0;
+    int strict = 0;
+
+    if(root && !json_is_array(root)) {
+        set_error(s, "<validation>", "Expected array, got %s", type_name(root));
+        return -1;
+    }
+    next_token(s);
+
+    while(s->token != ']') {
+        json_t *value;
+
+        if(strict != 0) {
+            set_error(s, "<format>", "Expected ']' after '%c', got '%c'",
+                      (strict == 1 ? '!' : '*'),
+                      s->token);
+            return -1;
+        }
+
+        if(!s->token) {
+            set_error(s, "<format>", "Unexpected end of format string");
+            return -1;
+        }
+
+        if(s->token == '!' || s->token == '*') {
+            strict = (s->token == '!' ? 1 : -1);
+            next_token(s);
+            continue;
+        }
+
+        if(!strchr(unpack_value_starters, s->token)) {
+            set_error(s, "<format>", "Unexpected format character '%c'",
+                      s->token);
+            return -1;
+        }
+
+        if(!root) {
+            /* skipping */
+            value = NULL;
+        }
+        else {
+            value = json_array_get(root, i);
+            if(!value) {
+                set_error(s, "<validation>", "Array index %lu out of range",
+                          (unsigned long)i);
+                return -1;
+            }
+        }
+
+        if(unpack(s, value, ap))
+            return -1;
+
+        next_token(s);
+        i++;
+    }
+
+    if(strict == 0 && (s->flags & JSON_STRICT))
+        strict = 1;
+
+    if(root && strict == 1 && i != json_array_size(root)) {
+        long diff = (long)json_array_size(root) - (long)i;
+        set_error(s, "<validation>", "%li array item(s) left unpacked", diff);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int unpack(scanner_t *s, json_t *root, va_list *ap)
+{
+    switch(s->token)
+    {
+        case '{':
+            return unpack_object(s, root, ap);
+
+        case '[':
+            return unpack_array(s, root, ap);
+
+        case 's':
+            if(root && !json_is_string(root)) {
+                set_error(s, "<validation>", "Expected string, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                const char **target;
+
+                target = va_arg(*ap, const char **);
+                if(!target) {
+                    set_error(s, "<args>", "NULL string argument");
+                    return -1;
+                }
+
+                if(root)
+                    *target = json_string_value(root);
+            }
+            return 0;
+
+        case 'i':
+            if(root && !json_is_integer(root)) {
+                set_error(s, "<validation>", "Expected integer, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                int *target = va_arg(*ap, int*);
+                if(root)
+                    *target = (int)json_integer_value(root);
+            }
+
+            return 0;
+
+        case 'I':
+            if(root && !json_is_integer(root)) {
+                set_error(s, "<validation>", "Expected integer, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                json_int_t *target = va_arg(*ap, json_int_t*);
+                if(root)
+                    *target = json_integer_value(root);
+            }
+
+            return 0;
+
+        case 'b':
+            if(root && !json_is_boolean(root)) {
+                set_error(s, "<validation>", "Expected true or false, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                int *target = va_arg(*ap, int*);
+                if(root)
+                    *target = json_is_true(root);
+            }
+
+            return 0;
+
+        case 'f':
+            if(root && !json_is_real(root)) {
+                set_error(s, "<validation>", "Expected real, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                double *target = va_arg(*ap, double*);
+                if(root)
+                    *target = json_real_value(root);
+            }
+
+            return 0;
+
+        case 'F':
+            if(root && !json_is_number(root)) {
+                set_error(s, "<validation>", "Expected real or integer, got %s",
+                          type_name(root));
+                return -1;
+            }
+
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                double *target = va_arg(*ap, double*);
+                if(root)
+                    *target = json_number_value(root);
+            }
+
+            return 0;
+
+        case 'O':
+            if(root && !(s->flags & JSON_VALIDATE_ONLY))
+                json_incref(root);
+            /* Fall through */
+
+        case 'o':
+            if(!(s->flags & JSON_VALIDATE_ONLY)) {
+                json_t **target = va_arg(*ap, json_t**);
+                if(root)
+                    *target = root;
+            }
+
+            return 0;
+
+        case 'n':
+            /* Never assign, just validate */
+            if(root && !json_is_null(root)) {
+                set_error(s, "<validation>", "Expected null, got %s",
+                          type_name(root));
+                return -1;
+            }
+            return 0;
+
+        default:
+            set_error(s, "<format>", "Unexpected format character '%c'",
+                      s->token);
+            return -1;
+    }
+}
+
+json_t *json_vpack_ex(json_error_t *error, size_t flags,
+                      const char *fmt, va_list ap)
+{
+    scanner_t s;
+    va_list ap_copy;
+    json_t *value;
+
+    if(!fmt || !*fmt) {
+        jsonp_error_init(error, "<format>");
+        jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
+        return NULL;
+    }
+    jsonp_error_init(error, NULL);
+
+    scanner_init(&s, error, flags, fmt);
+    next_token(&s);
+
+    va_copy(ap_copy, ap);
+    value = pack(&s, &ap_copy);
+    va_end(ap_copy);
+
+    if(!value)
+        return NULL;
+
+    next_token(&s);
+    if(s.token) {
+        json_decref(value);
+        set_error(&s, "<format>", "Garbage after format string");
+        return NULL;
+    }
+
+    return value;
+}
+
+json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
+{
+    json_t *value;
+    va_list ap;
+
+    va_start(ap, fmt);
+    value = json_vpack_ex(error, flags, fmt, ap);
+    va_end(ap);
+
+    return value;
+}
+
+json_t *json_pack(const char *fmt, ...)
+{
+    json_t *value;
+    va_list ap;
+
+    va_start(ap, fmt);
+    value = json_vpack_ex(NULL, 0, fmt, ap);
+    va_end(ap);
+
+    return value;
+}
+
+int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
+                    const char *fmt, va_list ap)
+{
+    scanner_t s;
+    va_list ap_copy;
+
+    if(!root) {
+        jsonp_error_init(error, "<root>");
+        jsonp_error_set(error, -1, -1, 0, "NULL root value");
+        return -1;
+    }
+
+    if(!fmt || !*fmt) {
+        jsonp_error_init(error, "<format>");
+        jsonp_error_set(error, -1, -1, 0, "NULL or empty format string");
+        return -1;
+    }
+    jsonp_error_init(error, NULL);
+
+    scanner_init(&s, error, flags, fmt);
+    next_token(&s);
+
+    va_copy(ap_copy, ap);
+    if(unpack(&s, root, &ap_copy)) {
+        va_end(ap_copy);
+        return -1;
+    }
+    va_end(ap_copy);
+
+    next_token(&s);
+    if(s.token) {
+        set_error(&s, "<format>", "Garbage after format string");
+        return -1;
+    }
+
+    return 0;
+}
+
+int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    va_start(ap, fmt);
+    ret = json_vunpack_ex(root, error, flags, fmt, ap);
+    va_end(ap);
+
+    return ret;
+}
+
+int json_unpack(json_t *root, const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    va_start(ap, fmt);
+    ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
+    va_end(ap);
+
+    return ret;
+}

+ 10 - 3
compat/jansson/strbuffer.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -13,6 +13,7 @@
 
 
 #define STRBUFFER_MIN_SIZE  16
 #define STRBUFFER_MIN_SIZE  16
 #define STRBUFFER_FACTOR    2
 #define STRBUFFER_FACTOR    2
+#define STRBUFFER_SIZE_MAX  ((size_t)-1)
 
 
 int strbuffer_init(strbuffer_t *strbuff)
 int strbuffer_init(strbuffer_t *strbuff)
 {
 {
@@ -64,13 +65,19 @@ int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
     return strbuffer_append_bytes(strbuff, &byte, 1);
     return strbuffer_append_bytes(strbuff, &byte, 1);
 }
 }
 
 
-int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size)
+int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
 {
 {
-    if(strbuff->length + size >= strbuff->size)
+    if(size >= strbuff->size - strbuff->length)
     {
     {
         size_t new_size;
         size_t new_size;
         char *new_value;
         char *new_value;
 
 
+        /* avoid integer overflow */
+        if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
+            || size > STRBUFFER_SIZE_MAX - 1
+            || strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
+            return -1;
+
         new_size = max(strbuff->size * STRBUFFER_FACTOR,
         new_size = max(strbuff->size * STRBUFFER_FACTOR,
                        strbuff->length + size + 1);
                        strbuff->length + size + 1);
 
 

+ 4 - 4
compat/jansson/strbuffer.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -10,8 +10,8 @@
 
 
 typedef struct {
 typedef struct {
     char *value;
     char *value;
-    int length;   /* bytes used */
-    int size;     /* bytes allocated */
+    size_t length;   /* bytes used */
+    size_t size;     /* bytes allocated */
 } strbuffer_t;
 } strbuffer_t;
 
 
 int strbuffer_init(strbuffer_t *strbuff);
 int strbuffer_init(strbuffer_t *strbuff);
@@ -24,7 +24,7 @@ char *strbuffer_steal_value(strbuffer_t *strbuff);
 
 
 int strbuffer_append(strbuffer_t *strbuff, const char *string);
 int strbuffer_append(strbuffer_t *strbuff, const char *string);
 int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
 int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
-int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, int size);
+int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
 
 
 char strbuffer_pop(strbuffer_t *strbuff);
 char strbuffer_pop(strbuffer_t *strbuff);
 
 

+ 129 - 0
compat/jansson/strconv.c

@@ -0,0 +1,129 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "jansson_private.h"
+#include "strbuffer.h"
+
+#if JSON_HAVE_LOCALECONV
+#include <locale.h>
+
+/*
+  - This code assumes that the decimal separator is exactly one
+    character.
+
+  - If setlocale() is called by another thread between the call to
+    localeconv() and the call to sprintf() or strtod(), the result may
+    be wrong. setlocale() is not thread-safe and should not be used
+    this way. Multi-threaded programs should use uselocale() instead.
+*/
+
+static void to_locale(strbuffer_t *strbuffer)
+{
+    const char *point;
+    char *pos;
+
+    point = localeconv()->decimal_point;
+    if(*point == '.') {
+        /* No conversion needed */
+        return;
+    }
+
+    pos = strchr(strbuffer->value, '.');
+    if(pos)
+        *pos = *point;
+}
+
+static void from_locale(char *buffer)
+{
+    const char *point;
+    char *pos;
+
+    point = localeconv()->decimal_point;
+    if(*point == '.') {
+        /* No conversion needed */
+        return;
+    }
+
+    pos = strchr(buffer, *point);
+    if(pos)
+        *pos = '.';
+}
+#endif
+
+int jsonp_strtod(strbuffer_t *strbuffer, double *out)
+{
+    double value;
+    char *end;
+
+#if JSON_HAVE_LOCALECONV
+    to_locale(strbuffer);
+#endif
+
+    errno = 0;
+    value = strtod(strbuffer->value, &end);
+    assert(end == strbuffer->value + strbuffer->length);
+
+    if(errno == ERANGE && value != 0) {
+        /* Overflow */
+        return -1;
+    }
+
+    *out = value;
+    return 0;
+}
+
+int jsonp_dtostr(char *buffer, size_t size, double value)
+{
+    int ret;
+    char *start, *end;
+    size_t length;
+
+    ret = snprintf(buffer, size, "%.17g", value);
+    if(ret < 0)
+        return -1;
+
+    length = (size_t)ret;
+    if(length >= size)
+        return -1;
+
+#if JSON_HAVE_LOCALECONV
+    from_locale(buffer);
+#endif
+
+    /* Make sure there's a dot or 'e' in the output. Otherwise
+       a real is converted to an integer when decoding */
+    if(strchr(buffer, '.') == NULL &&
+       strchr(buffer, 'e') == NULL)
+    {
+        if(length + 3 >= size) {
+            /* No space to append ".0" */
+            return -1;
+        }
+        buffer[length] = '.';
+        buffer[length + 1] = '0';
+        buffer[length + 2] = '\0';
+        length += 2;
+    }
+
+    /* Remove leading '+' from positive exponent. Also remove leading
+       zeros from exponents (added by some printf() implementations) */
+    start = strchr(buffer, 'e');
+    if(start) {
+        start++;
+        end = start + 1;
+
+        if(*start == '-')
+            start++;
+
+        while(*end == '0')
+            end++;
+
+        if(end != start) {
+            memmove(start, end, length - (size_t)(end - buffer));
+            length -= (size_t)(end - start);
+        }
+    }
+
+    return (int)length;
+}

+ 1 - 1
compat/jansson/utf.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.

+ 1 - 1
compat/jansson/utf.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.

+ 86 - 132
compat/jansson/value.c

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
+ * Copyright (c) 2009-2012 Petri Lehtinen <petri@digip.org>
  *
  *
  * Jansson is free software; you can redistribute it and/or modify
  * Jansson is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See LICENSE for details.
  * it under the terms of the MIT license. See LICENSE for details.
@@ -10,12 +10,20 @@
 #include <stddef.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
+#include <math.h>
 
 
-#include <jansson.h>
+#include "jansson.h"
 #include "hashtable.h"
 #include "hashtable.h"
 #include "jansson_private.h"
 #include "jansson_private.h"
 #include "utf.h"
 #include "utf.h"
 
 
+/* Work around nonstandard isnan() and isinf() implementations */
+#ifndef isnan
+static JSON_INLINE int isnan(double x) { return x != x; }
+#endif
+#ifndef isinf
+static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
+#endif
 
 
 static JSON_INLINE void json_init(json_t *json, json_type type)
 static JSON_INLINE void json_init(json_t *json, json_type type)
 {
 {
@@ -26,50 +34,6 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
 
 
 /*** object ***/
 /*** object ***/
 
 
-/* From http://www.cse.yorku.ca/~oz/hash.html */
-size_t jsonp_hash_str(const void *ptr)
-{
-    const char *str = (const char *)ptr;
-
-    size_t hash = 5381;
-    size_t c;
-
-    while((c = (size_t)*str))
-    {
-        hash = ((hash << 5) + hash) + c;
-        str++;
-    }
-
-    return hash;
-}
-
-int jsonp_str_equal(const void *ptr1, const void *ptr2)
-{
-    return strcmp((const char *)ptr1, (const char *)ptr2) == 0;
-}
-
-/* This macro just returns a pointer that's a few bytes backwards from
-   string. This makes it possible to pass a pointer to object_key_t
-   when only the string inside it is used, without actually creating
-   an object_key_t instance. */
-#define string_to_key(string)  container_of(string, object_key_t, key)
-
-static size_t hash_key(const void *ptr)
-{
-    return jsonp_hash_str(((const object_key_t *)ptr)->key);
-}
-
-static int key_equal(const void *ptr1, const void *ptr2)
-{
-    return jsonp_str_equal(((const object_key_t *)ptr1)->key,
-                           ((const object_key_t *)ptr2)->key);
-}
-
-static void value_decref(void *value)
-{
-    json_decref((json_t *)value);
-}
-
 json_t *json_object(void)
 json_t *json_object(void)
 {
 {
     json_object_t *object = jsonp_malloc(sizeof(json_object_t));
     json_object_t *object = jsonp_malloc(sizeof(json_object_t));
@@ -77,9 +41,7 @@ json_t *json_object(void)
         return NULL;
         return NULL;
     json_init(&object->json, JSON_OBJECT);
     json_init(&object->json, JSON_OBJECT);
 
 
-    if(hashtable_init(&object->hashtable,
-                      hash_key, key_equal,
-                      jsonp_free, value_decref))
+    if(hashtable_init(&object->hashtable))
     {
     {
         jsonp_free(object);
         jsonp_free(object);
         return NULL;
         return NULL;
@@ -116,38 +78,24 @@ json_t *json_object_get(const json_t *json, const char *key)
         return NULL;
         return NULL;
 
 
     object = json_to_object(json);
     object = json_to_object(json);
-    return hashtable_get(&object->hashtable, string_to_key(key));
+    return hashtable_get(&object->hashtable, key);
 }
 }
 
 
 int json_object_set_new_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;
     json_object_t *object;
-    object_key_t *k;
 
 
-    if(!key || !value)
+    if(!value)
         return -1;
         return -1;
 
 
-    if(!json_is_object(json) || json == value)
+    if(!key || !json_is_object(json) || json == value)
     {
     {
         json_decref(value);
         json_decref(value);
         return -1;
         return -1;
     }
     }
     object = json_to_object(json);
     object = json_to_object(json);
 
 
-    /* offsetof(...) returns the size of object_key_t without the
-       last, flexible member. This way, the correct amount is
-       allocated. */
-    k = jsonp_malloc(offsetof(object_key_t, key) + strlen(key) + 1);
-    if(!k)
-    {
-        json_decref(value);
-        return -1;
-    }
-
-    k->serial = object->serial++;
-    strcpy(k->key, key);
-
-    if(hashtable_set(&object->hashtable, k, value))
+    if(hashtable_set(&object->hashtable, key, object->serial++, value))
     {
     {
         json_decref(value);
         json_decref(value);
         return -1;
         return -1;
@@ -175,7 +123,7 @@ int json_object_del(json_t *json, const char *key)
         return -1;
         return -1;
 
 
     object = json_to_object(json);
     object = json_to_object(json);
-    return hashtable_del(&object->hashtable, string_to_key(key));
+    return hashtable_del(&object->hashtable, key);
 }
 }
 
 
 int json_object_clear(json_t *json)
 int json_object_clear(json_t *json)
@@ -186,30 +134,56 @@ int json_object_clear(json_t *json)
         return -1;
         return -1;
 
 
     object = json_to_object(json);
     object = json_to_object(json);
+
     hashtable_clear(&object->hashtable);
     hashtable_clear(&object->hashtable);
+    object->serial = 0;
 
 
     return 0;
     return 0;
 }
 }
 
 
 int json_object_update(json_t *object, json_t *other)
 int json_object_update(json_t *object, json_t *other)
 {
 {
-    void *iter;
+    const char *key;
+    json_t *value;
 
 
     if(!json_is_object(object) || !json_is_object(other))
     if(!json_is_object(object) || !json_is_object(other))
         return -1;
         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);
-
+    json_object_foreach(other, key, value) {
         if(json_object_set_nocheck(object, key, value))
         if(json_object_set_nocheck(object, key, value))
             return -1;
             return -1;
+    }
+
+    return 0;
+}
+
+int json_object_update_existing(json_t *object, json_t *other)
+{
+    const char *key;
+    json_t *value;
+
+    if(!json_is_object(object) || !json_is_object(other))
+        return -1;
+
+    json_object_foreach(other, key, value) {
+        if(json_object_get(object, key))
+            json_object_set_nocheck(object, key, value);
+    }
+
+    return 0;
+}
+
+int json_object_update_missing(json_t *object, json_t *other)
+{
+    const char *key;
+    json_t *value;
+
+    if(!json_is_object(object) || !json_is_object(other))
+        return -1;
 
 
-        iter = json_object_iter_next(other, iter);
+    json_object_foreach(other, key, value) {
+        if(!json_object_get(object, key))
+            json_object_set_nocheck(object, key, value);
     }
     }
 
 
     return 0;
     return 0;
@@ -234,7 +208,7 @@ void *json_object_iter_at(json_t *json, const char *key)
         return NULL;
         return NULL;
 
 
     object = json_to_object(json);
     object = json_to_object(json);
-    return hashtable_iter_at(&object->hashtable, string_to_key(key));
+    return hashtable_iter_at(&object->hashtable, key);
 }
 }
 
 
 void *json_object_iter_next(json_t *json, void *iter)
 void *json_object_iter_next(json_t *json, void *iter)
@@ -248,20 +222,12 @@ void *json_object_iter_next(json_t *json, void *iter)
     return hashtable_iter_next(&object->hashtable, iter);
     return hashtable_iter_next(&object->hashtable, iter);
 }
 }
 
 
-const object_key_t *jsonp_object_iter_fullkey(void *iter)
-{
-    if(!iter)
-        return NULL;
-
-    return hashtable_iter_key(iter);
-}
-
 const char *json_object_iter_key(void *iter)
 const char *json_object_iter_key(void *iter)
 {
 {
     if(!iter)
     if(!iter)
         return NULL;
         return NULL;
 
 
-    return jsonp_object_iter_fullkey(iter)->key;
+    return hashtable_iter_key(iter);
 }
 }
 
 
 json_t *json_object_iter_value(void *iter)
 json_t *json_object_iter_value(void *iter)
@@ -274,38 +240,34 @@ json_t *json_object_iter_value(void *iter)
 
 
 int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
 int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
 {
 {
-    json_object_t *object;
-
     if(!json_is_object(json) || !iter || !value)
     if(!json_is_object(json) || !iter || !value)
         return -1;
         return -1;
 
 
-    object = json_to_object(json);
-    hashtable_iter_set(&object->hashtable, iter, value);
-
+    hashtable_iter_set(iter, value);
     return 0;
     return 0;
 }
 }
 
 
+void *json_object_key_to_iter(const char *key)
+{
+    if(!key)
+        return NULL;
+
+    return hashtable_key_to_iter(key);
+}
+
 static int json_object_equal(json_t *object1, json_t *object2)
 static int json_object_equal(json_t *object1, json_t *object2)
 {
 {
-    void *iter;
+    const char *key;
+    json_t *value1, *value2;
 
 
     if(json_object_size(object1) != json_object_size(object2))
     if(json_object_size(object1) != json_object_size(object2))
         return 0;
         return 0;
 
 
-    iter = json_object_iter(object1);
-    while(iter)
-    {
-        const char *key;
-        json_t *value1, *value2;
-
-        key = json_object_iter_key(iter);
-        value1 = json_object_iter_value(iter);
+    json_object_foreach(object1, key, value1) {
         value2 = json_object_get(object2, key);
         value2 = json_object_get(object2, key);
 
 
         if(!json_equal(value1, value2))
         if(!json_equal(value1, value2))
             return 0;
             return 0;
-
-        iter = json_object_iter_next(object1, iter);
     }
     }
 
 
     return 1;
     return 1;
@@ -314,50 +276,34 @@ static int json_object_equal(json_t *object1, json_t *object2)
 static json_t *json_object_copy(json_t *object)
 static json_t *json_object_copy(json_t *object)
 {
 {
     json_t *result;
     json_t *result;
-    void *iter;
+
+    const char *key;
+    json_t *value;
 
 
     result = json_object();
     result = json_object();
     if(!result)
     if(!result)
         return NULL;
         return NULL;
 
 
-    iter = json_object_iter(object);
-    while(iter)
-    {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    json_object_foreach(object, key, value)
         json_object_set_nocheck(result, key, value);
         json_object_set_nocheck(result, key, value);
 
 
-        iter = json_object_iter_next(object, iter);
-    }
-
     return result;
     return result;
 }
 }
 
 
 static json_t *json_object_deep_copy(json_t *object)
 static json_t *json_object_deep_copy(json_t *object)
 {
 {
     json_t *result;
     json_t *result;
-    void *iter;
+
+    const char *key;
+    json_t *value;
 
 
     result = json_object();
     result = json_object();
     if(!result)
     if(!result)
         return NULL;
         return NULL;
 
 
-    iter = json_object_iter(object);
-    while(iter)
-    {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    json_object_foreach(object, key, value)
         json_object_set_new_nocheck(result, key, json_deep_copy(value));
         json_object_set_new_nocheck(result, key, json_deep_copy(value));
 
 
-        iter = json_object_iter_next(object, iter);
-    }
-
     return result;
     return result;
 }
 }
 
 
@@ -703,6 +649,9 @@ int json_string_set_nocheck(json_t *json, const char *value)
     char *dup;
     char *dup;
     json_string_t *string;
     json_string_t *string;
 
 
+    if(!json_is_string(json) || !value)
+        return -1;
+
     dup = jsonp_strdup(value);
     dup = jsonp_strdup(value);
     if(!dup)
     if(!dup)
         return -1;
         return -1;
@@ -790,7 +739,12 @@ static json_t *json_integer_copy(json_t *integer)
 
 
 json_t *json_real(double value)
 json_t *json_real(double value)
 {
 {
-    json_real_t *real = jsonp_malloc(sizeof(json_real_t));
+    json_real_t *real;
+
+    if(isnan(value) || isinf(value))
+        return NULL;
+
+    real = jsonp_malloc(sizeof(json_real_t));
     if(!real)
     if(!real)
         return NULL;
         return NULL;
     json_init(&real->json, JSON_REAL);
     json_init(&real->json, JSON_REAL);
@@ -809,8 +763,8 @@ double json_real_value(const json_t *json)
 
 
 int json_real_set(json_t *json, double value)
 int json_real_set(json_t *json, double value)
 {
 {
-    if(!json_is_real(json))
-        return 0;
+    if(!json_is_real(json) || isnan(value) || isinf(value))
+        return -1;
 
 
     json_to_real(json)->value = value;
     json_to_real(json)->value = value;
 
 
@@ -838,7 +792,7 @@ static json_t *json_real_copy(json_t *real)
 double json_number_value(const json_t *json)
 double json_number_value(const json_t *json)
 {
 {
     if(json_is_integer(json))
     if(json_is_integer(json))
-        return json_integer_value(json);
+        return (double)json_integer_value(json);
     else if(json_is_real(json))
     else if(json_is_real(json))
         return json_real_value(json);
         return json_real_value(json);
     else
     else