aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Garzik <jeff@garzik.org>2010-11-26 16:13:32 -0500
committerJeff Garzik <jgarzik@redhat.com>2010-11-26 16:13:32 -0500
commit4575851ea331ea85946c30421541a493c6659445 (patch)
treed71f52e1328b2db7f39102bdf2fdfca99d8d31e6
parent750deb7a9912af13a83f737753fac24dc4dc5bd8 (diff)
Build jansson 1.3 in-tree, if not present on system.
-rw-r--r--Makefile.am10
-rw-r--r--compat/Makefile.am7
-rw-r--r--compat/jansson/LICENSE19
-rw-r--r--compat/jansson/Makefile.am18
-rw-r--r--compat/jansson/config.h73
-rw-r--r--compat/jansson/dump.c460
-rw-r--r--compat/jansson/hashtable.c375
-rw-r--r--compat/jansson/hashtable.h207
-rw-r--r--compat/jansson/jansson.h191
-rw-r--r--compat/jansson/jansson_private.h60
-rw-r--r--compat/jansson/load.c879
-rw-r--r--compat/jansson/strbuffer.c95
-rw-r--r--compat/jansson/strbuffer.h31
-rw-r--r--compat/jansson/utf.c190
-rw-r--r--compat/jansson/utf.h28
-rw-r--r--compat/jansson/util.h13
-rw-r--r--compat/jansson/value.c976
-rw-r--r--configure.ac13
18 files changed, 3643 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 585e89e..299787e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,13 @@
-INCLUDES = -pthread -fno-strict-aliasing
+if WANT_JANSSON
+JANSSON_INCLUDES= -I$(top_srcdir)/compat/jansson
+else
+JANSSON_INCLUDES=
+endif
+
+SUBDIRS = compat
+
+INCLUDES = -pthread -fno-strict-aliasing $(JANSSON_INCLUDES)
bin_PROGRAMS = minerd
diff --git a/compat/Makefile.am b/compat/Makefile.am
new file mode 100644
index 0000000..77af3c5
--- /dev/null
+++ b/compat/Makefile.am
@@ -0,0 +1,7 @@
+
+if WANT_JANSSON
+SUBDIRS = jansson
+else
+SUBDIRS =
+endif
+
diff --git a/compat/jansson/LICENSE b/compat/jansson/LICENSE
new file mode 100644
index 0000000..552b349
--- /dev/null
+++ b/compat/jansson/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/compat/jansson/Makefile.am b/compat/jansson/Makefile.am
new file mode 100644
index 0000000..94a583f
--- /dev/null
+++ b/compat/jansson/Makefile.am
@@ -0,0 +1,18 @@
+
+noinst_LIBRARIES = libjansson.a
+
+libjansson_a_SOURCES = \
+ config.h \
+ dump.c \
+ hashtable.c \
+ hashtable.h \
+ jansson.h \
+ jansson_private.h \
+ load.c \
+ strbuffer.c \
+ strbuffer.h \
+ utf.c \
+ utf.h \
+ util.h \
+ value.c
+
diff --git a/compat/jansson/config.h b/compat/jansson/config.h
new file mode 100644
index 0000000..43858aa
--- /dev/null
+++ b/compat/jansson/config.h
@@ -0,0 +1,73 @@
+/* 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 */
diff --git a/compat/jansson/dump.c b/compat/jansson/dump.c
new file mode 100644
index 0000000..dc27fbd
--- /dev/null
+++ b/compat/jansson/dump.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ *
+ * Jansson is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <jansson.h>
+#include "jansson_private.h"
+#include "strbuffer.h"
+#include "utf.h"
+
+#define MAX_INTEGER_STR_LENGTH 100
+#define MAX_REAL_STR_LENGTH 100
+
+typedef int (*dump_func)(const char *buffer, int size, void *data);
+
+struct string
+{
+ char *buffer;
+ int length;
+ int size;
+};
+
+static int dump_to_strbuffer(const char *buffer, int size, void *data)
+{
+ return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
+}
+
+static int dump_to_file(const char *buffer, int size, void *data)
+{
+ FILE *dest = (FILE *)data;
+ if(fwrite(buffer, size, 1, dest) != 1)
+ return -1;
+ return 0;
+}
+
+/* 256 spaces (the maximum indentation size) */
+static char whitespace[] = " ";
+
+static int dump_indent(unsigned long flags, int depth, int space, dump_func dump, void *data)
+{
+ if(JSON_INDENT(flags) > 0)
+ {
+ int i, ws_count = JSON_INDENT(flags);
+
+ if(dump("\n", 1, data))
+ return -1;
+
+ for(i = 0; i < depth; i++)
+ {
+ if(dump(whitespace, ws_count, data))
+ return -1;
+ }
+ }
+ else if(space && !(flags & JSON_COMPACT))
+ {
+ return dump(" ", 1, data);
+ }
+ return 0;
+}
+
+static int dump_string(const char *str, int ascii, dump_func dump, void *data)
+{
+ const char *pos, *end;
+ int32_t codepoint;
+
+ if(dump("\"", 1, data))
+ return -1;
+
+ end = pos = str;
+ while(1)
+ {
+ const char *text;
+ char seq[13];
+ int length;
+
+ while(*end)
+ {
+ end = utf8_iterate(pos, &codepoint);
+ if(!end)
+ return -1;
+
+ /* mandatory escape or control char */
+ if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
+ break;
+
+ /* non-ASCII */
+ if(ascii && codepoint > 0x7F)
+ break;
+
+ pos = end;
+ }
+
+ if(pos != str) {
+ if(dump(str, pos - str, data))
+ return -1;
+ }
+
+ if(end == pos)
+ break;
+
+ /* handle \, ", and control codes */
+ length = 2;
+ switch(codepoint)
+ {
+ case '\\': text = "\\\\"; break;
+ case '\"': text = "\\\""; break;
+ case '\b': text = "\\b"; break;
+ case '\f': text = "\\f"; break;
+ case '\n': text = "\\n"; break;
+ case '\r': text = "\\r"; break;
+ case '\t': text = "\\t"; break;
+ default:
+ {
+ /* codepoint is in BMP */
+ if(codepoint < 0x10000)
+ {
+ sprintf(seq, "\\u%04x", codepoint);
+ length = 6;
+ }
+
+ /* not in BMP -> construct a UTF-16 surrogate pair */
+ else
+ {
+ int32_t first, last;
+
+ codepoint -= 0x10000;
+ first = 0xD800 | ((codepoint & 0xffc00) >> 10);
+ last = 0xDC00 | (codepoint & 0x003ff);
+
+ sprintf(seq, "\\u%04x\\u%04x", first, last);
+ length = 12;
+ }
+
+ text = seq;
+ break;
+ }
+ }
+
+ if(dump(text, length, data))
+ return -1;
+
+ str = pos = end;
+ }
+
+ return dump("\"", 1, data);
+}
+
+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);
+}
+
+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;
+}
+
+static int do_dump(const json_t *json, unsigned long flags, int depth,
+ dump_func dump, void *data)
+{
+ int ascii = flags & JSON_ENSURE_ASCII ? 1 : 0;
+
+ switch(json_typeof(json)) {
+ case JSON_NULL:
+ return dump("null", 4, data);
+
+ case JSON_TRUE:
+ return dump("true", 4, data);
+
+ case JSON_FALSE:
+ return dump("false", 5, data);
+
+ case JSON_INTEGER:
+ {
+ char buffer[MAX_INTEGER_STR_LENGTH];
+ int size;
+
+ size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%d", json_integer_value(json));
+ if(size >= MAX_INTEGER_STR_LENGTH)
+ return -1;
+
+ return dump(buffer, size, data);
+ }
+
+ case JSON_REAL:
+ {
+ char buffer[MAX_REAL_STR_LENGTH];
+ int size;
+
+ size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
+ json_real_value(json));
+ if(size >= MAX_REAL_STR_LENGTH)
+ 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);
+ }
+
+ case JSON_STRING:
+ return dump_string(json_string_value(json), ascii, dump, data);
+
+ case JSON_ARRAY:
+ {
+ int i;
+ int n;
+ json_array_t *array;
+
+ /* detect circular references */
+ array = json_to_array(json);
+ if(array->visited)
+ goto array_error;
+ array->visited = 1;
+
+ n = json_array_size(json);
+
+ if(dump("[", 1, data))
+ goto array_error;
+ if(n == 0) {
+ array->visited = 0;
+ return dump("]", 1, data);
+ }
+ if(dump_indent(flags, depth + 1, 0, dump, data))
+ goto array_error;
+
+ for(i = 0; i < n; ++i) {
+ if(do_dump(json_array_get(json, i), flags, depth + 1,
+ dump, data))
+ goto array_error;
+
+ if(i < n - 1)
+ {
+ if(dump(",", 1, data) ||
+ dump_indent(flags, depth + 1, 1, dump, data))
+ goto array_error;
+ }
+ else
+ {
+ if(dump_indent(flags, depth, 0, dump, data))
+ goto array_error;
+ }
+ }
+
+ array->visited = 0;
+ return dump("]", 1, data);
+
+ array_error:
+ array->visited = 0;
+ return -1;
+ }
+
+ case JSON_OBJECT:
+ {
+ json_object_t *object;
+ void *iter;
+ const char *separator;
+ int separator_length;
+
+ if(flags & JSON_COMPACT) {
+ separator = ":";
+ separator_length = 1;
+ }
+ else {
+ separator = ": ";
+ separator_length = 2;
+ }
+
+ /* detect circular references */
+ object = json_to_object(json);
+ if(object->visited)
+ goto object_error;
+ object->visited = 1;
+
+ iter = json_object_iter((json_t *)json);
+
+ if(dump("{", 1, data))
+ goto object_error;
+ if(!iter) {
+ object->visited = 0;
+ return dump("}", 1, data);
+ }
+ if(dump_indent(flags, depth + 1, 0, dump, data))
+ goto object_error;
+
+ if(flags & JSON_SORT_KEYS || flags & JSON_PRESERVE_ORDER)
+ {
+ const object_key_t **keys;
+ unsigned int size;
+ unsigned int i;
+ int (*cmp_func)(const void *, const void *);
+
+ size = json_object_size(json);
+ keys = malloc(size * sizeof(object_key_t *));
+ if(!keys)
+ goto object_error;
+
+ i = 0;
+ while(iter)
+ {
+ keys[i] = jsonp_object_iter_fullkey(iter);
+ iter = json_object_iter_next((json_t *)json, iter);
+ i++;
+ }
+ assert(i == size);
+
+ if(flags & JSON_SORT_KEYS)
+ cmp_func = object_key_compare_keys;
+ else
+ cmp_func = object_key_compare_serials;
+
+ qsort(keys, size, sizeof(object_key_t *), cmp_func);
+
+ for(i = 0; i < size; i++)
+ {
+ const char *key;
+ json_t *value;
+
+ key = keys[i]->key;
+ value = json_object_get(json, key);
+ assert(value);
+
+ dump_string(key, ascii, dump, data);
+ if(dump(separator, separator_length, data) ||
+ do_dump(value, flags, depth + 1, dump, data))
+ {
+ free(keys);
+ goto object_error;
+ }
+
+ if(i < size - 1)
+ {
+ if(dump(",", 1, data) ||
+ dump_indent(flags, depth + 1, 1, dump, data))
+ {
+ free(keys);
+ goto object_error;
+ }
+ }
+ else
+ {
+ if(dump_indent(flags, depth, 0, dump, data))
+ {
+ free(keys);
+ goto object_error;
+ }
+ }
+ }
+
+ free(keys);
+ }
+ else
+ {
+ /* Don't sort keys */
+
+ while(iter)
+ {
+ void *next = json_object_iter_next((json_t *)json, iter);
+
+ dump_string(json_object_iter_key(iter), ascii, dump, data);
+ if(dump(separator, separator_length, data) ||
+ do_dump(json_object_iter_value(iter), flags, depth + 1,
+ dump, data))
+ goto object_error;
+
+ if(next)
+ {
+ if(dump(",", 1, data) ||
+ dump_indent(flags, depth + 1, 1, dump, data))
+ goto object_error;
+ }
+ else
+ {
+ if(dump_indent(flags, depth, 0, dump, data))
+ goto object_error;
+ }
+
+ iter = next;
+ }
+ }
+
+ object->visited = 0;
+ return dump("}", 1, data);
+
+ object_error:
+ object->visited = 0;
+ return -1;
+ }
+
+ default:
+ /* not reached */
+ return -1;
+ }
+}
+
+
+char *json_dumps(const json_t *json, unsigned long flags)
+{
+ strbuffer_t strbuff;
+ char *result;
+
+ if(!json_is_array(json) && !json_is_object(json))
+ return NULL;
+
+ if(strbuffer_init(&strbuff))
+ return NULL;
+
+ if(do_dump(json, flags, 0, dump_to_strbuffer, (void *)&strbuff)) {
+ strbuffer_close(&strbuff);
+ return NULL;
+ }
+
+ result = strdup(strbuffer_value(&strbuff));
+ strbuffer_close(&strbuff);
+
+ return result;
+}
+
+int json_dumpf(const json_t *json, FILE *output, unsigned long flags)
+{
+ if(!json_is_array(json) && !json_is_object(json))
+ return -1;
+
+ return do_dump(json, flags, 0, dump_to_file, (void *)output);
+}
+
+int json_dump_file(const json_t *json, const char *path, unsigned long flags)
+{
+ int result;
+
+ FILE *output = fopen(path, "w");
+ if(!output)
+ return -1;
+
+ result = json_dumpf(json, output, flags);
+
+ fclose(output);
+ return result;
+}
diff --git a/compat/jansson/hashtable.c b/compat/jansson/hashtable.c
new file mode 100644
index 0000000..a312047
--- /dev/null
+++ b/compat/jansson/hashtable.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include "hashtable.h"
+
+typedef struct hashtable_list list_t;
+typedef struct hashtable_pair pair_t;
+typedef struct hashtable_bucket bucket_t;
+
+#define container_of(ptr_, type_, member_) \
+ ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
+
+#define list_to_pair(list_) container_of(list_, pair_t, list)
+
+static inline void list_init(list_t *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void list_insert(list_t *list, list_t *node)
+{
+ node->next = list;
+ node->prev = list->prev;
+ list->prev->next = node;
+ list->prev = node;
+}
+
+static inline void list_remove(list_t *list)
+{
+ list->prev->next = list->next;
+ list->next->prev = list->prev;
+}
+
+static inline int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
+{
+ return bucket->first == &hashtable->list && bucket->first == bucket->last;
+}
+
+static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
+ list_t *list)
+{
+ if(bucket_is_empty(hashtable, bucket))
+ {
+ list_insert(&hashtable->list, list);
+ bucket->first = bucket->last = list;
+ }
+ else
+ {
+ list_insert(bucket->first, list);
+ bucket->first = list;
+ }
+}
+
+static unsigned int primes[] = {
+ 5, 13, 23, 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,
+ 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
+ 12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
+ 805306457, 1610612741
+};
+static const unsigned int num_primes = sizeof(primes) / sizeof(unsigned int);
+
+static inline unsigned int num_buckets(hashtable_t *hashtable)
+{
+ return primes[hashtable->num_buckets];
+}
+
+
+static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
+ const void *key, unsigned int hash)
+{
+ list_t *list;
+ pair_t *pair;
+
+ if(bucket_is_empty(hashtable, bucket))
+ return NULL;
+
+ list = bucket->first;
+ while(1)
+ {
+ pair = list_to_pair(list);
+ if(pair->hash == hash && hashtable->cmp_keys(pair->key, key))
+ return pair;
+
+ if(list == bucket->last)
+ break;
+
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+/* returns 0 on success, -1 if key was not found */
+static int hashtable_do_del(hashtable_t *hashtable,
+ const void *key, unsigned int hash)
+{
+ pair_t *pair;
+ bucket_t *bucket;
+ unsigned int index;
+
+ index = hash % num_buckets(hashtable);
+ bucket = &hashtable->buckets[index];
+
+ pair = hashtable_find_pair(hashtable, bucket, key, hash);
+ if(!pair)
+ return -1;
+
+ if(&pair->list == bucket->first && &pair->list == bucket->last)
+ bucket->first = bucket->last = &hashtable->list;
+
+ else if(&pair->list == bucket->first)
+ bucket->first = pair->list.next;
+
+ else if(&pair->list == bucket->last)
+ bucket->last = pair->list.prev;
+
+ list_remove(&pair->list);
+
+ if(hashtable->free_key)
+ hashtable->free_key(pair->key);
+ if(hashtable->free_value)
+ hashtable->free_value(pair->value);
+
+ free(pair);
+ hashtable->size--;
+
+ return 0;
+}
+
+static void hashtable_do_clear(hashtable_t *hashtable)
+{
+ list_t *list, *next;
+ pair_t *pair;
+
+ for(list = hashtable->list.next; list != &hashtable->list; list = next)
+ {
+ next = list->next;
+ pair = list_to_pair(list);
+ if(hashtable->free_key)
+ hashtable->free_key(pair->key);
+ if(hashtable->free_value)
+ hashtable->free_value(pair->value);
+ free(pair);
+ }
+}
+
+static int hashtable_do_rehash(hashtable_t *hashtable)
+{
+ list_t *list, *next;
+ pair_t *pair;
+ unsigned int i, index, new_size;
+
+ free(hashtable->buckets);
+
+ hashtable->num_buckets++;
+ new_size = num_buckets(hashtable);
+
+ hashtable->buckets = malloc(new_size * sizeof(bucket_t));
+ if(!hashtable->buckets)
+ return -1;
+
+ for(i = 0; i < num_buckets(hashtable); i++)
+ {
+ hashtable->buckets[i].first = hashtable->buckets[i].last =
+ &hashtable->list;
+ }
+
+ list = hashtable->list.next;
+ list_init(&hashtable->list);
+
+ for(; list != &hashtable->list; list = next) {
+ next = list->next;
+ pair = list_to_pair(list);
+ index = pair->hash % new_size;
+ insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list);
+ }
+
+ return 0;
+}
+
+
+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 = malloc(sizeof(hashtable_t));
+ if(!hashtable)
+ return NULL;
+
+ if(hashtable_init(hashtable, hash_key, cmp_keys, free_key, free_value))
+ {
+ free(hashtable);
+ return NULL;
+ }
+
+ return hashtable;
+}
+
+void hashtable_destroy(hashtable_t *hashtable)
+{
+ hashtable_close(hashtable);
+ 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)
+{
+ unsigned int i;
+
+ hashtable->size = 0;
+ hashtable->num_buckets = 0; /* index to primes[] */
+ hashtable->buckets = malloc(num_buckets(hashtable) * sizeof(bucket_t));
+ if(!hashtable->buckets)
+ return -1;
+
+ 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++)
+ {
+ hashtable->buckets[i].first = hashtable->buckets[i].last =
+ &hashtable->list;
+ }
+
+ return 0;
+}
+
+void hashtable_close(hashtable_t *hashtable)
+{
+ hashtable_do_clear(hashtable);
+ free(hashtable->buckets);
+}
+
+int hashtable_set(hashtable_t *hashtable, void *key, void *value)
+{
+ pair_t *pair;
+ bucket_t *bucket;
+ unsigned int hash, index;
+
+ /* rehash if the load ratio exceeds 1 */
+ if(hashtable->size >= num_buckets(hashtable))
+ if(hashtable_do_rehash(hashtable))
+ return -1;
+
+ hash = hashtable->hash_key(key);
+ index = hash % num_buckets(hashtable);
+ bucket = &hashtable->buckets[index];
+ pair = hashtable_find_pair(hashtable, bucket, key, hash);
+
+ if(pair)
+ {
+ if(hashtable->free_key)
+ hashtable->free_key(key);
+ if(hashtable->free_value)
+ hashtable->free_value(pair->value);
+ pair->value = value;
+ }
+ else
+ {
+ pair = malloc(sizeof(pair_t));
+ if(!pair)
+ return -1;
+
+ pair->key = key;
+ pair->value = value;
+ pair->hash = hash;
+ list_init(&pair->list);
+
+ insert_to_bucket(hashtable, bucket, &pair->list);
+
+ hashtable->size++;
+ }
+ return 0;
+}
+
+void *hashtable_get(hashtable_t *hashtable, const void *key)
+{
+ pair_t *pair;
+ unsigned int hash;
+ bucket_t *bucket;
+
+ hash = hashtable->hash_key(key);
+ bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
+
+ pair = hashtable_find_pair(hashtable, bucket, key, hash);
+ if(!pair)
+ return NULL;
+
+ return pair->value;
+}
+
+int hashtable_del(hashtable_t *hashtable, const void *key)
+{
+ unsigned int hash = hashtable->hash_key(key);
+ return hashtable_do_del(hashtable, key, hash);
+}
+
+void hashtable_clear(hashtable_t *hashtable)
+{
+ unsigned int i;
+
+ hashtable_do_clear(hashtable);
+
+ for(i = 0; i < num_buckets(hashtable); i++)
+ {
+ hashtable->buckets[i].first = hashtable->buckets[i].last =
+ &hashtable->list;
+ }
+
+ list_init(&hashtable->list);
+ hashtable->size = 0;
+}
+
+void *hashtable_iter(hashtable_t *hashtable)
+{
+ return hashtable_iter_next(hashtable, &hashtable->list);
+}
+
+void *hashtable_iter_at(hashtable_t *hashtable, const void *key)
+{
+ pair_t *pair;
+ unsigned int hash;
+ bucket_t *bucket;
+
+ hash = hashtable->hash_key(key);
+ bucket = &hashtable->buckets[hash % num_buckets(hashtable)];
+
+ pair = hashtable_find_pair(hashtable, bucket, key, hash);
+ if(!pair)
+ return NULL;
+
+ return &pair->list;
+}
+
+void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
+{
+ list_t *list = (list_t *)iter;
+ if(list->next == &hashtable->list)
+ return NULL;
+ return list->next;
+}
+
+void *hashtable_iter_key(void *iter)
+{
+ pair_t *pair = list_to_pair((list_t *)iter);
+ return pair->key;
+}
+
+void *hashtable_iter_value(void *iter)
+{
+ pair_t *pair = list_to_pair((list_t *)iter);
+ return pair->value;
+}
+
+void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value)
+{
+ pair_t *pair = list_to_pair((list_t *)iter);
+
+ if(hashtable->free_value)
+ hashtable->free_value(pair->value);
+
+ pair->value = value;
+}
diff --git a/compat/jansson/hashtable.h b/compat/jansson/hashtable.h
new file mode 100644
index 0000000..f03a769
--- /dev/null
+++ b/compat/jansson/hashtable.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2009, 2010 Petri Lehtinen <petri@digip.org>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#ifndef HASHTABLE_H
+#define HASHTABLE_H
+
+typedef unsigned int (*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 *prev;
+ struct hashtable_list *next;
+};
+
+struct hashtable_pair {
+ void *key;
+ void *value;
+ unsigned int hash;
+ struct hashtable_list list;
+};
+
+struct hashtable_bucket {
+ struct hashtable_list *first;
+ struct hashtable_list *last;
+};
+
+typedef struct hashtable {
+ unsigned int size;
+ struct hashtable_bucket *buckets;
+ unsigned int num_buckets; /* index to primes[] */
+ struct hashtable_list list;
+
+ key_hash_fn hash_key;