Merge pull request #520 from Mephistophiles/getn
Add support getn, setn functions
This commit is contained in:
commit
9a0fc069bf
@ -508,14 +508,15 @@ if (NOT JANSSON_WITHOUT_TESTS)
|
||||
|
||||
set(api_tests
|
||||
test_array
|
||||
test_copy
|
||||
test_chaos
|
||||
test_copy
|
||||
test_dump
|
||||
test_dump_callback
|
||||
test_equal
|
||||
test_fixed_size
|
||||
test_load
|
||||
test_loadb
|
||||
test_load_callback
|
||||
test_loadb
|
||||
test_number
|
||||
test_object
|
||||
test_pack
|
||||
|
160
doc/apiref.rst
160
doc/apiref.rst
@ -648,6 +648,15 @@ allowed in object keys.
|
||||
Get a value corresponding to *key* from *object*. Returns *NULL* if
|
||||
*key* is not found and on error.
|
||||
|
||||
.. function:: json_t *json_object_getn(const json_t *object, const char *key, size_t key_len)
|
||||
|
||||
.. refcounting:: borrow
|
||||
|
||||
Like :func:`json_object_get`, but give the fixed-length *key* with length *key_len*.
|
||||
See :ref:`fixed_length_keys` for details.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: int json_object_set(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Set the value of *key* to *value* in *object*. *key* must be a
|
||||
@ -655,6 +664,13 @@ allowed in object keys.
|
||||
already is a value for *key*, it is replaced by the new value.
|
||||
Returns 0 on success and -1 on error.
|
||||
|
||||
.. function:: int json_object_setn(json_t *object, const char *key, size_t key_len, json_t *value)
|
||||
|
||||
Like :func:`json_object_set`, but give the fixed-length *key* with length *key_len*.
|
||||
See :ref:`fixed_length_keys` for details.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :func:`json_object_set`, but doesn't check that *key* is
|
||||
@ -662,12 +678,26 @@ allowed in object keys.
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. function:: int json_object_setn_nocheck(json_t *object, const char *key, size_t key_len, json_t *value)
|
||||
|
||||
Like :func:`json_object_set_nocheck`, but give the fixed-length *key* with length *key_len*.
|
||||
See :ref:`fixed_length_keys` for details.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: int json_object_set_new(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :func:`json_object_set()` but steals the reference to
|
||||
*value*. This is useful when *value* is newly created and not used
|
||||
after the call.
|
||||
|
||||
.. function:: int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value)
|
||||
|
||||
Like :func:`json_object_set_new`, but give the fixed-length *key* with length *key_len*.
|
||||
See :ref:`fixed_length_keys` for details.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value)
|
||||
|
||||
Like :func:`json_object_set_new`, but doesn't check that *key* is
|
||||
@ -675,12 +705,26 @@ allowed in object keys.
|
||||
really is the case (e.g. you have already checked it by other
|
||||
means).
|
||||
|
||||
.. function:: int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len, json_t *value)
|
||||
|
||||
Like :func:`json_object_set_new_nocheck`, but give the fixed-length *key* with length *key_len*.
|
||||
See :ref:`fixed_length_keys` for details.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: int json_object_del(json_t *object, const char *key)
|
||||
|
||||
Delete *key* from *object* if it exists. Returns 0 on success, or
|
||||
-1 if *key* was not found. The reference count of the removed value
|
||||
is decremented.
|
||||
|
||||
.. function:: int json_object_deln(json_t *object, const char *key, size_t key_len)
|
||||
|
||||
Like :func:`json_object_del`, but give the fixed-length *key* with length *key_len*.
|
||||
See :ref:`fixed_length_keys` for details.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: int json_object_clear(json_t *object)
|
||||
|
||||
Remove all elements from *object*. Returns 0 on success and -1 if
|
||||
@ -750,7 +794,7 @@ allowed in object keys.
|
||||
The items are returned in the order they were inserted to the
|
||||
object.
|
||||
|
||||
**Note:** It's not safe to call ``json_object_del(object, key)``
|
||||
**Note:** It's not safe to call ``json_object_del(object, key)`` or ``json_object_deln(object, key, key_len)``
|
||||
during iteration. If you need to, use
|
||||
:func:`json_object_foreach_safe` instead.
|
||||
|
||||
@ -767,11 +811,39 @@ allowed in object keys.
|
||||
.. function:: void json_object_foreach_safe(object, tmp, key, value)
|
||||
|
||||
Like :func:`json_object_foreach()`, but it's safe to call
|
||||
``json_object_del(object, key)`` during iteration. You need to pass
|
||||
an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
|
||||
``json_object_del(object, key)`` or ``json_object_deln(object, key, key_len)`` during iteration.
|
||||
You need to pass an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
|
||||
|
||||
.. versionadded:: 2.8
|
||||
|
||||
.. function:: void json_object_keylen_foreach(object, key, key_len, value)
|
||||
|
||||
Like :c:func:`json_object_foreach`, but in *key_len* stored length of the *key*.
|
||||
Example::
|
||||
|
||||
/* obj is a JSON object */
|
||||
const char *key;
|
||||
json_t *value;
|
||||
size_t len;
|
||||
|
||||
json_object_keylen_foreach(obj, key, len, value) {
|
||||
printf("got key %s with length %zu\n", key, len);
|
||||
}
|
||||
|
||||
**Note:** It's not safe to call ``json_object_deln(object, key, key_len)``
|
||||
during iteration. If you need to, use
|
||||
:func:`json_object_keylen_foreach_safe` instead.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
|
||||
.. function:: void json_object_keylen_foreach_safe(object, tmp, key, key_len, value)
|
||||
|
||||
Like :func:`json_object_keylen_foreach()`, but it's safe to call
|
||||
``json_object_deln(object, key, key_len)`` during iteration.
|
||||
You need to pass an extra ``void *`` parameter ``tmp`` that is used for temporary storage.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
The following functions can be used to iterate through all key-value
|
||||
pairs in an object. The items are returned in the order they were
|
||||
@ -800,6 +872,12 @@ inserted to the object.
|
||||
|
||||
Extract the associated key from *iter*.
|
||||
|
||||
.. function:: size_t json_object_iter_key_len(void *iter)
|
||||
|
||||
Extract the associated key length from *iter*.
|
||||
|
||||
.. versionadded:: 2.14
|
||||
|
||||
.. function:: json_t *json_object_iter_value(void *iter)
|
||||
|
||||
.. refcounting:: borrow
|
||||
@ -1909,3 +1987,79 @@ memory, see
|
||||
http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html.
|
||||
The page also explains the :func:`guaranteed_memset()` function used
|
||||
in the example and gives a sample implementation for it.
|
||||
|
||||
.. _fixed_length_keys:
|
||||
|
||||
Fixed-Length keys
|
||||
=================
|
||||
|
||||
The Jansson API allows work with fixed-length keys. This can be useful in the following cases:
|
||||
|
||||
* The key is contained inside a buffer and is not null-terminated. In this case creating a new temporary buffer is not needed.
|
||||
* The key contains U+0000 inside it.
|
||||
|
||||
List of API for fixed-length keys:
|
||||
|
||||
* :c:func:`json_object_getn`
|
||||
* :c:func:`json_object_setn`
|
||||
* :c:func:`json_object_setn_nocheck`
|
||||
* :c:func:`json_object_setn_new`
|
||||
* :c:func:`json_object_setn_new_nocheck`
|
||||
* :c:func:`json_object_deln`
|
||||
* :c:func:`json_object_iter_key_len`
|
||||
* :c:func:`json_object_keylen_foreach`
|
||||
* :c:func:`json_object_keylen_foreach_safe`
|
||||
|
||||
**Examples:**
|
||||
|
||||
Try to write a new function to get :c:struct:`json_t` by path separated by ``.``
|
||||
|
||||
This requires:
|
||||
|
||||
* string iterator (no need to modify the input for better performance)
|
||||
* API for working with fixed-size keys
|
||||
|
||||
The iterator::
|
||||
|
||||
struct string {
|
||||
const char *string;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
size_t string_try_next(struct string *str, const char *delimiter) {
|
||||
str->string += strspn(str->string, delimiter);
|
||||
str->length = strcspn(str->string, delimiter);
|
||||
return str->length;
|
||||
}
|
||||
|
||||
#define string_foreach(_string, _delimiter) \
|
||||
for (; string_try_next(&(_string), _delimiter); (_string).string += (_string).length)
|
||||
|
||||
|
||||
The function::
|
||||
|
||||
json_t *json_object_get_by_path(json_t *object, const char *path) {
|
||||
struct string str;
|
||||
json_t *out = object;
|
||||
|
||||
str.string = path;
|
||||
|
||||
string_foreach(str, ".") {
|
||||
out = json_object_getn(out, str.string, str.length);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
And usage::
|
||||
|
||||
int main(void) {
|
||||
json_t *obj = json_pack("{s:{s:{s:b}}}", "a", "b", "c", 1);
|
||||
|
||||
json_t *c = json_object_get_by_path(obj, "a.b.c");
|
||||
assert(json_is_true(c));
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
@ -22,8 +22,7 @@ JSON strings are mapped to C-style null-terminated character arrays,
|
||||
and UTF-8 encoding is used internally.
|
||||
|
||||
All Unicode codepoints U+0000 through U+10FFFF are allowed in string
|
||||
values. However, U+0000 is not allowed in object keys because of API
|
||||
restrictions.
|
||||
values. However, U+0000 is allowed in object keys only for length-aware functions.
|
||||
|
||||
Unicode normalization or any other transformation is never performed
|
||||
on any strings (string values or object keys). When checking for
|
||||
|
53
src/dump.c
53
src/dump.c
@ -195,8 +195,21 @@ static int dump_string(const char *str, size_t len, json_dump_callback_t dump, v
|
||||
return dump("\"", 1, data);
|
||||
}
|
||||
|
||||
struct key_len {
|
||||
const char *key;
|
||||
int len;
|
||||
};
|
||||
|
||||
static int compare_keys(const void *key1, const void *key2) {
|
||||
return strcmp(*(const char **)key1, *(const char **)key2);
|
||||
const struct key_len *k1 = key1;
|
||||
const struct key_len *k2 = key2;
|
||||
const size_t min_size = k1->len < k2->len ? k1->len : k2->len;
|
||||
int res = memcmp(k1->key, k2->key, min_size);
|
||||
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
return k1->len - k2->len;
|
||||
}
|
||||
|
||||
static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents,
|
||||
@ -253,9 +266,10 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
/* Space for "0x", double the sizeof a pointer for the hex and a
|
||||
* terminator. */
|
||||
char key[2 + (sizeof(json) * 2) + 1];
|
||||
size_t key_len;
|
||||
|
||||
/* detect circular references */
|
||||
if (jsonp_loop_check(parents, json, key, sizeof(key)))
|
||||
if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len))
|
||||
return -1;
|
||||
|
||||
n = json_array_size(json);
|
||||
@ -263,7 +277,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
if (!embed && dump("[", 1, data))
|
||||
return -1;
|
||||
if (n == 0) {
|
||||
hashtable_del(parents, key);
|
||||
hashtable_del(parents, key, key_len);
|
||||
return embed ? 0 : dump("]", 1, data);
|
||||
}
|
||||
if (dump_indent(flags, depth + 1, 0, dump, data))
|
||||
@ -284,7 +298,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
}
|
||||
}
|
||||
|
||||
hashtable_del(parents, key);
|
||||
hashtable_del(parents, key, key_len);
|
||||
return embed ? 0 : dump("]", 1, data);
|
||||
}
|
||||
|
||||
@ -293,6 +307,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
const char *separator;
|
||||
int separator_length;
|
||||
char loop_key[LOOP_KEY_LEN];
|
||||
size_t loop_key_len;
|
||||
|
||||
if (flags & JSON_COMPACT) {
|
||||
separator = ":";
|
||||
@ -303,7 +318,8 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
}
|
||||
|
||||
/* detect circular references */
|
||||
if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key)))
|
||||
if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key),
|
||||
&loop_key_len))
|
||||
return -1;
|
||||
|
||||
iter = json_object_iter((json_t *)json);
|
||||
@ -311,40 +327,44 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
if (!embed && dump("{", 1, data))
|
||||
return -1;
|
||||
if (!iter) {
|
||||
hashtable_del(parents, loop_key);
|
||||
hashtable_del(parents, loop_key, loop_key_len);
|
||||
return embed ? 0 : dump("}", 1, data);
|
||||
}
|
||||
if (dump_indent(flags, depth + 1, 0, dump, data))
|
||||
return -1;
|
||||
|
||||
if (flags & JSON_SORT_KEYS) {
|
||||
const char **keys;
|
||||
struct key_len *keys;
|
||||
size_t size, i;
|
||||
|
||||
size = json_object_size(json);
|
||||
keys = jsonp_malloc(size * sizeof(const char *));
|
||||
keys = jsonp_malloc(size * sizeof(struct key_len));
|
||||
if (!keys)
|
||||
return -1;
|
||||
|
||||
i = 0;
|
||||
while (iter) {
|
||||
keys[i] = json_object_iter_key(iter);
|
||||
struct key_len *keylen = &keys[i];
|
||||
|
||||
keylen->key = json_object_iter_key(iter);
|
||||
keylen->len = json_object_iter_key_len(iter);
|
||||
|
||||
iter = json_object_iter_next((json_t *)json, iter);
|
||||
i++;
|
||||
}
|
||||
assert(i == size);
|
||||
|
||||
qsort(keys, size, sizeof(const char *), compare_keys);
|
||||
qsort(keys, size, sizeof(struct key_len), compare_keys);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
const char *key;
|
||||
const struct key_len *key;
|
||||
json_t *value;
|
||||
|
||||
key = keys[i];
|
||||
value = json_object_get(json, key);
|
||||
key = &keys[i];
|
||||
value = json_object_getn(json, key->key, key->len);
|
||||
assert(value);
|
||||
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
dump_string(key->key, key->len, dump, data, flags);
|
||||
if (dump(separator, separator_length, data) ||
|
||||
do_dump(value, flags, depth + 1, parents, dump, data)) {
|
||||
jsonp_free(keys);
|
||||
@ -372,8 +392,9 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
while (iter) {
|
||||
void *next = json_object_iter_next((json_t *)json, iter);
|
||||
const char *key = json_object_iter_key(iter);
|
||||
const size_t key_len = json_object_iter_key_len(iter);
|
||||
|
||||
dump_string(key, strlen(key), dump, data, flags);
|
||||
dump_string(key, key_len, dump, data, flags);
|
||||
if (dump(separator, separator_length, data) ||
|
||||
do_dump(json_object_iter_value(iter), flags, depth + 1, parents,
|
||||
dump, data))
|
||||
@ -392,7 +413,7 @@ static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *par
|
||||
}
|
||||
}
|
||||
|
||||
hashtable_del(parents, loop_key);
|
||||
hashtable_del(parents, loop_key, loop_key_len);
|
||||
return embed ? 0 : dump("}", 1, data);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ extern volatile uint32_t hashtable_seed;
|
||||
|
||||
#define list_to_pair(list_) container_of(list_, pair_t, list)
|
||||
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
|
||||
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
|
||||
#define hash_str(key, len) ((size_t)hashlittle((key), len, hashtable_seed))
|
||||
|
||||
static JSON_INLINE void list_init(list_t *list) {
|
||||
list->next = list;
|
||||
@ -69,7 +69,7 @@ static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket, list_t *l
|
||||
}
|
||||
|
||||
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
const char *key, size_t hash) {
|
||||
const char *key, size_t key_len, size_t hash) {
|
||||
list_t *list;
|
||||
pair_t *pair;
|
||||
|
||||
@ -79,7 +79,8 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
list = bucket->first;
|
||||
while (1) {
|
||||
pair = list_to_pair(list);
|
||||
if (pair->hash == hash && strcmp(pair->key, key) == 0)
|
||||
if (pair->hash == hash && pair->key_len == key_len &&
|
||||
memcmp(pair->key, key, key_len) == 0)
|
||||
return pair;
|
||||
|
||||
if (list == bucket->last)
|
||||
@ -92,7 +93,8 @@ static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||
}
|
||||
|
||||
/* returns 0 on success, -1 if key was not found */
|
||||
static int hashtable_do_del(hashtable_t *hashtable, const char *key, size_t hash) {
|
||||
static int hashtable_do_del(hashtable_t *hashtable, const char *key, size_t key_len,
|
||||
size_t hash) {
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
size_t index;
|
||||
@ -100,7 +102,7 @@ static int hashtable_do_del(hashtable_t *hashtable, const char *key, size_t hash
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
|
||||
if (!pair)
|
||||
return -1;
|
||||
|
||||
@ -193,7 +195,37 @@ void hashtable_close(hashtable_t *hashtable) {
|
||||
jsonp_free(hashtable->buckets);
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value) {
|
||||
static pair_t *init_pair(json_t *value, const char *key, size_t key_len, size_t hash) {
|
||||
pair_t *pair;
|
||||
|
||||
/* offsetof(...) returns the size of pair_t without the last,
|
||||
flexible member. This way, the correct amount is
|
||||
allocated. */
|
||||
|
||||
if (key_len >= (size_t)-1 - offsetof(pair_t, key)) {
|
||||
/* Avoid an overflow if the key is very long */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + key_len + 1);
|
||||
|
||||
if (!pair)
|
||||
return NULL;
|
||||
|
||||
pair->hash = hash;
|
||||
memcpy(pair->key, key, key_len);
|
||||
pair->key[key_len] = '\0';
|
||||
pair->key_len = key_len;
|
||||
pair->value = value;
|
||||
|
||||
list_init(&pair->list);
|
||||
list_init(&pair->ordered_list);
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, size_t key_len,
|
||||
json_t *value) {
|
||||
pair_t *pair;
|
||||
bucket_t *bucket;
|
||||
size_t hash, index;
|
||||
@ -203,35 +235,20 @@ int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value) {
|
||||
if (hashtable_do_rehash(hashtable))
|
||||
return -1;
|
||||
|
||||
hash = hash_str(key);
|
||||
hash = hash_str(key, key_len);
|
||||
index = hash & hashmask(hashtable->order);
|
||||
bucket = &hashtable->buckets[index];
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
|
||||
|
||||
if (pair) {
|
||||
json_decref(pair->value);
|
||||
pair->value = value;
|
||||
} else {
|
||||
/* offsetof(...) returns the size of pair_t without the last,
|
||||
flexible member. This way, the correct amount is
|
||||
allocated. */
|
||||
pair = init_pair(value, key, key_len, hash);
|
||||
|
||||
size_t len = strlen(key);
|
||||
if (len >= (size_t)-1 - offsetof(pair_t, key)) {
|
||||
/* Avoid an overflow if the key is very long */
|
||||
return -1;
|
||||
}
|
||||
|
||||
pair = jsonp_malloc(offsetof(pair_t, key) + len + 1);
|
||||
if (!pair)
|
||||
return -1;
|
||||
|
||||
pair->hash = hash;
|
||||
strncpy(pair->key, key, len + 1);
|
||||
pair->value = value;
|
||||
list_init(&pair->list);
|
||||
list_init(&pair->ordered_list);
|
||||
|
||||
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||
list_insert(&hashtable->ordered_list, &pair->ordered_list);
|
||||
|
||||
@ -240,24 +257,24 @@ int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key) {
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key, size_t key_len) {
|
||||
pair_t *pair;
|
||||
size_t hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
hash = hash_str(key, key_len);
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
|
||||
if (!pair)
|
||||
return NULL;
|
||||
|
||||
return pair->value;
|
||||
}
|
||||
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key) {
|
||||
size_t hash = hash_str(key);
|
||||
return hashtable_do_del(hashtable, key, hash);
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key, size_t key_len) {
|
||||
size_t hash = hash_str(key, key_len);
|
||||
return hashtable_do_del(hashtable, key, key_len, hash);
|
||||
}
|
||||
|
||||
void hashtable_clear(hashtable_t *hashtable) {
|
||||
@ -278,15 +295,15 @@ void *hashtable_iter(hashtable_t *hashtable) {
|
||||
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
|
||||
}
|
||||
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key) {
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key, size_t key_len) {
|
||||
pair_t *pair;
|
||||
size_t hash;
|
||||
bucket_t *bucket;
|
||||
|
||||
hash = hash_str(key);
|
||||
hash = hash_str(key, key_len);
|
||||
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||
pair = hashtable_find_pair(hashtable, bucket, key, key_len, hash);
|
||||
if (!pair)
|
||||
return NULL;
|
||||
|
||||
@ -305,6 +322,11 @@ void *hashtable_iter_key(void *iter) {
|
||||
return pair->key;
|
||||
}
|
||||
|
||||
size_t hashtable_iter_key_len(void *iter) {
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->key_len;
|
||||
}
|
||||
|
||||
void *hashtable_iter_value(void *iter) {
|
||||
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||
return pair->value;
|
||||
|
@ -24,6 +24,7 @@ struct hashtable_pair {
|
||||
struct hashtable_list ordered_list;
|
||||
size_t hash;
|
||||
json_t *value;
|
||||
size_t key_len;
|
||||
char key[1];
|
||||
};
|
||||
|
||||
@ -69,6 +70,7 @@ void hashtable_close(hashtable_t *hashtable);
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
* @key: The length of key
|
||||
* @serial: For addition order of keys
|
||||
* @value: The value
|
||||
*
|
||||
@ -79,27 +81,29 @@ void hashtable_close(hashtable_t *hashtable);
|
||||
*
|
||||
* Returns 0 on success, -1 on failure (out of memory).
|
||||
*/
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
|
||||
int hashtable_set(hashtable_t *hashtable, const char *key, size_t key_len, json_t *value);
|
||||
|
||||
/**
|
||||
* hashtable_get - Get a value associated with a key
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
* @key: The length of key
|
||||
*
|
||||
* Returns value if it is found, or NULL otherwise.
|
||||
*/
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key);
|
||||
void *hashtable_get(hashtable_t *hashtable, const char *key, size_t key_len);
|
||||
|
||||
/**
|
||||
* hashtable_del - Remove a value from the hashtable
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key
|
||||
* @key: The length of key
|
||||
*
|
||||
* Returns 0 on success, or -1 if the key was not found.
|
||||
*/
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key);
|
||||
int hashtable_del(hashtable_t *hashtable, const char *key, size_t key_len);
|
||||
|
||||
/**
|
||||
* hashtable_clear - Clear hashtable
|
||||
@ -132,11 +136,12 @@ void *hashtable_iter(hashtable_t *hashtable);
|
||||
*
|
||||
* @hashtable: The hashtable object
|
||||
* @key: The key that the iterator should point to
|
||||
* @key: The length of key
|
||||
*
|
||||
* Like hashtable_iter() but returns an iterator pointing to a
|
||||
* specific key.
|
||||
*/
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
|
||||
void *hashtable_iter_at(hashtable_t *hashtable, const char *key, size_t key_len);
|
||||
|
||||
/**
|
||||
* hashtable_iter_next - Advance an iterator
|
||||
@ -156,6 +161,13 @@ void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
|
||||
*/
|
||||
void *hashtable_iter_key(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_key_len - Retrieve the key length pointed by an iterator
|
||||
*
|
||||
* @iter: The iterator
|
||||
*/
|
||||
size_t hashtable_iter_key_len(void *iter);
|
||||
|
||||
/**
|
||||
* hashtable_iter_value - Retrieve the value pointed by an iterator
|
||||
*
|
||||
|
@ -34,9 +34,13 @@ EXPORTS
|
||||
json_object
|
||||
json_object_size
|
||||
json_object_get
|
||||
json_object_getn
|
||||
json_object_set_new
|
||||
json_object_setn_new
|
||||
json_object_set_new_nocheck
|
||||
json_object_setn_new_nocheck
|
||||
json_object_del
|
||||
json_object_deln
|
||||
json_object_clear
|
||||
json_object_update
|
||||
json_object_update_existing
|
||||
@ -46,6 +50,7 @@ EXPORTS
|
||||
json_object_iter_at
|
||||
json_object_iter_next
|
||||
json_object_iter_key
|
||||
json_object_iter_key_len
|
||||
json_object_iter_value
|
||||
json_object_iter_set_new
|
||||
json_object_key_to_iter
|
||||
|
@ -188,9 +188,15 @@ void json_object_seed(size_t seed);
|
||||
size_t json_object_size(const json_t *object);
|
||||
json_t *json_object_get(const json_t *object, const char *key)
|
||||
JANSSON_ATTRS((warn_unused_result));
|
||||
json_t *json_object_getn(const json_t *object, const char *key, size_t key_len)
|
||||
JANSSON_ATTRS((warn_unused_result));
|
||||
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||
int json_object_setn_new(json_t *object, const char *key, size_t key_len, json_t *value);
|
||||
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
|
||||
int json_object_setn_new_nocheck(json_t *object, const char *key, size_t key_len,
|
||||
json_t *value);
|
||||
int json_object_del(json_t *object, const char *key);
|
||||
int json_object_deln(json_t *object, const char *key, size_t key_len);
|
||||
int json_object_clear(json_t *object);
|
||||
int json_object_update(json_t *object, json_t *other);
|
||||
int json_object_update_existing(json_t *object, json_t *other);
|
||||
@ -201,6 +207,7 @@ 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);
|
||||
const char *json_object_iter_key(void *iter);
|
||||
size_t json_object_iter_key_len(void *iter);
|
||||
json_t *json_object_iter_value(void *iter);
|
||||
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
|
||||
@ -210,6 +217,14 @@ int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
key = json_object_iter_key( \
|
||||
json_object_iter_next(object, json_object_key_to_iter(key))))
|
||||
|
||||
#define json_object_keylen_foreach(object, key, key_len, value) \
|
||||
for (key = json_object_iter_key(json_object_iter(object)), \
|
||||
key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \
|
||||
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))), \
|
||||
key_len = json_object_iter_key_len(json_object_key_to_iter(key)))
|
||||
|
||||
#define json_object_foreach_safe(object, n, key, value) \
|
||||
for (key = json_object_iter_key(json_object_iter(object)), \
|
||||
n = json_object_iter_next(object, json_object_key_to_iter(key)); \
|
||||
@ -217,6 +232,14 @@ int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||
key = json_object_iter_key(n), \
|
||||
n = json_object_iter_next(object, json_object_key_to_iter(key)))
|
||||
|
||||
#define json_object_keylen_foreach_safe(object, n, key, key_len, value) \
|
||||
for (key = json_object_iter_key(json_object_iter(object)), \
|
||||
n = json_object_iter_next(object, json_object_key_to_iter(key)), \
|
||||
key_len = json_object_iter_key_len(json_object_key_to_iter(key)); \
|
||||
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
|
||||
key = json_object_iter_key(n), key_len = json_object_iter_key_len(n), \
|
||||
n = json_object_iter_next(object, json_object_key_to_iter(key)))
|
||||
|
||||
#define json_array_foreach(array, index, value) \
|
||||
for (index = 0; \
|
||||
index < json_array_size(array) && (value = json_array_get(array, index)); \
|
||||
@ -226,11 +249,21 @@ static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *
|
||||
return json_object_set_new(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE int json_object_setn(json_t *object, const char *key, size_t key_len,
|
||||
json_t *value) {
|
||||
return json_object_setn_new(object, key, key_len, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE int json_object_set_nocheck(json_t *object, const char *key,
|
||||
json_t *value) {
|
||||
return json_object_set_new_nocheck(object, key, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE int json_object_setn_nocheck(json_t *object, const char *key,
|
||||
size_t key_len, json_t *value) {
|
||||
return json_object_setn_new_nocheck(object, key, key_len, json_incref(value));
|
||||
}
|
||||
|
||||
static JSON_INLINE int json_object_iter_set(json_t *object, void *iter, json_t *value) {
|
||||
return json_object_iter_set_new(object, iter, json_incref(value));
|
||||
}
|
||||
|
@ -91,8 +91,8 @@ char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS((warn_unused_resu
|
||||
/* Circular reference check*/
|
||||
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||
#define LOOP_KEY_LEN (2 + (sizeof(json_t *) * 2) + 1)
|
||||
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key,
|
||||
size_t key_size);
|
||||
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size,
|
||||
size_t *key_len_out);
|
||||
|
||||
/* Windows compatibility */
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
@ -689,7 +689,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) {
|
||||
}
|
||||
|
||||
if (flags & JSON_REJECT_DUPLICATES) {
|
||||
if (json_object_get(object, key)) {
|
||||
if (json_object_getn(object, key, len)) {
|
||||
jsonp_free(key);
|
||||
error_set(error, lex, json_error_duplicate_key, "duplicate object key");
|
||||
goto error;
|
||||
@ -710,7 +710,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (json_object_set_new_nocheck(object, key, value)) {
|
||||
if (json_object_setn_new_nocheck(object, key, len, value)) {
|
||||
jsonp_free(key);
|
||||
goto error;
|
||||
}
|
||||
|
@ -544,7 +544,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) {
|
||||
if (unpack(s, value, ap))
|
||||
goto out;
|
||||
|
||||
hashtable_set(&key_set, key, json_null());
|
||||
hashtable_set(&key_set, key, strlen(key), json_null());
|
||||
next_token(s);
|
||||
}
|
||||
|
||||
@ -554,6 +554,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) {
|
||||
if (root && strict == 1) {
|
||||
/* We need to check that all non optional items have been parsed */
|
||||
const char *key;
|
||||
size_t key_len;
|
||||
/* keys_res is 1 for uninitialized, 0 for success, -1 for error. */
|
||||
int keys_res = 1;
|
||||
strbuffer_t unrecognized_keys;
|
||||
@ -562,7 +563,8 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) {
|
||||
|
||||
if (gotopt || json_object_size(root) != key_set.size) {
|
||||
json_object_foreach(root, key, value) {
|
||||
if (!hashtable_get(&key_set, key)) {
|
||||
key_len = strlen(key);
|
||||
if (!hashtable_get(&key_set, key, key_len)) {
|
||||
unpacked++;
|
||||
|
||||
/* Save unrecognized keys for the error message */
|
||||
@ -574,7 +576,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) {
|
||||
|
||||
if (!keys_res)
|
||||
keys_res =
|
||||
strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
|
||||
strbuffer_append_bytes(&unrecognized_keys, key, key_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
92
src/value.c
92
src/value.c
@ -44,13 +44,17 @@ static JSON_INLINE void json_init(json_t *json, json_type type) {
|
||||
json->refcount = 1;
|
||||
}
|
||||
|
||||
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key,
|
||||
size_t key_size) {
|
||||
snprintf(key, key_size, "%p", json);
|
||||
if (hashtable_get(parents, key))
|
||||
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size,
|
||||
size_t *key_len_out) {
|
||||
size_t key_len = snprintf(key, key_size, "%p", json);
|
||||
|
||||
if (key_len_out)
|
||||
*key_len_out = key_len;
|
||||
|
||||
if (hashtable_get(parents, key, key_len))
|
||||
return -1;
|
||||
|
||||
return hashtable_set(parents, key, json_null());
|
||||
return hashtable_set(parents, key, key_len, json_null());
|
||||
}
|
||||
|
||||
/*** object ***/
|
||||
@ -93,16 +97,32 @@ size_t json_object_size(const json_t *json) {
|
||||
}
|
||||
|
||||
json_t *json_object_get(const json_t *json, const char *key) {
|
||||
if (!key)
|
||||
return NULL;
|
||||
|
||||
return json_object_getn(json, key, strlen(key));
|
||||
}
|
||||
|
||||
json_t *json_object_getn(const json_t *json, const char *key, size_t key_len) {
|
||||
json_object_t *object;
|
||||
|
||||
if (!key || !json_is_object(json))
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_get(&object->hashtable, key);
|
||||
return hashtable_get(&object->hashtable, key, key_len);
|
||||
}
|
||||
|
||||
int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) {
|
||||
if (!key) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
return json_object_setn_new_nocheck(json, key, strlen(key), value);
|
||||
}
|
||||
|
||||
int json_object_setn_new_nocheck(json_t *json, const char *key, size_t key_len,
|
||||
json_t *value) {
|
||||
json_object_t *object;
|
||||
|
||||
if (!value)
|
||||
@ -114,7 +134,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) {
|
||||
}
|
||||
object = json_to_object(json);
|
||||
|
||||
if (hashtable_set(&object->hashtable, key, value)) {
|
||||
if (hashtable_set(&object->hashtable, key, key_len, value)) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
@ -123,22 +143,38 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value) {
|
||||
}
|
||||
|
||||
int json_object_set_new(json_t *json, const char *key, json_t *value) {
|
||||
if (!key || !utf8_check_string(key, strlen(key))) {
|
||||
if (!key) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return json_object_set_new_nocheck(json, key, value);
|
||||
return json_object_setn_new(json, key, strlen(key), value);
|
||||
}
|
||||
|
||||
int json_object_setn_new(json_t *json, const char *key, size_t key_len, json_t *value) {
|
||||
if (!key || !utf8_check_string(key, key_len)) {
|
||||
json_decref(value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return json_object_setn_new_nocheck(json, key, key_len, value);
|
||||
}
|
||||
|
||||
int json_object_del(json_t *json, const char *key) {
|
||||
if (!key)
|
||||
return -1;
|
||||
|
||||
return json_object_deln(json, key, strlen(key));
|
||||
}
|
||||
|
||||
int json_object_deln(json_t *json, const char *key, size_t key_len) {
|
||||
json_object_t *object;
|
||||
|
||||
if (!key || !json_is_object(json))
|
||||
return -1;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_del(&object->hashtable, key);
|
||||
return hashtable_del(&object->hashtable, key, key_len);
|
||||
}
|
||||
|
||||
int json_object_clear(json_t *json) {
|
||||
@ -170,14 +206,15 @@ int json_object_update(json_t *object, json_t *other) {
|
||||
|
||||
int json_object_update_existing(json_t *object, json_t *other) {
|
||||
const char *key;
|
||||
size_t key_len;
|
||||
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);
|
||||
json_object_keylen_foreach(other, key, key_len, value) {
|
||||
if (json_object_getn(object, key, key_len))
|
||||
json_object_setn_nocheck(object, key, key_len, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -200,17 +237,19 @@ int json_object_update_missing(json_t *object, json_t *other) {
|
||||
|
||||
int do_object_update_recursive(json_t *object, json_t *other, hashtable_t *parents) {
|
||||
const char *key;
|
||||
size_t key_len;
|
||||
json_t *value;
|
||||
char loop_key[LOOP_KEY_LEN];
|
||||
int res = 0;
|
||||
size_t loop_key_len;
|
||||
|
||||
if (!json_is_object(object) || !json_is_object(other))
|
||||
return -1;
|
||||
|
||||
if (jsonp_loop_check(parents, other, loop_key, sizeof(loop_key)))
|
||||
if (jsonp_loop_check(parents, other, loop_key, sizeof(loop_key), &loop_key_len))
|
||||
return -1;
|
||||
|
||||
json_object_foreach(other, key, value) {
|
||||
json_object_keylen_foreach(other, key, key_len, value) {
|
||||
json_t *v = json_object_get(object, key);
|
||||
|
||||
if (json_is_object(v) && json_is_object(value)) {
|
||||
@ -219,14 +258,14 @@ int do_object_update_recursive(json_t *object, json_t *other, hashtable_t *paren
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (json_object_set_nocheck(object, key, value)) {
|
||||
if (json_object_setn_nocheck(object, key, key_len, value)) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hashtable_del(parents, loop_key);
|
||||
hashtable_del(parents, loop_key, loop_key_len);
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -260,7 +299,7 @@ void *json_object_iter_at(json_t *json, const char *key) {
|
||||
return NULL;
|
||||
|
||||
object = json_to_object(json);
|
||||
return hashtable_iter_at(&object->hashtable, key);
|
||||
return hashtable_iter_at(&object->hashtable, key, strlen(key));
|
||||
}
|
||||
|
||||
void *json_object_iter_next(json_t *json, void *iter) {
|
||||
@ -280,6 +319,13 @@ const char *json_object_iter_key(void *iter) {
|
||||
return hashtable_iter_key(iter);
|
||||
}
|
||||
|
||||
size_t json_object_iter_key_len(void *iter) {
|
||||
if (!iter)
|
||||
return 0;
|
||||
|
||||
return hashtable_iter_key_len(iter);
|
||||
}
|
||||
|
||||
json_t *json_object_iter_value(void *iter) {
|
||||
if (!iter)
|
||||
return NULL;
|
||||
@ -340,8 +386,9 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
|
||||
json_t *result;
|
||||
void *iter;
|
||||
char loop_key[LOOP_KEY_LEN];
|
||||
size_t loop_key_len;
|
||||
|
||||
if (jsonp_loop_check(parents, object, loop_key, sizeof(loop_key)))
|
||||
if (jsonp_loop_check(parents, object, loop_key, sizeof(loop_key), &loop_key_len))
|
||||
return NULL;
|
||||
|
||||
result = json_object();
|
||||
@ -366,7 +413,7 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
|
||||
}
|
||||
|
||||
out:
|
||||
hashtable_del(parents, loop_key);
|
||||
hashtable_del(parents, loop_key, loop_key_len);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -633,8 +680,9 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents) {
|
||||
json_t *result;
|
||||
size_t i;
|
||||
char loop_key[LOOP_KEY_LEN];
|
||||
size_t loop_key_len;
|
||||
|
||||
if (jsonp_loop_check(parents, array, loop_key, sizeof(loop_key)))
|
||||
if (jsonp_loop_check(parents, array, loop_key, sizeof(loop_key), &loop_key_len))
|
||||
return NULL;
|
||||
|
||||
result = json_array();
|
||||
@ -651,7 +699,7 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents) {
|
||||
}
|
||||
|
||||
out:
|
||||
hashtable_del(parents, loop_key);
|
||||
hashtable_del(parents, loop_key, loop_key_len);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -7,9 +7,10 @@ check_PROGRAMS = \
|
||||
test_dump \
|
||||
test_dump_callback \
|
||||
test_equal \
|
||||
test_fixed_size \
|
||||
test_load \
|
||||
test_loadb \
|
||||
test_load_callback \
|
||||
test_loadb \
|
||||
test_memory_funcs \
|
||||
test_number \
|
||||
test_object \
|
||||
@ -24,6 +25,7 @@ test_chaos_SOURCES = test_chaos.c util.h
|
||||
test_copy_SOURCES = test_copy.c util.h
|
||||
test_dump_SOURCES = test_dump.c util.h
|
||||
test_dump_callback_SOURCES = test_dump_callback.c util.h
|
||||
test_fixed_size_SOURCES = test_fixed_size.c util.h
|
||||
test_load_SOURCES = test_load.c util.h
|
||||
test_loadb_SOURCES = test_loadb.c util.h
|
||||
test_memory_funcs_SOURCES = test_memory_funcs.c util.h
|
||||
|
205
test/suites/api/test_fixed_size.c
Normal file
205
test/suites/api/test_fixed_size.c
Normal file
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Petri Lehtinen <petri@digip.org>
|
||||
*
|
||||
* Jansson is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "util.h"
|
||||
#include <jansson.h>
|
||||
#include <string.h>
|
||||
|
||||
static void test_keylen_iterator(json_t *object) {
|
||||
const char key1[] = {'t', 'e', 's', 't', '1'};
|
||||
const char key2[] = {'t', 'e', 's', 't'};
|
||||
const char key3[] = {'t', 'e', 's', '\0', 't'};
|
||||
const char key4[] = {'t', 'e', 's', 't', '\0'};
|
||||
const char *reference_keys[] = {key1, key2, key3, key4};
|
||||
const size_t reference_keys_len[] = {sizeof(key1), sizeof(key2), sizeof(key3),
|
||||
sizeof(key4)};
|
||||
size_t index = 0;
|
||||
json_t *value;
|
||||
const char *key;
|
||||
size_t keylen;
|
||||
|
||||
json_object_keylen_foreach(object, key, keylen, value) {
|
||||
if (keylen != reference_keys_len[index])
|
||||
fail("invalid key len in iterator");
|
||||
if (memcmp(key, reference_keys[index], reference_keys_len[index]) != 0)
|
||||
fail("invalid key in iterator");
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
static void test_keylen(void) {
|
||||
json_t *obj = json_object();
|
||||
const char key[] = {'t', 'e', 's', 't', '1'};
|
||||
const char key2[] = {'t', 'e', 's', 't'};
|
||||
const char key3[] = {'t', 'e', 's', '\0', 't'};
|
||||
const char key4[] = {'t', 'e', 's', 't', '\0'};
|
||||
|
||||
if (json_object_size(obj) != 0)
|
||||
fail("incorrect json");
|
||||
|
||||
json_object_set_new_nocheck(obj, "test1", json_true());
|
||||
|
||||
if (json_object_size(obj) != 1)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_getn(obj, key, sizeof(key)) != json_true())
|
||||
fail("json_object_getn failed");
|
||||
|
||||
if (json_object_getn(obj, key2, sizeof(key2)) != NULL)
|
||||
fail("false positive json_object_getn by key2");
|
||||
|
||||
if (json_object_setn_nocheck(obj, key2, sizeof(key2), json_false()))
|
||||
fail("json_object_setn_nocheck for key2 failed");
|
||||
|
||||
if (json_object_size(obj) != 2)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_get(obj, "test") != json_false())
|
||||
fail("json_object_setn_nocheck for key2 failed");
|
||||
|
||||
if (json_object_getn(obj, key2, sizeof(key2)) != json_false())
|
||||
fail("json_object_getn by key 2 failed");
|
||||
|
||||
if (json_object_getn(obj, key3, sizeof(key3)) != NULL)
|
||||
fail("false positive json_object_getn by key3");
|
||||
|
||||
if (json_object_setn_nocheck(obj, key3, sizeof(key3), json_false()))
|
||||
fail("json_object_setn_nocheck for key3 failed");
|
||||
|
||||
if (json_object_size(obj) != 3)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_getn(obj, key3, sizeof(key3)) != json_false())
|
||||
fail("json_object_getn by key 3 failed");
|
||||
|
||||
if (json_object_getn(obj, key4, sizeof(key4)) != NULL)
|
||||
fail("false positive json_object_getn by key3");
|
||||
|
||||
if (json_object_setn_nocheck(obj, key4, sizeof(key4), json_false()))
|
||||
fail("json_object_setn_nocheck for key3 failed");
|
||||
|
||||
if (json_object_size(obj) != 4)
|
||||
fail("incorrect json");
|
||||
|
||||
test_keylen_iterator(obj);
|
||||
|
||||
if (json_object_getn(obj, key4, sizeof(key4)) != json_false())
|
||||
fail("json_object_getn by key 3 failed");
|
||||
|
||||
if (json_object_size(obj) != 4)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_deln(obj, key4, sizeof(key4)))
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_getn(obj, key4, sizeof(key4)) != NULL)
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_size(obj) != 3)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_deln(obj, key3, sizeof(key3)))
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_getn(obj, key3, sizeof(key3)) != NULL)
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_size(obj) != 2)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_deln(obj, key2, sizeof(key2)))
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_getn(obj, key2, sizeof(key2)) != NULL)
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_size(obj) != 1)
|
||||
fail("incorrect json");
|
||||
|
||||
if (json_object_deln(obj, key, sizeof(key)))
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_getn(obj, key, sizeof(key)) != NULL)
|
||||
fail("json_object_deln failed");
|
||||
if (json_object_size(obj) != 0)
|
||||
fail("incorrect json");
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void test_invalid_keylen(void) {
|
||||
json_t *obj = json_object();
|
||||
const char key[] = {'t', 'e', 's', 't', '1'};
|
||||
|
||||
json_object_set_new_nocheck(obj, "test1", json_true());
|
||||
|
||||
if (json_object_getn(NULL, key, sizeof(key)) != NULL)
|
||||
fail("json_object_getn on NULL failed");
|
||||
|
||||
if (json_object_getn(obj, NULL, sizeof(key)) != NULL)
|
||||
fail("json_object_getn on NULL failed");
|
||||
|
||||
if (json_object_getn(obj, key, 0) != NULL)
|
||||
fail("json_object_getn on NULL failed");
|
||||
|
||||
if (!json_object_setn_new(obj, NULL, sizeof(key), json_true()))
|
||||
fail("json_object_setn_new with NULL key failed");
|
||||
|
||||
if (!json_object_setn_new_nocheck(obj, NULL, sizeof(key), json_true()))
|
||||
fail("json_object_setn_new_nocheck with NULL key failed");
|
||||
|
||||
if (!json_object_del(obj, NULL))
|
||||
fail("json_object_del with NULL failed");
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void test_binary_keys(void) {
|
||||
json_t *obj = json_object();
|
||||
int key1 = 0;
|
||||
int key2 = 1;
|
||||
|
||||
json_object_setn_nocheck(obj, (const char *)&key1, sizeof(key1), json_true());
|
||||
json_object_setn_nocheck(obj, (const char *)&key2, sizeof(key2), json_true());
|
||||
|
||||
if (!json_is_true(json_object_getn(obj, (const char *)&key1, sizeof(key1))))
|
||||
fail("cannot get integer key1");
|
||||
|
||||
if (!json_is_true(json_object_getn(obj, (const char *)&key1, sizeof(key2))))
|
||||
fail("cannot get integer key2");
|
||||
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void test_dump_order(void) {
|
||||
json_t *obj = json_object();
|
||||
char key1[] = {'k', '\0', '-', '2'};
|
||||
char key2[] = {'k', '\0', '-', '1'};
|
||||
const char expected_sorted_str[] =
|
||||
"{\"k\\u0000-1\": \"first\", \"k\\u0000-2\": \"second\"}";
|
||||
const char expected_nonsorted_str[] =
|
||||
"{\"k\\u0000-2\": \"second\", \"k\\u0000-1\": \"first\"}";
|
||||
char *out;
|
||||
|
||||
json_object_setn_new_nocheck(obj, key1, sizeof(key1), json_string("second"));
|
||||
json_object_setn_new_nocheck(obj, key2, sizeof(key2), json_string("first"));
|
||||
|
||||
out = malloc(512);
|
||||
|
||||
json_dumpb(obj, out, 512, 0);
|
||||
|
||||
if (memcmp(expected_nonsorted_str, out, sizeof(expected_nonsorted_str) - 1) != 0)
|
||||
fail("preserve order failed");
|
||||
|
||||
json_dumpb(obj, out, 512, JSON_SORT_KEYS);
|
||||
if (memcmp(expected_sorted_str, out, sizeof(expected_sorted_str) - 1) != 0)
|
||||
fail("utf-8 sort failed");
|
||||
|
||||
free(out);
|
||||
json_decref(obj);
|
||||
}
|
||||
|
||||
static void run_tests() {
|
||||
test_keylen();
|
||||
test_invalid_keylen();
|
||||
test_binary_keys();
|
||||
test_dump_order();
|
||||
}
|
Loading…
Reference in New Issue
Block a user