Initial import

This commit is contained in:
Petri Lehtinen 2009-02-06 20:26:27 +02:00
commit 17a69c2d66
8 changed files with 1361 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.o
*.a

92
include/jansson.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef JANSSON_H
#define JANSSON_H
#include <stdio.h>
#include <stdint.h>
/* types */
typedef enum {
JSON_OBJECT,
JSON_ARRAY,
JSON_STRING,
JSON_NUMBER,
JSON_TRUE,
JSON_FALSE,
JSON_NULL
} json_type;
typedef struct {
json_type type;
unsigned long refcount;
} json_t;
#define json_typeof(json) ((json)->type)
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT)
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY)
#define json_is_string(json) (json && json_typeof(json) == JSON_STRING)
#define json_is_number(json) (json && json_typeof(json) == JSON_NUMBER)
#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE)
#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE)
#define json_is_null(json) (json && json_typeof(json) == JSON_NULL)
/* construction, destruction, reference counting */
json_t *json_object(void);
json_t *json_array(void);
json_t *json_string(const char *value);
json_t *json_number(double value);
json_t *json_true(void);
json_t *json_false(void);
json_t *json_null(void);
json_t *json_clone(json_t *json);
static inline json_t *json_incref(json_t *json)
{
if(json)
++json->refcount;
return json;
}
/* do not call json_delete directly */
void json_delete(json_t *json);
static inline void json_decref(json_t *json)
{
if(json && --json->refcount == 0)
json_delete(json);
}
/* getters, setters, manipulation */
json_t *json_object_get(const json_t *object, const char *key);
int json_object_set(json_t *object, const char *key, json_t *value);
int json_object_del(json_t *object, const char *key);
unsigned int json_array_size(const json_t *array);
json_t *json_array_get(const json_t *array, unsigned int index);
int json_array_set(json_t *array, unsigned int index, json_t *value);
int json_array_append(json_t *array, json_t *value);
const char *json_string_value(const json_t *json);
double json_number_value(const json_t *json);
/* loading, printing */
const char *json_get_error(void);
json_t *json_load(const char *path);
json_t *json_loads(const char *input);
json_t *json_loadf(FILE *input);
#define JSON_INDENT(n) (n & 0xFF)
#define JSON_SORT_KEYS 0x100
int json_dump(const json_t *json, const char *path, uint32_t flags);
char *json_dumps(const json_t *json, uint32_t flags);
int json_dumpf(const json_t *json, FILE *output, uint32_t flags);
#endif

14
src/Makefile Normal file
View File

@ -0,0 +1,14 @@
CFLAGS = -I../include -std=c99 -Wall -Wextra -Werror -g -O0
OBJS = dump.o load.o value.o hashtable.o
LIB = libjansson.a
all: $(LIB)
$(LIB): $(OBJS)
ar crsv $@ $^
clean:
rm -f -- $(OBJS)
rm -f -- $(LIB)

46
src/dump.c Normal file
View File

@ -0,0 +1,46 @@
#include <jansson.h>
char *json_dumps(const json_t *json, uint32_t flags)
{
(void)flags;
switch(json_typeof(json)) {
case JSON_NULL:
printf("null");
break;
case JSON_TRUE:
printf("true");
break;
case JSON_FALSE:
printf("false");
break;
case JSON_NUMBER:
printf("%f", json_number_value(json));
break;
case JSON_STRING:
printf("%s", json_string_value(json));
break;
case JSON_ARRAY: {
int i, n = json_array_size(json);
printf("[");
for(i = 0; i < n; ++i) {
json_dumps(json_array_get(json, i), 0);
if(i < n - 1)
printf(", ");
}
printf("]");
break;
}
default:
printf("<object>");
break;
}
return NULL;
}

326
src/hashtable.c Normal file
View File

@ -0,0 +1,326 @@
/*
* Copyright (c) 2009 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 <stdlib.h>
#include "hashtable.h"
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
typedef struct list_t {
struct list_t *prev;
struct list_t *next;
} list_t;
typedef struct {
void *key;
void *value;
unsigned int hash;
list_t list;
} pair_t;
#define list_to_pair(list_) container_of(list_, pair_t, list)
typedef struct {
list_t *first;
list_t *last;
} bucket_t;
struct hashtable {
unsigned int size;
bucket_t *buckets;
unsigned int num_buckets; /* index to primes[] */
list_t 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;
};
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 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_new(key_hash_fn hash_key, key_cmp_fn cmp_keys,
free_fn free_key, free_fn free_value)
{
unsigned int i;
hashtable_t *hashtable = malloc(sizeof(hashtable_t));
if(!hashtable)
return NULL;
hashtable->size = 0;
hashtable->num_buckets = 0; /* index to primes[] */
hashtable->buckets = malloc(num_buckets(hashtable) * sizeof(bucket_t));
if(!hashtable->buckets)
{
free(hashtable);
return NULL;
}
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 hashtable;
}
void hashtable_free(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);
}
free(hashtable->buckets);
free(hashtable);
}
int hashtable_set(hashtable_t *hashtable, void *key, void *value)
{
pair_t *pair;
bucket_t *bucket;
unsigned int hash, index;
hash = hashtable->hash_key(key);
/* if the key already exists, delete it */
hashtable_do_del(hashtable, key, hash);
/* rehash if the load ratio exceeds 1 */
if(hashtable->size >= num_buckets(hashtable))
if(hashtable_do_rehash(hashtable))
return -1;
pair = malloc(sizeof(pair_t));
if(!pair)
return -1;
pair->key = key;
pair->value = value;
pair->hash = hash;
index = hash % num_buckets(hashtable);
bucket = &hashtable->buckets[index];
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_iter(hashtable_t *hashtable)
{
return hashtable_iter_next(hashtable, &hashtable->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;
}

121
src/hashtable.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2009 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 struct hashtable hashtable_t;
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);
/**
* hashtable_new - 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_free when it's no longer used.
*/
hashtable_t *hashtable_new(key_hash_fn hash_key, key_cmp_fn cmp_keys,
free_fn free_key, free_fn free_value);
/**
* hashtable_free - Destroy a hashtable object
*
* @hashtable: The hashtable
*/
void hashtable_free(hashtable_t *hashtable);
/**
* hashtable_set - Add/modify value in hashtable
*
* @hashtable: The hashtable object
* @key: The key
* @value: The value
*
* 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".
*
* Returns 0 on success, -1 on failure (out of memory).
*/
int hashtable_set(hashtable_t *hashtable, void *key, void *value);
/**
* hashtable_get - Get a value associated with a key
*
* @hashtable: The hashtable object
* @key: The key
*
* Returns value if it is found, or NULL otherwise.
*/
void *hashtable_get(hashtable_t *hashtable, const void *key);
/**
* hashtable_del - Remove a value from the hashtable
*
* @hashtable: The hashtable object
* @key: The key
*
* Returns 0 on success, or -1 if the key was not found.
*/
int hashtable_del(hashtable_t *hashtable, const void *key);
/**
* hashtable_iter - Iterate over hashtable
*
* @hashtable: The hashtable object
*
* Returns an opaque iterator to the first element in the hashtable.
* The iterator should be passed to hashtable_iter_* functions.
* The hashtable items are not iterated over in any particular order.
*
* There's no need to free the iterator in any way. The iterator is
* valid as long as the item that is referenced by the iterator is not
* deleted. Other values may be added or deleted. In particular,
* hashtable_iter_next() may be called on an iterator, and after that
* the key/value pair pointed by the old iterator may be deleted.
*/
void *hashtable_iter(hashtable_t *hashtable);
/**
* hashtable_iter_next - Advance an iterator
*
* @hashtable: The hashtable object
* @iter: The iterator
*
* Returns a new iterator pointing to the next element in the
* hashtable or NULL if the whole hastable has been iterated over.
*/
void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
/**
* hashtable_iter_key - Retrieve the key pointed by an iterator
*
* @iter: The iterator
*/
void *hashtable_iter_key(void *iter);
/**
* hashtable_iter_value - Retrieve the value pointed by an iterator
*
* @iter: The iterator
*/
void *hashtable_iter_value(void *iter);
#endif

443
src/load.c Normal file
View File

@ -0,0 +1,443 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <jansson.h>
#define JSON_TOKEN_INVALID -1
#define JSON_TOKEN_EOF 0
#define JSON_TOKEN_STRING 256
#define JSON_TOKEN_NUMBER 257
#define JSON_TOKEN_TRUE 258
#define JSON_TOKEN_FALSE 259
#define JSON_TOKEN_NULL 260
typedef struct {
const char *input;
const char *start;
int token;
int line, column;
union {
char *string;
double number;
} value;
} json_lex;
/*** error reporting ***/
static __thread char *json_error_msg = NULL;
static void json_set_error(const json_lex *lex, const char *msg)
{
free(json_error_msg);
if(*lex->start)
asprintf(&json_error_msg, "%s near '%.*s' on line %d", msg,
(int)(lex->input - lex->start), lex->start, lex->line);
else
asprintf(&json_error_msg, "%s near end of file", msg);
}
const char *json_get_error(void)
{
if(!json_error_msg)
json_error_msg = strdup("success");
return json_error_msg;
}
/*** lexical analyzer ***/
static void json_scan_string(json_lex *lex)
{
/* skip the " */
const char *p = lex->input + 1;
char *t;
lex->token = JSON_TOKEN_INVALID;
while(*p != '"') {
if(*p == '\0') {
/* unterminated string literal */
goto out;
}
if(0 <= *p && *p <= 31) {
/* control character */
goto out;
}
else if(*p == '\\') {
p++;
if(*p == 'u') {
p++;
for(int i = 0; i < 4; i++, p++) {
if(!isxdigit(*p))
goto out;
}
}
if(*p == '"' || *p == '\\' || *p == '/' || *p == 'b' ||
*p == 'f' || *p == 'n' || *p == 'r' || *p == 't')
p++;
else
goto out;
}
else
p++;
}
/* the actual value is at most of the same length as the source
string */
lex->value.string = malloc(p - lex->start);
if(!lex->value.string) {
/* this is not very nice, since TOKEN_INVALID is returned */
goto out;
}
/* the target */
t = lex->value.string;
p = lex->input + 1;
while(*p != '"') {
if(*p == '\\') {
p++;
if(*p == 'u') {
/* TODO: \uXXXX not supported yet */
free(lex->value.string);
lex->value.string = NULL;
goto out;
} else {
switch(*p) {
case '"': case '\\': case '/':
*t = *p; break;
case 'b': *t = '\b'; break;
case 'f': *t = '\f'; break;
case 'n': *t = '\n'; break;
case 'r': *t = '\r'; break;
case 't': *t = '\t'; break;
default: assert(0);
}
}
}
else
*t = *p;
t++;
p++;
}
/* skip the " */
p++;
*t = '\0';
lex->token = JSON_TOKEN_STRING;
out:
lex->input = p;
}
static void json_scan_number(json_lex *lex)
{
const char *p = lex->input;
char *end;
lex->token = JSON_TOKEN_INVALID;
if(*p == '-')
p++;
if(*p == '0')
p++;
else /* *p != '0' */ {
p++;
while(isdigit(*p))
p++;
}
if(*p == '.') {
p++;
if(!isdigit(*(p++)))
goto out;
while(isdigit(*p))
p++;
}
if(*p == 'E' || *p == 'e') {
p++;
if(*p == '+' || *p == '-')
p++;
if(!isdigit(*(p++)))
goto out;
while(isdigit(*p))
p++;
}
lex->token = JSON_TOKEN_NUMBER;
lex->value.number = strtod(lex->start, &end);
assert(end == p);
out:
lex->input = p;
}
static int json_lex_scan(json_lex *lex)
{
char c;
if(lex->token == JSON_TOKEN_STRING) {
free(lex->value.string);
lex->value.string = NULL;
}
while(isspace(*lex->input)) {
if(*lex->input == '\n')
lex->line++;
lex->input++;
}
lex->start = lex->input;
c = *lex->input;
if(c == '\0')
lex->token = JSON_TOKEN_EOF;
else if(c == '{' || c == '}' || c == '[' || c == ']' ||
c == ':' || c == ',') {
lex->token = c;
lex->input++;
}
else if(c == '"')
json_scan_string(lex);
else if(isdigit(c) || c == '-')
json_scan_number(lex);
else if(isalpha(c)) {
/* eat up the whole identifier for clearer error messages */
int len;
while(isalpha(*lex->input))
lex->input++;
len = lex->input - lex->start;
if(strncmp(lex->start, "true", len) == 0)
lex->token = JSON_TOKEN_TRUE;
else if(strncmp(lex->start, "false", len) == 0)
lex->token = JSON_TOKEN_FALSE;
else if(strncmp(lex->start, "null", len) == 0)
lex->token = JSON_TOKEN_NULL;
else
lex->token = JSON_TOKEN_INVALID;
}
else {
lex->token = JSON_TOKEN_INVALID;
lex->input++;
}
return lex->token;
}
static int json_lex_init(json_lex *lex, const char *input)
{
lex->input = input;
lex->token = JSON_TOKEN_INVALID;
lex->line = 1;
json_lex_scan(lex);
return 0;
}
static void json_lex_close(json_lex *lex)
{
if(lex->token == JSON_TOKEN_STRING)
free(lex->value.string);
}
/*** parser ***/
static json_t *json_parse(json_lex *lex);
static json_t *json_parse_object(json_lex *lex)
{
json_t *object = json_object();
if(!object)
return NULL;
json_lex_scan(lex);
while(1) {
char *key;
json_t *value;
if(lex->token != JSON_TOKEN_STRING) {
json_set_error(lex, "string expected");
goto error;
}
key = strdup(lex->value.string);
if(!key)
return NULL;
json_lex_scan(lex);
if(lex->token != ':') {
json_set_error(lex, "':' expected");
goto error;
}
json_lex_scan(lex);
value = json_parse(lex);
if(!value)
goto error;
if(json_object_set(object, key, value)) {
json_decref(value);
goto error;
}
json_decref(value);
free(key);
if(lex->token != ',')
break;
json_lex_scan(lex);
}
if(lex->token != '}') {
json_set_error(lex, "'}' expected");
goto error;
}
return object;
error:
json_decref(object);
return NULL;
}
static json_t *json_parse_array(json_lex *lex)
{
json_t *array = json_array();
if(!array)
return NULL;
json_lex_scan(lex);
if(lex->token != ']') {
while(1) {
json_t *elem = json_parse(lex);
if(!elem)
goto error;
if(json_array_append(array, elem)) {
json_decref(elem);
goto error;
}
json_decref(elem);
if(lex->token != ',')
break;
json_lex_scan(lex);
}
}
if(lex->token != ']') {
json_set_error(lex, "']' expected");
goto error;
}
return array;
error:
json_decref(array);
return NULL;
}
static json_t *json_parse(json_lex *lex)
{
json_t *json;
switch(lex->token) {
case JSON_TOKEN_STRING: {
json = json_string(lex->value.string);
break;
}
case JSON_TOKEN_NUMBER: {
json = json_number(lex->value.number);
break;
}
case JSON_TOKEN_TRUE:
json = json_true();
break;
case JSON_TOKEN_FALSE:
json = json_false();
break;
case JSON_TOKEN_NULL:
json = json_null();
break;
case '{':
json = json_parse_object(lex);
break;
case '[':
json = json_parse_array(lex);
break;
case JSON_TOKEN_INVALID:
json_set_error(lex, "invalid token");
return NULL;
default:
json_set_error(lex, "unexpected token");
return NULL;
}
if(!json)
return NULL;
json_lex_scan(lex);
return json;
}
json_t *json_loads(const char *string)
{
json_lex lex;
json_t *result = NULL;
if(json_lex_init(&lex, string))
return NULL;
if(lex.token != '[' && lex.token != '{') {
json_set_error(&lex, "'[' or '{' expected");
goto out;
}
result = json_parse(&lex);
if(!result)
goto out;
if(lex.token != JSON_TOKEN_EOF) {
json_set_error(&lex, "end of file expected");
json_decref(result);
result = NULL;
}
out:
json_lex_close(&lex);
return result;
}

317
src/value.c Normal file
View File

@ -0,0 +1,317 @@
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <jansson.h>
#include "hashtable.h"
#define max(a, b) ((a) > (b) ? (a) : (b))
#define container_of(ptr_, type_, member_) \
((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
typedef struct {
json_t json;
hashtable_t *hashtable;
} json_object_t;
typedef struct {
json_t json;
unsigned int size;
unsigned int entries;
json_t **table;
} json_array_t;
typedef struct {
json_t json;
char *value;
} json_string_t;
typedef struct {
json_t json;
double value;
} json_number_t;
#define json_to_object(json_) container_of(json_, json_object_t, json)
#define json_to_array(json_) container_of(json_, json_array_t, json)
#define json_to_string(json_) container_of(json_, json_string_t, json)
#define json_to_number(json_) container_of(json_, json_number_t, json)
static inline void json_init(json_t *json, json_type type)
{
json->type = type;
json->refcount = 1;
}
/*** object ***/
static unsigned int hash_string(const void *key)
{
const char *str = (const char *)key;
unsigned int hash = 5381;
unsigned int c;
while((c = (unsigned int)*str))
{
hash = ((hash << 5) + hash) + c;
str++;
}
return hash;
}
static int string_equal(const void *key1, const void *key2)
{
return strcmp((const char *)key1, (const char *)key2) == 0;
}
static void value_decref(void *value)
{
json_decref((json_t *)value);
}
json_t *json_object(void)
{
json_object_t *object = malloc(sizeof(json_object_t));
if(!object)
return NULL;
json_init(&object->json, JSON_OBJECT);
object->hashtable =
hashtable_new(hash_string, string_equal, free, value_decref);
if(!object->hashtable)
{
free(object);
return NULL;
}
return &object->json;
}
static void json_delete_object(json_object_t *object)
{
hashtable_free(object->hashtable);
free(object);
}
json_t *json_object_get(const json_t *json, const char *key)
{
json_object_t *object;
if(!json_is_object(json))
return NULL;
return hashtable_get(object->hashtable, key);
}
int json_object_del(json_t *json, const char *key)
{
json_object_t *object;
if(!json_is_object(json))
return -1;
object = json_to_object(json);
return hashtable_del(object->hashtable, key);
}
int json_object_set(json_t *json, const char *key, json_t *value)
{
json_object_t *object;
if(!json_is_object(json))
return -1;
object = json_to_object(json);
return hashtable_set(object->hashtable, strdup(key), json_incref(value));
}
/*** array ***/
json_t *json_array(void)
{
json_array_t *array = malloc(sizeof(json_array_t));
if(!array)
return NULL;
json_init(&array->json, JSON_ARRAY);
array->entries = 0;
array->size = 0;
array->table = NULL;
return &array->json;
}
static void json_delete_array(json_array_t *array)
{
unsigned int i;
for(i = 0; i < array->entries; i++)
json_decref(array->table[i]);
free(array->table);
free(array);
}
unsigned int json_array_size(const json_t *json)
{
if(!json_is_array(json))
return 0;
return json_to_array(json)->entries;
}
json_t *json_array_get(const json_t *json, unsigned int index)
{
json_array_t *array;
if(!json_is_array(json))
return NULL;
array = json_to_array(json);
if(index >= array->size)
return NULL;
return array->table[index];
}
int json_array_set(json_t *json, unsigned int index, json_t *value)
{
json_array_t *array;
if(!json_is_array(json))
return -1;
array = json_to_array(json);
if(index >= array->size)
return -1;
array->table[index] = json_incref(value);
return 0;
}
int json_array_append(json_t *json, json_t *value)
{
json_array_t *array;
if(!json_is_array(json))
return -1;
array = json_to_array(json);
if(array->entries == array->size) {
array->size = max(8, array->size * 2);
array->table = realloc(array->table, array->size * sizeof(json_t *));
if(!array->table)
return -1;
}
array->table[array->entries] = json_incref(value);
array->entries++;
return 0;
}
/*** string ***/
json_t *json_string(const char *value)
{
json_string_t *string = malloc(sizeof(json_string_t));
if(!string)
return NULL;
json_init(&string->json, JSON_STRING);
string->value = strdup(value);
return &string->json;
}
const char *json_string_value(const json_t *json)
{
if(!json_is_string(json))
return NULL;
return json_to_string(json)->value;
}
static void json_delete_string(json_string_t *string)
{
free(string->value);
free(string);
}
json_t *json_number(double value)
{
json_number_t *number = malloc(sizeof(json_number_t));
if(!number)
return NULL;
json_init(&number->json, JSON_NUMBER);
number->value = value;
return &number->json;
}
/*** number ***/
double json_number_value(const json_t *json)
{
if(!json_is_number(json))
return 0.0;
return json_to_number(json)->value;
}
static void json_delete_number(json_number_t *number)
{
free(number);
}
/*** simple values ***/
json_t *json_true(void)
{
static json_t the_true = {
.type = JSON_TRUE,
.refcount = 1
};
return json_incref(&the_true);
}
json_t *json_false(void)
{
static json_t the_false = {
.type = JSON_FALSE,
.refcount = 1
};
return json_incref(&the_false);
}
json_t *json_null(void)
{
static json_t the_null = {
.type = JSON_NULL,
.refcount = 1
};
return json_incref(&the_null);
}
/*** deletion ***/
void json_delete(json_t *json)
{
if(json_is_object(json))
json_delete_object(json_to_object(json));
else if(json_is_array(json))
json_delete_array(json_to_array(json));
else if(json_is_string(json))
json_delete_string(json_to_string(json));
else if(json_is_number(json))
json_delete_number(json_to_number(json));
/* json_delete is not called for true, false or null */
}