@@ -3,7 +3,7 @@ | |||||
* Symbol versioning for libjson-c. | * Symbol versioning for libjson-c. | ||||
* All exported symbols must be listed here. | * All exported symbols must be listed here. | ||||
* | * | ||||
* See | |||||
* See | |||||
* https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf | * https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf | ||||
*/ | */ | ||||
@@ -165,6 +165,17 @@ JSONC_0.15 { | |||||
} JSONC_0.14; | } JSONC_0.14; | ||||
JSONC_0.16 { | JSONC_0.16 { | ||||
# global: | |||||
# ...new symbols here... | |||||
global: | |||||
json_key_data; | |||||
json_key_size; | |||||
json_object_object_add_len; | |||||
json_object_object_add_ex_len; | |||||
json_object_object_add_key; | |||||
json_object_object_del_len; | |||||
json_object_object_del_key; | |||||
json_object_object_get_len; | |||||
json_object_object_get_ex_len; | |||||
json_object_object_get_key; | |||||
json_c_shallow_copy_default_len; | |||||
json_object_deep_copy_len; | |||||
} JSONC_0.15; | } JSONC_0.15; |
@@ -41,6 +41,7 @@ | |||||
#error "The long long type isn't 64-bits" | #error "The long long type isn't 64-bits" | ||||
#endif | #endif | ||||
#include <limits.h> | |||||
#ifndef SSIZE_T_MAX | #ifndef SSIZE_T_MAX | ||||
#if SIZEOF_SSIZE_T == SIZEOF_INT | #if SIZEOF_SSIZE_T == SIZEOF_INT | ||||
#define SSIZE_T_MAX INT_MAX | #define SSIZE_T_MAX INT_MAX | ||||
@@ -520,7 +521,7 @@ static int json_object_object_to_json_string(struct json_object *jso, struct pri | |||||
printbuf_strappend(pb, " "); | printbuf_strappend(pb, " "); | ||||
indent(pb, level + 1, flags); | indent(pb, level + 1, flags); | ||||
printbuf_strappend(pb, "\""); | printbuf_strappend(pb, "\""); | ||||
json_escape_str(pb, iter.key, strlen(iter.key), flags); | |||||
json_escape_str(pb, json_key_data(iter.key), json_key_size(iter.key), flags); | |||||
if (flags & JSON_C_TO_STRING_SPACED) | if (flags & JSON_C_TO_STRING_SPACED) | ||||
printbuf_strappend(pb, "\": "); | printbuf_strappend(pb, "\": "); | ||||
else | else | ||||
@@ -585,43 +586,85 @@ struct lh_table *json_object_get_object(const struct json_object *jso) | |||||
int json_object_object_add_ex(struct json_object *jso, const char *const key, | int json_object_object_add_ex(struct json_object *jso, const char *const key, | ||||
struct json_object *const val, const unsigned opts) | struct json_object *const val, const unsigned opts) | ||||
{ | { | ||||
struct json_object *existing_value = NULL; | |||||
return json_object_object_add_ex_len(jso, key, strlen(key), val, opts); | |||||
} | |||||
int json_object_object_add_ex_len(struct json_object *jso, const char *const key, const size_t len, | |||||
struct json_object *const val, const unsigned opts) | |||||
{ | |||||
// Created on the stack rather than calling `json_key_new_ptr` | |||||
// or `json_key_new_imm` since this saves copying `key` if it turns | |||||
// out the value already exists in the hash table | |||||
struct json_key hashable = {len, {key}}; | |||||
return json_object_object_add_key(jso, &hashable, val, opts); | |||||
} | |||||
int json_object_object_add_key(struct json_object *jso, const struct json_key *key, | |||||
struct json_object *const val, const unsigned opts) | |||||
{ | |||||
struct json_object *existing_value; | |||||
struct lh_entry *existing_entry; | struct lh_entry *existing_entry; | ||||
unsigned long hash; | unsigned long hash; | ||||
/** Required due to the `lh_get_hash` function wanting a `const struct json_key *` */ | |||||
assert(json_object_get_type(jso) == json_type_object); | assert(json_object_get_type(jso) == json_type_object); | ||||
// The caller must avoid creating loops in the object tree, but do a | |||||
// quick check anyway to make sure we're not creating a trivial loop. | |||||
if (jso == val) | |||||
{ | |||||
return -1; | |||||
} | |||||
// We lookup the entry and replace the value, rather than just deleting | // We lookup the entry and replace the value, rather than just deleting | ||||
// and re-adding it, so the existing key remains valid. | // and re-adding it, so the existing key remains valid. | ||||
hash = lh_get_hash(JC_OBJECT(jso)->c_object, (const void *)key); | |||||
hash = lh_get_hash(JC_OBJECT(jso)->c_object, key); | |||||
existing_entry = | existing_entry = | ||||
(opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) | (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) | ||||
? NULL | ? NULL | ||||
: lh_table_lookup_entry_w_hash(JC_OBJECT(jso)->c_object, (const void *)key, hash); | : lh_table_lookup_entry_w_hash(JC_OBJECT(jso)->c_object, (const void *)key, hash); | ||||
// The caller must avoid creating loops in the object tree, but do a | |||||
// quick check anyway to make sure we're not creating a trivial loop. | |||||
if (jso == val) | |||||
return -1; | |||||
if (!existing_entry) | |||||
if (existing_entry == NULL) | |||||
{ | { | ||||
const void *const k = | |||||
(opts & JSON_C_OBJECT_KEY_IS_CONSTANT) ? (const void *)key : strdup(key); | |||||
// need to copy `key` because the caller might have created it | |||||
// on the stack, which would be more efficient than copying | |||||
// `json_key_data` into `key->string.idata` if it had happened | |||||
// that `existing_entry` wasn't NULL. | |||||
const struct json_key *k = | |||||
(opts & JSON_C_OBJECT_KEY_IS_CONSTANT) | |||||
? json_key_new_ptr(json_key_size(key), json_key_data(key)) | |||||
: json_key_new_imm(json_key_size(key), json_key_data(key)); | |||||
// TODO some optimization where (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) | |||||
if (k == NULL) | if (k == NULL) | ||||
{ | |||||
return -1; | return -1; | ||||
return lh_table_insert_w_hash(JC_OBJECT(jso)->c_object, k, val, hash, opts); | |||||
} | |||||
return lh_table_insert_w_hash(JC_OBJECT(jso)->c_object, k, val, hash, | |||||
opts & ~JSON_C_OBJECT_KEY_IS_CONSTANT); | |||||
// `struct json_key` always needs to be freed, | |||||
// so JSON_C_OBJECT_KEY_IS_CONSTANT cannot be set | |||||
} | |||||
else | |||||
{ | |||||
existing_value = (json_object *)lh_entry_v(existing_entry); | |||||
if (existing_value) | |||||
{ | |||||
json_object_put(existing_value); | |||||
} | |||||
existing_entry->v = val; | |||||
return 0; | |||||
} | } | ||||
existing_value = (json_object *)lh_entry_v(existing_entry); | |||||
if (existing_value) | |||||
json_object_put(existing_value); | |||||
existing_entry->v = val; | |||||
return 0; | |||||
} | } | ||||
int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) | int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) | ||||
{ | { | ||||
return json_object_object_add_ex(jso, key, val, 0); | |||||
return json_object_object_add_ex_len(jso, key, strlen(key), val, 0); | |||||
} | |||||
int json_object_object_add_len(struct json_object *jso, const char *key, const size_t len, | |||||
struct json_object *val) | |||||
{ | |||||
return json_object_object_add_ex_len(jso, key, len, val, 0); | |||||
} | } | ||||
int json_object_object_length(const struct json_object *jso) | int json_object_object_length(const struct json_object *jso) | ||||
@@ -638,12 +681,26 @@ size_t json_c_object_sizeof(void) | |||||
struct json_object *json_object_object_get(const struct json_object *jso, const char *key) | struct json_object *json_object_object_get(const struct json_object *jso, const char *key) | ||||
{ | { | ||||
struct json_object *result = NULL; | struct json_object *result = NULL; | ||||
json_object_object_get_ex(jso, key, &result); | |||||
json_object_object_get_ex_len(jso, key, strlen(key), &result); | |||||
return result; | |||||
} | |||||
struct json_object *json_object_object_get_len(const struct json_object *jso, const char *key, | |||||
const size_t len) | |||||
{ | |||||
struct json_object *result = NULL; | |||||
json_object_object_get_ex_len(jso, key, len, &result); | |||||
return result; | return result; | ||||
} | } | ||||
json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, | json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, | ||||
struct json_object **value) | struct json_object **value) | ||||
{ | |||||
return json_object_object_get_ex_len(jso, key, strlen(key), value); | |||||
} | |||||
json_bool json_object_object_get_ex_len(const struct json_object *jso, const char *key, | |||||
const size_t len, struct json_object **value) | |||||
{ | { | ||||
if (value != NULL) | if (value != NULL) | ||||
*value = NULL; | *value = NULL; | ||||
@@ -654,8 +711,10 @@ json_bool json_object_object_get_ex(const struct json_object *jso, const char *k | |||||
switch (jso->o_type) | switch (jso->o_type) | ||||
{ | { | ||||
case json_type_object: | case json_type_object: | ||||
return lh_table_lookup_ex(JC_OBJECT_C(jso)->c_object, (const void *)key, | |||||
(void **)value); | |||||
{ | |||||
const struct json_key hashable = {len, {key}}; | |||||
return json_object_object_get_key(jso, &hashable, value); | |||||
} | |||||
default: | default: | ||||
if (value != NULL) | if (value != NULL) | ||||
*value = NULL; | *value = NULL; | ||||
@@ -663,10 +722,28 @@ json_bool json_object_object_get_ex(const struct json_object *jso, const char *k | |||||
} | } | ||||
} | } | ||||
json_bool json_object_object_get_key(const struct json_object *jso, const struct json_key *key, | |||||
struct json_object **value) | |||||
{ | |||||
assert(json_object_get_type(jso) == json_type_object); | |||||
return lh_table_lookup_ex(JC_OBJECT_C(jso)->c_object, key, (void **)value); | |||||
} | |||||
void json_object_object_del(struct json_object *jso, const char *key) | void json_object_object_del(struct json_object *jso, const char *key) | ||||
{ | |||||
json_object_object_del_len(jso, key, strlen(key)); | |||||
} | |||||
void json_object_object_del_len(struct json_object *jso, const char *key, const size_t len) | |||||
{ | |||||
const struct json_key hashable = {len, {key}}; | |||||
json_object_object_del_key(jso, &hashable); | |||||
} | |||||
int json_object_object_del_key(struct json_object *jso, const struct json_key *key) | |||||
{ | { | ||||
assert(json_object_get_type(jso) == json_type_object); | assert(json_object_get_type(jso) == json_type_object); | ||||
lh_table_delete(JC_OBJECT(jso)->c_object, key); | |||||
return lh_table_delete(JC_OBJECT(jso)->c_object, key); | |||||
} | } | ||||
/* json_object_boolean */ | /* json_object_boolean */ | ||||
@@ -1153,7 +1230,7 @@ static int _json_object_userdata_to_json_string(struct json_object *jso, struct | |||||
int json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, int level, | int json_object_userdata_to_json_string(struct json_object *jso, struct printbuf *pb, int level, | ||||
int flags) | int flags) | ||||
{ | { | ||||
int userdata_len = strlen((const char *)jso->_userdata); | |||||
size_t userdata_len = strlen((const char *)jso->_userdata); | |||||
printbuf_memappend(pb, (const char *)jso->_userdata, userdata_len); | printbuf_memappend(pb, (const char *)jso->_userdata, userdata_len); | ||||
return userdata_len; | return userdata_len; | ||||
} | } | ||||
@@ -1658,6 +1735,28 @@ static int json_object_copy_serializer_data(struct json_object *src, struct json | |||||
*/ | */ | ||||
int json_c_shallow_copy_default(json_object *src, json_object *parent, const char *key, | int json_c_shallow_copy_default(json_object *src, json_object *parent, const char *key, | ||||
size_t index, json_object **dst) | size_t index, json_object **dst) | ||||
{ | |||||
if (key == NULL) | |||||
{ | |||||
return json_c_shallow_copy_default_len(src, parent, NULL, index, dst); | |||||
} | |||||
else | |||||
{ | |||||
const struct json_key nkey = {strlen(key), {key}}; | |||||
return json_c_shallow_copy_default_len(src, parent, &nkey, index, dst); | |||||
} | |||||
} | |||||
/** | |||||
* The default shallow copy implementation. Simply creates a new object of the same | |||||
* type but does *not* copy over _userdata nor retain any custom serializer. | |||||
* If custom serializers are in use, json_object_deep_copy() must be passed a shallow copy | |||||
* implementation that is aware of how to copy them. | |||||
* | |||||
* This always returns -1 or 1. It will never return 2 since it does not copy the serializer. | |||||
*/ | |||||
int json_c_shallow_copy_default_len(json_object *src, json_object *parent, | |||||
const struct json_key *key, size_t index, json_object **dst) | |||||
{ | { | ||||
switch (src->o_type) | switch (src->o_type) | ||||
{ | { | ||||
@@ -1733,14 +1832,15 @@ static int json_object_deep_copy_recursive(struct json_object *src, struct json_ | |||||
/* This handles the `json_type_null` case */ | /* This handles the `json_type_null` case */ | ||||
if (!iter.val) | if (!iter.val) | ||||
jso = NULL; | jso = NULL; | ||||
else if (json_object_deep_copy_recursive(iter.val, src, iter.key, UINT_MAX, &jso, | |||||
shallow_copy) < 0) | |||||
else if (json_object_deep_copy_recursive(iter.val, src, | |||||
json_key_data(iter.key), UINT_MAX, | |||||
&jso, shallow_copy) < 0) | |||||
{ | { | ||||
json_object_put(jso); | json_object_put(jso); | ||||
return -1; | return -1; | ||||
} | } | ||||
if (json_object_object_add(*dst, iter.key, jso) < 0) | |||||
if (json_object_object_add_key(*dst, iter.key, jso, 0) < 0) | |||||
{ | { | ||||
json_object_put(jso); | json_object_put(jso); | ||||
return -1; | return -1; | ||||
@@ -1783,6 +1883,89 @@ static int json_object_deep_copy_recursive(struct json_object *src, struct json_ | |||||
return 0; | return 0; | ||||
} | } | ||||
/* | |||||
* The actual guts of json_object_deep_copy(), with a few additional args | |||||
* needed so we can keep track of where we are within the object tree. | |||||
* | |||||
* Note: caller is responsible for freeing *dst if this fails and returns -1. | |||||
*/ | |||||
static int json_object_deep_copy_recursive_len(struct json_object *src, struct json_object *parent, | |||||
const struct json_key *key_in_parent, | |||||
size_t index_in_parent, struct json_object **dst, | |||||
json_c_shallow_copy_fn_len *shallow_copy) | |||||
{ | |||||
struct json_object_iter iter; | |||||
size_t src_array_len, ii; | |||||
int shallow_copy_rc = 0; | |||||
shallow_copy_rc = shallow_copy(src, parent, key_in_parent, index_in_parent, dst); | |||||
/* -1=error, 1=object created ok, 2=userdata set */ | |||||
if (shallow_copy_rc < 1) | |||||
{ | |||||
errno = EINVAL; | |||||
return -1; | |||||
} | |||||
assert(*dst != NULL); | |||||
switch (src->o_type) | |||||
{ | |||||
case json_type_object: | |||||
json_object_object_foreachC(src, iter) | |||||
{ | |||||
struct json_object *jso = NULL; | |||||
/* This handles the `json_type_null` case */ | |||||
if (!iter.val) | |||||
jso = NULL; | |||||
else if (json_object_deep_copy_recursive_len( | |||||
iter.val, src, iter.key, UINT_MAX, &jso, shallow_copy) < 0) | |||||
{ | |||||
json_object_put(jso); | |||||
return -1; | |||||
} | |||||
if (json_object_object_add_key(*dst, iter.key, jso, 0) < 0) | |||||
{ | |||||
json_object_put(jso); | |||||
return -1; | |||||
} | |||||
} | |||||
break; | |||||
case json_type_array: | |||||
src_array_len = json_object_array_length(src); | |||||
for (ii = 0; ii < src_array_len; ii++) | |||||
{ | |||||
struct json_object *jso = NULL; | |||||
struct json_object *jso1 = json_object_array_get_idx(src, ii); | |||||
/* This handles the `json_type_null` case */ | |||||
if (!jso1) | |||||
jso = NULL; | |||||
else if (json_object_deep_copy_recursive_len(jso1, src, NULL, ii, &jso, | |||||
shallow_copy) < 0) | |||||
{ | |||||
json_object_put(jso); | |||||
return -1; | |||||
} | |||||
if (json_object_array_add(*dst, jso) < 0) | |||||
{ | |||||
json_object_put(jso); | |||||
return -1; | |||||
} | |||||
} | |||||
break; | |||||
default: | |||||
break; | |||||
/* else, nothing to do, shallow_copy already did. */ | |||||
} | |||||
if (shallow_copy_rc != 2) | |||||
return json_object_copy_serializer_data(src, *dst); | |||||
return 0; | |||||
} | |||||
int json_object_deep_copy(struct json_object *src, struct json_object **dst, | int json_object_deep_copy(struct json_object *src, struct json_object **dst, | ||||
json_c_shallow_copy_fn *shallow_copy) | json_c_shallow_copy_fn *shallow_copy) | ||||
{ | { | ||||
@@ -1808,9 +1991,76 @@ int json_object_deep_copy(struct json_object *src, struct json_object **dst, | |||||
return rc; | return rc; | ||||
} | } | ||||
int json_object_deep_copy_len(struct json_object *src, struct json_object **dst, | |||||
json_c_shallow_copy_fn_len *shallow_copy) | |||||
{ | |||||
int rc; | |||||
/* Check if arguments are sane ; *dst must not point to a non-NULL object */ | |||||
if (!src || !dst || *dst) | |||||
{ | |||||
errno = EINVAL; | |||||
return -1; | |||||
} | |||||
if (shallow_copy == NULL) | |||||
shallow_copy = json_c_shallow_copy_default_len; | |||||
rc = json_object_deep_copy_recursive_len(src, NULL, NULL, UINT_MAX, dst, shallow_copy); | |||||
if (rc < 0) | |||||
{ | |||||
json_object_put(*dst); | |||||
*dst = NULL; | |||||
} | |||||
return rc; | |||||
} | |||||
static void json_abort(const char *message) | static void json_abort(const char *message) | ||||
{ | { | ||||
if (message != NULL) | if (message != NULL) | ||||
fprintf(stderr, "json-c aborts with error: %s\n", message); | fprintf(stderr, "json-c aborts with error: %s\n", message); | ||||
abort(); | abort(); | ||||
} | } | ||||
size_t json_key_size(const struct json_key *str) | |||||
{ | |||||
return (str->length > 0) ? (size_t)str->length : (size_t)(-(str->length)); | |||||
} | |||||
const char *json_key_data(const struct json_key *str) | |||||
{ | |||||
return (str->length > 0) ? str->str.pdata : str->str.idata; | |||||
} | |||||
struct json_key *json_key_new_ptr(const size_t length, const char *data) | |||||
{ | |||||
struct json_key *result = malloc(sizeof(struct json_key)); | |||||
if (result == NULL) | |||||
{ | |||||
return NULL; | |||||
} | |||||
result->length = (ssize_t)length; | |||||
result->str.pdata = data; | |||||
return result; | |||||
} | |||||
struct json_key *json_key_new_imm(const size_t length, const char *data) | |||||
{ | |||||
struct json_key *result; | |||||
if (length > | |||||
SSIZE_T_MAX - (sizeof(struct json_key) - sizeof(((struct json_key *)NULL)->str)) - 1) | |||||
{ | |||||
return NULL; | |||||
} | |||||
result = | |||||
malloc(sizeof(struct json_key) - sizeof(((struct json_key *)NULL)->str) + length + 1); | |||||
if (result == NULL) | |||||
{ | |||||
return NULL; | |||||
} | |||||
result->length = -((ssize_t)length); | |||||
memcpy(result->str.idata, data, length); | |||||
result->str.idata[length] = '\0'; | |||||
return result; | |||||
} |
@@ -357,7 +357,8 @@ JSON_EXPORT int json_object_object_length(const struct json_object *obj); | |||||
*/ | */ | ||||
JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); | JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); | ||||
/** Add an object field to a json_object of type json_type_object | |||||
/** | |||||
* @brief Add an object field to a json_object of type json_type_object | |||||
* | * | ||||
* The reference count of `val` will *not* be incremented, in effect | * The reference count of `val` will *not* be incremented, in effect | ||||
* transferring ownership that object to `obj`, and thus `val` will be | * transferring ownership that object to `obj`, and thus `val` will be | ||||
@@ -385,7 +386,39 @@ JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); | |||||
JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, | JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, | ||||
struct json_object *val); | struct json_object *val); | ||||
/** Add an object field to a json_object of type json_type_object | |||||
/** | |||||
* @brief Add an object field to a json_object of type json_type_object | |||||
* | |||||
* The reference count of `val` will *not* be incremented, in effect | |||||
* transferring ownership that object to `obj`, and thus `val` will be | |||||
* freed when `obj` is. (i.e. through `json_object_put(obj)`) | |||||
* | |||||
* If you want to retain a reference to the added object, independent | |||||
* of the lifetime of obj, you must increment the refcount with | |||||
* `json_object_get(val)` (and later release it with json_object_put()). | |||||
* | |||||
* Since ownership transfers to `obj`, you must make sure | |||||
* that you do in fact have ownership over `val`. For instance, | |||||
* json_object_new_object() will give you ownership until you transfer it, | |||||
* whereas json_object_object_get() does not. | |||||
* | |||||
* Any previous object stored under `key` in `obj` will have its refcount | |||||
* decremented, and be freed normally if that drops to zero. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name (a private copy will be duplicated), | |||||
* which is not terminated by a NULL ( @c '\0' ) character | |||||
* @param len the length of @p key | |||||
* @param val a json_object or NULL member to associate with the given field | |||||
* | |||||
* @return On success, @c 0 is returned. | |||||
* On error, a negative value is returned. | |||||
*/ | |||||
JSON_EXPORT int json_object_object_add_len(struct json_object *obj, const char *key, | |||||
const size_t len, struct json_object *val); | |||||
/** | |||||
* @brief Add an object field to a json_object of type json_type_object | |||||
* | * | ||||
* The semantics are identical to json_object_object_add, except that an | * The semantics are identical to json_object_object_add, except that an | ||||
* additional flag fields gives you more control over some detail aspects | * additional flag fields gives you more control over some detail aspects | ||||
@@ -397,12 +430,72 @@ JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, | |||||
* @param val a json_object or NULL member to associate with the given field | * @param val a json_object or NULL member to associate with the given field | ||||
* @param opts process-modifying options. To specify multiple options, use | * @param opts process-modifying options. To specify multiple options, use | ||||
* (OPT1|OPT2) | * (OPT1|OPT2) | ||||
* | |||||
* @return On success, @c 0 is returned. | |||||
* On error, a negative value is returned. | |||||
*/ | */ | ||||
JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key, | JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key, | ||||
struct json_object *const val, const unsigned opts); | struct json_object *const val, const unsigned opts); | ||||
/** Get the json_object associate with a given object field. | |||||
* Deprecated/discouraged: used json_object_object_get_ex instead. | |||||
/** | |||||
* @brief Add an object field to a json_object of type json_type_object | |||||
* | |||||
* The semantics are identical to json_object_object_add, except that an | |||||
* additional flag fields gives you more control over some detail aspects | |||||
* of processing. See the description of JSON_C_OBJECT_ADD_* flags for more | |||||
* details. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name (a private copy will be duplicated), | |||||
* which is not terminated by a NULL ( @c '\0' ) character | |||||
* @param len the length of @p key | |||||
* @param val a json_object or NULL member to associate with the given field | |||||
* @param opts process-modifying options. To specify multiple options, use | |||||
* (OPT1|OPT2) | |||||
* | |||||
* @return On success, @c 0 is returned. | |||||
* On error, a negative value is returned. | |||||
*/ | |||||
JSON_EXPORT int json_object_object_add_ex_len(struct json_object *obj, const char *const key, | |||||
const size_t len, struct json_object *const val, | |||||
const unsigned opts); | |||||
/** | |||||
* @brief Add an object field to a json_object of type json_type_object | |||||
* | |||||
* The semantics are identical to `json_object_object_add_ex`, except that @p key | |||||
* contains both the data and the length. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name (a private copy will be duplicated) | |||||
* @param val a json_object or NULL member to associate with the given field | |||||
* @param opts process-modifying options. To specify multiple options, use | |||||
* (OPT1|OPT2) | |||||
* @return On success, @c 0 is returned. | |||||
* On error, a negative value is returned. | |||||
*/ | |||||
JSON_EXPORT int json_object_object_add_key(struct json_object *obj, const struct json_key *key, | |||||
struct json_object *const val, const unsigned opts); | |||||
/** | |||||
* @brief Get the json_object associate with a given object field. | |||||
* | |||||
* @deprecated Deprecated/discouraged: used json_object_object_get_ex instead. | |||||
* | |||||
* This functions exactly like calling | |||||
* @code json_object_object_get_len(obj, key, strlen(key)) @endcode | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name | |||||
* @returns the json_object associated with the given field name | |||||
* @see json_object_object_get_len() | |||||
*/ | |||||
JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj, | |||||
const char *key); | |||||
/** | |||||
* @brief Get the json_object associate with a given object field. | |||||
* | |||||
* @deprecated Deprecated/discouraged: used json_object_object_get_ex_len instead. | |||||
* | * | ||||
* This returns NULL if the field is found but its value is null, or if | * This returns NULL if the field is found but its value is null, or if | ||||
* the field is not found, or if obj is not a json_type_object. If you | * the field is not found, or if obj is not a json_type_object. If you | ||||
@@ -419,13 +512,35 @@ JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *c | |||||
* or transfer ownership to prevent a memory leak). | * or transfer ownership to prevent a memory leak). | ||||
* | * | ||||
* @param obj the json_object instance | * @param obj the json_object instance | ||||
* @param key the object field name | |||||
* @param key the object field name, | |||||
* which is not terminated by a NULL ( @c '\0' ) character | |||||
* @param len the length of @p key | |||||
* @returns the json_object associated with the given field name | * @returns the json_object associated with the given field name | ||||
*/ | */ | ||||
JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj, | |||||
const char *key); | |||||
/** Get the json_object associated with a given object field. | |||||
JSON_EXPORT struct json_object *json_object_object_get_len(const struct json_object *obj, | |||||
const char *key, const size_t len); | |||||
/** | |||||
* @brief Get the json_object associated with a given object field. | |||||
* | |||||
* This functions exactly like calling | |||||
* @code json_object_object_get_ex_len(obj, key, strlen(key), value) @endcode | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name | |||||
* @param value a pointer where to store a reference to the json_object | |||||
* associated with the given field name. | |||||
* \n | |||||
* It is safe to pass a NULL value. | |||||
* @returns whether or not the key exists | |||||
* @see json_object_object_get_ex_len() | |||||
*/ | |||||
JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key, | |||||
struct json_object **value); | |||||
/** | |||||
* @brief Get the json_object associated with a given object field. | |||||
* | * | ||||
* This returns true if the key is found, false in all other cases (including | * This returns true if the key is found, false in all other cases (including | ||||
* if obj isn't a json_type_object). | * if obj isn't a json_type_object). | ||||
@@ -436,17 +551,41 @@ JSON_EXPORT struct json_object *json_object_object_get(const struct json_object | |||||
* than the owning parent (obj). Ownership of value is retained by obj. | * than the owning parent (obj). Ownership of value is retained by obj. | ||||
* | * | ||||
* @param obj the json_object instance | * @param obj the json_object instance | ||||
* @param key the object field name | |||||
* @param key the object field name, | |||||
* which is not terminated by a NULL ( @c '\0' ) character | |||||
* @param len the length of @p key | |||||
* @param value a pointer where to store a reference to the json_object | * @param value a pointer where to store a reference to the json_object | ||||
* associated with the given field name. | * associated with the given field name. | ||||
* | |||||
* \n | |||||
* It is safe to pass a NULL value. | * It is safe to pass a NULL value. | ||||
* @returns whether or not the key exists | * @returns whether or not the key exists | ||||
*/ | */ | ||||
JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key, | |||||
struct json_object **value); | |||||
JSON_EXPORT json_bool json_object_object_get_ex_len(const struct json_object *obj, const char *key, | |||||
const size_t len, struct json_object **value); | |||||
/** | |||||
* @brief Get the json_object associated with a given object field. | |||||
* | |||||
* *No* reference counts will be changed. There is no need to manually adjust | |||||
* reference counts through the json_object_put/json_object_get methods unless | |||||
* you need to have the child (value) reference maintain a different lifetime | |||||
* than the owning parent (obj). Ownership of value is retained by obj. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name | |||||
* @param value a pointer where to store a reference to the json_object | |||||
* associated with the given field name. | |||||
* \n | |||||
* It is safe to pass a NULL value. | |||||
* @returns true if the key is found, false in all other cases (including | |||||
* if obj isn't a json_type_object) | |||||
*/ | |||||
JSON_EXPORT json_bool json_object_object_get_key(const struct json_object *jso, | |||||
const struct json_key *key, | |||||
struct json_object **value); | |||||
/** Delete the given json_object field | |||||
/** | |||||
* @brief Delete the given json_object field | |||||
* | * | ||||
* The reference count will be decremented for the deleted object. If there | * The reference count will be decremented for the deleted object. If there | ||||
* are no more owners of the value represented by this key, then the value is | * are no more owners of the value represented by this key, then the value is | ||||
@@ -458,7 +597,81 @@ JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, c | |||||
JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key); | JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key); | ||||
/** | /** | ||||
* Iterate through all keys and values of an object. | |||||
* @brief Delete the given json_object field | |||||
* | |||||
* The reference count will be decremented for the deleted object. If there | |||||
* are no more owners of the value represented by this key, then the value is | |||||
* freed. Otherwise, the reference to the value will remain in memory. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name, | |||||
* which is not terminated by a NULL ( @c '\0' ) character | |||||
* @param len the length of @p key | |||||
*/ | |||||
JSON_EXPORT void json_object_object_del_len(struct json_object *jso, const char *key, | |||||
const size_t len); | |||||
/** | |||||
* @brief Delete the given json_object field | |||||
* | |||||
* The reference count will be decremented for the deleted object. If there | |||||
* are no more owners of the value represented by this key, then the value is | |||||
* freed. Otherwise, the reference to the value will remain in memory. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the object field name | |||||
*/ | |||||
JSON_EXPORT int json_object_object_del_key(struct json_object *obj, const struct json_key *key); | |||||
/** | |||||
* @brief Iterate through all keys and values of an object. | |||||
* | |||||
* Adding keys to the object while iterating is NOT allowed. | |||||
* | |||||
* Deleting an existing key, or replacing an existing key with a | |||||
* new value IS allowed. | |||||
* | |||||
* @param obj the json_object instance | |||||
* @param key the local name for the `char *` key variable defined in the body | |||||
* @param val the local name for the `json_object *` object variable defined in | |||||
* the body | |||||
*/ | |||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && \ | |||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) | |||||
#define json_object_object_foreach(obj, key, val) \ | |||||
const char *key = NULL; \ | |||||
struct json_object *val __attribute__((__unused__)) = NULL; \ | |||||
for (struct lh_entry *entry##key = json_object_get_object(obj)->head, \ | |||||
*entry_next##key = NULL; \ | |||||
({ \ | |||||
if (entry##key) \ | |||||
{ \ | |||||
key = json_key_data((struct json_key *)lh_entry_k(entry##key)); \ | |||||
val = (struct json_object *)lh_entry_v(entry##key); \ | |||||
entry_next##key = entry##key->next; \ | |||||
}; \ | |||||
entry##key; \ | |||||
}); \ | |||||
entry##key = entry_next##key) | |||||
#else /* ANSI C or MSC */ | |||||
#define json_object_object_foreach(obj, key, val) \ | |||||
const char *key = NULL; \ | |||||
struct json_object *val = NULL; \ | |||||
struct lh_entry *entry##key; \ | |||||
struct lh_entry *entry_next##key = NULL; \ | |||||
for (entry##key = json_object_get_object(obj)->head; \ | |||||
(entry##key ? (key = json_key_data((struct json_key *)lh_entry_k(entry##key)), \ | |||||
val = (struct json_object *)lh_entry_v(entry##key), \ | |||||
entry_next##key = entry##key->next, entry##key) \ | |||||
: 0); \ | |||||
entry##key = entry_next##key) | |||||
#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) */ | |||||
/** | |||||
* @brief Iterate through all keys and values of an object. | |||||
* | * | ||||
* Adding keys to the object while iterating is NOT allowed. | * Adding keys to the object while iterating is NOT allowed. | ||||
* | * | ||||
@@ -466,21 +679,22 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key | |||||
* new value IS allowed. | * new value IS allowed. | ||||
* | * | ||||
* @param obj the json_object instance | * @param obj the json_object instance | ||||
* @param key the local name for the char* key variable defined in the body | |||||
* @param val the local name for the json_object* object variable defined in | |||||
* @param key the local name for the `struct json_key *` key variable defined in the body | |||||
* @param val the local name for the `json_object *` object variable defined in | |||||
* the body | * the body | ||||
*/ | */ | ||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) | |||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && \ | |||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) | |||||
#define json_object_object_foreach(obj, key, val) \ | |||||
char *key = NULL; \ | |||||
#define json_object_object_foreach_len(obj, key, val) \ | |||||
struct json_key *key = NULL; \ | |||||
struct json_object *val __attribute__((__unused__)) = NULL; \ | struct json_object *val __attribute__((__unused__)) = NULL; \ | ||||
for (struct lh_entry *entry##key = json_object_get_object(obj)->head, \ | for (struct lh_entry *entry##key = json_object_get_object(obj)->head, \ | ||||
*entry_next##key = NULL; \ | *entry_next##key = NULL; \ | ||||
({ \ | ({ \ | ||||
if (entry##key) \ | if (entry##key) \ | ||||
{ \ | { \ | ||||
key = (char *)lh_entry_k(entry##key); \ | |||||
key = (struct json_key *)lh_entry_k(entry##key); \ | |||||
val = (struct json_object *)lh_entry_v(entry##key); \ | val = (struct json_object *)lh_entry_v(entry##key); \ | ||||
entry_next##key = entry##key->next; \ | entry_next##key = entry##key->next; \ | ||||
}; \ | }; \ | ||||
@@ -490,13 +704,13 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key | |||||
#else /* ANSI C or MSC */ | #else /* ANSI C or MSC */ | ||||
#define json_object_object_foreach(obj, key, val) \ | |||||
char *key = NULL; \ | |||||
#define json_object_object_foreach_len(obj, key, val) \ | |||||
struct json_key *key = NULL; \ | |||||
struct json_object *val = NULL; \ | struct json_object *val = NULL; \ | ||||
struct lh_entry *entry##key; \ | struct lh_entry *entry##key; \ | ||||
struct lh_entry *entry_next##key = NULL; \ | struct lh_entry *entry_next##key = NULL; \ | ||||
for (entry##key = json_object_get_object(obj)->head; \ | for (entry##key = json_object_get_object(obj)->head; \ | ||||
(entry##key ? (key = (char *)lh_entry_k(entry##key), \ | |||||
(entry##key ? (key = (struct json_key *)lh_entry_k(entry##key), \ | |||||
val = (struct json_object *)lh_entry_v(entry##key), \ | val = (struct json_object *)lh_entry_v(entry##key), \ | ||||
entry_next##key = entry##key->next, entry##key) \ | entry_next##key = entry##key->next, entry##key) \ | ||||
: 0); \ | : 0); \ | ||||
@@ -504,13 +718,14 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key | |||||
#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) */ | #endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) && (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) */ | ||||
/** Iterate through all keys and values of an object (ANSI C Safe) | |||||
/** | |||||
* @brief Iterate through all keys and values of an object (ANSI C Safe) | |||||
* @param obj the json_object instance | * @param obj the json_object instance | ||||
* @param iter the object iterator, use type json_object_iter | * @param iter the object iterator, use type json_object_iter | ||||
*/ | */ | ||||
#define json_object_object_foreachC(obj, iter) \ | #define json_object_object_foreachC(obj, iter) \ | ||||
for (iter.entry = json_object_get_object(obj)->head; \ | for (iter.entry = json_object_get_object(obj)->head; \ | ||||
(iter.entry ? (iter.key = (char *)lh_entry_k(iter.entry), \ | |||||
(iter.entry ? (iter.key = (struct json_key *)lh_entry_k(iter.entry), \ | |||||
iter.val = (struct json_object *)lh_entry_v(iter.entry), iter.entry) \ | iter.val = (struct json_object *)lh_entry_v(iter.entry), iter.entry) \ | ||||
: 0); \ | : 0); \ | ||||
iter.entry = iter.entry->next) | iter.entry = iter.entry->next) | ||||
@@ -656,7 +871,7 @@ JSON_EXPORT struct json_object *json_object_new_boolean(json_bool b); | |||||
* The type is coerced to a json_bool if the passed object is not a json_bool. | * The type is coerced to a json_bool if the passed object is not a json_bool. | ||||
* integer and double objects will return 0 if there value is zero | * integer and double objects will return 0 if there value is zero | ||||
* or 1 otherwise. If the passed object is a string it will return | * or 1 otherwise. If the passed object is a string it will return | ||||
* 1 if it has a non zero length. | |||||
* 1 if it has a non zero length. | |||||
* If any other object type is passed 0 will be returned, even non-empty | * If any other object type is passed 0 will be returned, even non-empty | ||||
* json_type_array and json_type_object objects. | * json_type_array and json_type_object objects. | ||||
* | * | ||||
@@ -1011,7 +1226,11 @@ JSON_EXPORT struct json_object *json_object_new_null(void); | |||||
JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object *obj2); | JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object *obj2); | ||||
/** | /** | ||||
* Perform a shallow copy of src into *dst as part of an overall json_object_deep_copy(). | |||||
* @deprecated This type is provided for backward-compatability only. | |||||
* It is reccomended to use `json_c_shallow_copy_fn_len` instead, and | |||||
* properly handle copying of keys with embedded NULL (<c>'\0'</c>) characters. | |||||
* | |||||
* @brief Perform a shallow copy of src into *dst as part of an overall json_object_deep_copy(). | |||||
* | * | ||||
* If src is part of a containing object or array, parent will be non-NULL, | * If src is part of a containing object or array, parent will be non-NULL, | ||||
* and key or index will be provided. | * and key or index will be provided. | ||||
@@ -1022,13 +1241,51 @@ JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object * | |||||
* json_object_deep_copy that it should not attempt to use the standard userdata | * json_object_deep_copy that it should not attempt to use the standard userdata | ||||
* copy function. | * copy function. | ||||
* | * | ||||
* @param src The source object to be copied | |||||
* @param parent The the parent of the current object | |||||
* @param key The key that <p>src</p> has in <p>parent</p>, if <p>parent</p> | |||||
* is an object | |||||
* @param index The index that <p>src</p> can be found at in <p>parent</p>, | |||||
* if <p>parent</p> is an array | |||||
* @param dst The destination object | |||||
* | |||||
* @return On success 1 or 2, -1 on errors | * @return On success 1 or 2, -1 on errors | ||||
* | |||||
* @since 0.13 | |||||
* | |||||
* @see json_c_shallow_copy_fn_len | |||||
* | |||||
*/ | */ | ||||
typedef int(json_c_shallow_copy_fn)(json_object *src, json_object *parent, const char *key, | typedef int(json_c_shallow_copy_fn)(json_object *src, json_object *parent, const char *key, | ||||
size_t index, json_object **dst); | size_t index, json_object **dst); | ||||
/** | /** | ||||
* The default shallow copy implementation for use with json_object_deep_copy(). | |||||
* @brief Perform a shallow copy of src into *dst as part of an overall json_object_deep_copy(). | |||||
* | |||||
* If src is part of a containing object or array, parent will be non-NULL, | |||||
* and key or index will be provided. | |||||
* When shallow_copy is called *dst will be NULL, and must be non-NULL when it returns. | |||||
* src will never be NULL. | |||||
* | |||||
* If shallow_copy sets the serializer on an object, return 2 to indicate to | |||||
* json_object_deep_copy that it should not attempt to use the standard userdata | |||||
* copy function. | |||||
* | |||||
* @since 0.16 | |||||
* | |||||
* @return On success 1 or 2, -1 on errors | |||||
*/ | |||||
typedef int(json_c_shallow_copy_fn_len)(json_object *src, json_object *parent, | |||||
const struct json_key *key, size_t index, | |||||
json_object **dst); | |||||
/** | |||||
* @deprecated This type is provided for backward-compatability only. | |||||
* It is reccomended to use `json_c_shallow_copy_fn_len` instead, and | |||||
* properly handle copying of keys with embedded NULL (<c>'\0'</c>) characters. | |||||
* | |||||
* @brief The default shallow copy implementation for use with json_object_deep_copy(). | |||||
* | |||||
* This simply calls the appropriate json_object_new_<type>() function and | * This simply calls the appropriate json_object_new_<type>() function and | ||||
* copies over the serializer function (_to_json_string internal field of | * copies over the serializer function (_to_json_string internal field of | ||||
* the json_object structure) but not any _userdata or _user_delete values. | * the json_object structure) but not any _userdata or _user_delete values. | ||||
@@ -1037,12 +1294,38 @@ typedef int(json_c_shallow_copy_fn)(json_object *src, json_object *parent, const | |||||
* your own custom serializer, you can call this first to create the new object | * your own custom serializer, you can call this first to create the new object | ||||
* before customizing it with json_object_set_serializer(). | * before customizing it with json_object_set_serializer(). | ||||
* | * | ||||
* @since 0.13 | |||||
* | |||||
* @return 1 on success, -1 on errors, but never 2. | * @return 1 on success, -1 on errors, but never 2. | ||||
* | |||||
* @see json_c_shallow_copy_fn_len | |||||
*/ | */ | ||||
JSON_EXPORT json_c_shallow_copy_fn json_c_shallow_copy_default; | JSON_EXPORT json_c_shallow_copy_fn json_c_shallow_copy_default; | ||||
/** | /** | ||||
* Copy the contents of the JSON object. | |||||
* @brief The default shallow copy implementation for use with json_object_deep_copy(). | |||||
* | |||||
* This simply calls the appropriate json_object_new_<type>() function and | |||||
* copies over the serializer function (_to_json_string internal field of | |||||
* the json_object structure) but not any _userdata or _user_delete values. | |||||
* | |||||
* If you're writing a custom shallow_copy function, perhaps because you're using | |||||
* your own custom serializer, you can call this first to create the new object | |||||
* before customizing it with json_object_set_serializer(). | |||||
* | |||||
* @since 0.16 | |||||
* | |||||
* @return 1 on success, -1 on errors, but never 2. | |||||
*/ | |||||
JSON_EXPORT json_c_shallow_copy_fn_len json_c_shallow_copy_default_len; | |||||
/** | |||||
* @deprecated This function is provided for backward-compatability only. | |||||
* It is reccomended to use `json_object_deep_copy_len` instead, as it will | |||||
* properly handle copying of keys with embedded NULL (<c>'\0'</c>) characters. | |||||
* | |||||
* @brief Copy the contents of the JSON object. | |||||
* | |||||
* The destination object must be initialized to NULL, | * The destination object must be initialized to NULL, | ||||
* to make sure this function won't overwrite an existing JSON object. | * to make sure this function won't overwrite an existing JSON object. | ||||
* | * | ||||
@@ -1056,12 +1339,84 @@ JSON_EXPORT json_c_shallow_copy_fn json_c_shallow_copy_default; | |||||
* when custom serializers are in use. See also | * when custom serializers are in use. See also | ||||
* json_object set_serializer. | * json_object set_serializer. | ||||
* | * | ||||
* @since 0.13 | |||||
* | |||||
* @returns 0 if the copy went well, -1 if an error occured during copy | * @returns 0 if the copy went well, -1 if an error occured during copy | ||||
* or if the destination pointer is non-NULL | * or if the destination pointer is non-NULL | ||||
*/ | */ | ||||
JSON_EXPORT int json_object_deep_copy(struct json_object *src, struct json_object **dst, | JSON_EXPORT int json_object_deep_copy(struct json_object *src, struct json_object **dst, | ||||
json_c_shallow_copy_fn *shallow_copy); | json_c_shallow_copy_fn *shallow_copy); | ||||
/** | |||||
* @brief Copy the contents of the JSON object. | |||||
* | |||||
* The destination object must be initialized to NULL, | |||||
* to make sure this function won't overwrite an existing JSON object. | |||||
* | |||||
* This does roughly the same thing as | |||||
* `json_tokener_parse(json_object_get_string(src))`. | |||||
* | |||||
* @param src source JSON object whose contents will be copied | |||||
* @param dst pointer to the destination object where the contents of `src`; | |||||
* make sure this pointer is initialized to NULL | |||||
* @param shallow_copy an optional function to copy individual objects, needed | |||||
* when custom serializers are in use. See also | |||||
* json_object set_serializer. | |||||
* | |||||
* @since 0.16 | |||||
* | |||||
* @returns 0 if the copy went well, -1 if an error occured during copy | |||||
* or if the destination pointer is non-NULL | |||||
*/ | |||||
JSON_EXPORT int json_object_deep_copy_len(struct json_object *src, struct json_object **dst, | |||||
json_c_shallow_copy_fn_len *shallow_copy); | |||||
/* Json Object Keys */ | |||||
/** | |||||
* @brief Get the length of the data stored in a `struct json_key *` | |||||
* | |||||
* @param str value to retrieve the length of | |||||
*/ | |||||
JSON_EXPORT size_t json_key_size(const struct json_key *str); | |||||
/** | |||||
* @brief Get the data from a `struct json_key *` | |||||
* | |||||
* @param str value to retrieve the data from | |||||
*/ | |||||
JSON_EXPORT const char *json_key_data(const struct json_key *str); | |||||
/** | |||||
* @brief Creates a new `struct json_key` that uses the `pdata` field. | |||||
* | |||||
* This avoids unneeded copying, for cases wher ethe key is in an area of | |||||
* memory that will not be modified or freed until after this object is freed. | |||||
* | |||||
* @param length The length of the data located at @p data | |||||
* @param data The data to include | |||||
* @return On success, a pointer to the new key is returned. | |||||
* On error, a null pointer is returned. | |||||
*/ | |||||
JSON_EXPORT struct json_key *json_key_new_ptr(const size_t length, const char *data); | |||||
/** | |||||
* @brief Creates a new `struct json_key` that uses the `idata` field. | |||||
* | |||||
* @param length The length of the data to copy into the returned value | |||||
* @param data The data to include and copy into the returned value | |||||
* @return On success, a pointer to the new key is returned. | |||||
* On error, a null pointer is returned. | |||||
*/ | |||||
JSON_EXPORT struct json_key *json_key_new_imm(const size_t length, const char *data); | |||||
/** | |||||
* @brief Frees the memory associated with a `struct json_key` | |||||
* | |||||
* @param key The data to free | |||||
*/ | |||||
JSON_EXPORT void json_key_del(const struct json_key *key); | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif | ||||
@@ -104,12 +104,12 @@ void json_object_iter_next(struct json_object_iterator *iter) | |||||
/** | /** | ||||
* **************************************************************************** | * **************************************************************************** | ||||
*/ | */ | ||||
const char *json_object_iter_peek_name(const struct json_object_iterator *iter) | |||||
const struct json_key *json_object_iter_peek_name(const struct json_object_iterator *iter) | |||||
{ | { | ||||
JASSERT(NULL != iter); | JASSERT(NULL != iter); | ||||
JASSERT(kObjectEndIterValue != iter->opaque_); | JASSERT(kObjectEndIterValue != iter->opaque_); | ||||
return (const char *)(((const struct lh_entry *)iter->opaque_)->k); | |||||
return (const struct json_key *)(((const struct lh_entry *)iter->opaque_)->k); | |||||
} | } | ||||
/** | /** | ||||
@@ -168,7 +168,8 @@ JSON_EXPORT void json_object_iter_next(struct json_object_iterator *iter); | |||||
* deleted or modified, and MUST NOT be modified or | * deleted or modified, and MUST NOT be modified or | ||||
* freed by the user. | * freed by the user. | ||||
*/ | */ | ||||
JSON_EXPORT const char *json_object_iter_peek_name(const struct json_object_iterator *iter); | |||||
JSON_EXPORT const struct json_key * | |||||
json_object_iter_peek_name(const struct json_object_iterator *iter); | |||||
/** Returns a pointer to the json-c instance representing the | /** Returns a pointer to the json-c instance representing the | ||||
* value of the referenced name/value pair, without altering | * value of the referenced name/value pair, without altering | ||||
@@ -98,8 +98,53 @@ struct json_object_string | |||||
void _json_c_set_last_err(const char *err_fmt, ...); | void _json_c_set_last_err(const char *err_fmt, ...); | ||||
/** | |||||
* The characters that can make up hexadecimal numbers | |||||
*/ | |||||
extern const char *json_hex_chars; | extern const char *json_hex_chars; | ||||
/** | |||||
* @brief A buffer of characters that may contain null charaters in the middle | |||||
* | |||||
* A buffer of data that can hold a normal null-terminated string | |||||
* (in which case `length` should just be equal to `strlen`) | |||||
* or a string with embedded null characters (in which case `length` reflects | |||||
* all the characters that make up the "string"). | |||||
* Either way, this struct can be treated as if it contains null characters, | |||||
* since the `length` member should always be equal to the proper size of the | |||||
* buffer and the terminating null character wouldn't be included | |||||
* (it wouldn't be counted by strlen). | |||||
*/ | |||||
struct json_key | |||||
{ | |||||
/** | |||||
* @brief Stores the length of the buffer | |||||
* | |||||
* If the length is positive, then `pdata` should be used. | |||||
* Otherwise, idata should be used. | |||||
*/ | |||||
ssize_t length; | |||||
union | |||||
{ | |||||
/** | |||||
* @brief A pointer to data that is stored elsewhere | |||||
* | |||||
* If the data stored there will not change for the lifetime of | |||||
* the key, use `pdata` rather than `idata`. | |||||
*/ | |||||
const char *pdata; | |||||
/** | |||||
* @brief Data stored inline | |||||
* | |||||
* If the data stored may be overwritten, such as if it is | |||||
* copied from the stack, this struct should be allocated with | |||||
* enough space to store the whole string (of length `len`) | |||||
* and one additional null character. | |||||
*/ | |||||
char idata[0]; | |||||
} str; | |||||
}; | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif | ||||
@@ -1131,7 +1131,8 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char * | |||||
{ | { | ||||
printbuf_memappend_fast(tok->pb, case_start, | printbuf_memappend_fast(tok->pb, case_start, | ||||
str - case_start); | str - case_start); | ||||
obj_field_name = strdup(tok->pb->buf); | |||||
obj_field_name = | |||||
json_key_new_imm(tok->pb->bpos, tok->pb->buf); | |||||
saved_state = json_tokener_state_object_field_end; | saved_state = json_tokener_state_object_field_end; | ||||
state = json_tokener_state_eatws; | state = json_tokener_state_eatws; | ||||
break; | break; | ||||
@@ -1179,7 +1180,7 @@ struct json_object *json_tokener_parse_ex(struct json_tokener *tok, const char * | |||||
goto redo_char; | goto redo_char; | ||||
case json_tokener_state_object_value_add: | case json_tokener_state_object_value_add: | ||||
json_object_object_add(current, obj_field_name, obj); | |||||
json_object_object_add_key(current, obj_field_name, obj, 0); | |||||
free(obj_field_name); | free(obj_field_name); | ||||
obj_field_name = NULL; | obj_field_name = NULL; | ||||
saved_state = json_tokener_state_object_sep; | saved_state = json_tokener_state_object_sep; | ||||
@@ -85,7 +85,7 @@ struct json_tokener_srec | |||||
enum json_tokener_state state, saved_state; | enum json_tokener_state state, saved_state; | ||||
struct json_object *obj; | struct json_object *obj; | ||||
struct json_object *current; | struct json_object *current; | ||||
char *obj_field_name; | |||||
struct json_key *obj_field_name; | |||||
}; | }; | ||||
#define JSON_TOKENER_DEFAULT_DEPTH 32 | #define JSON_TOKENER_DEFAULT_DEPTH 32 | ||||
@@ -215,7 +215,7 @@ JSON_EXPORT struct json_tokener *json_tokener_new_ex(int depth); | |||||
JSON_EXPORT void json_tokener_free(struct json_tokener *tok); | JSON_EXPORT void json_tokener_free(struct json_tokener *tok); | ||||
/** | /** | ||||
* Reset the state of a json_tokener, to prepare to parse a | |||||
* Reset the state of a json_tokener, to prepare to parse a | |||||
* brand new JSON object. | * brand new JSON object. | ||||
*/ | */ | ||||
JSON_EXPORT void json_tokener_reset(struct json_tokener *tok); | JSON_EXPORT void json_tokener_reset(struct json_tokener *tok); | ||||
@@ -279,7 +279,7 @@ JSON_EXPORT void json_tokener_set_flags(struct json_tokener *tok, int flags); | |||||
* the length of the last len parameter passed in. | * the length of the last len parameter passed in. | ||||
* | * | ||||
* The tokener does \b not maintain an internal buffer so the caller is | * The tokener does \b not maintain an internal buffer so the caller is | ||||
* responsible for a subsequent call to json_tokener_parse_ex with an | |||||
* responsible for a subsequent call to json_tokener_parse_ex with an | |||||
* appropriate str parameter starting with the extra characters. | * appropriate str parameter starting with the extra characters. | ||||
* | * | ||||
* This interface is presently not 64-bit clean due to the int len argument | * This interface is presently not 64-bit clean due to the int len argument | ||||
@@ -33,7 +33,7 @@ struct printbuf; | |||||
*/ | */ | ||||
struct json_object_iter | struct json_object_iter | ||||
{ | { | ||||
char *key; | |||||
struct json_key *key; | |||||
struct json_object *val; | struct json_object *val; | ||||
struct lh_entry *entry; | struct lh_entry *entry; | ||||
}; | }; | ||||
@@ -71,6 +71,8 @@ typedef enum json_type | |||||
json_type_string | json_type_string | ||||
} json_type; | } json_type; | ||||
typedef struct json_key json_key; | |||||
#ifdef __cplusplus | #ifdef __cplusplus | ||||
} | } | ||||
#endif | #endif | ||||
@@ -13,7 +13,7 @@ | |||||
#include "json_visit.h" | #include "json_visit.h" | ||||
#include "linkhash.h" | #include "linkhash.h" | ||||
static int _json_c_visit(json_object *jso, json_object *parent_jso, const char *jso_key, | |||||
static int _json_c_visit(json_object *jso, json_object *parent_jso, const struct json_key *jso_key, | |||||
size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg); | size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg); | ||||
int json_c_visit(json_object *jso, int future_flags, json_c_visit_userfunc *userfunc, void *userarg) | int json_c_visit(json_object *jso, int future_flags, json_c_visit_userfunc *userfunc, void *userarg) | ||||
@@ -28,7 +28,7 @@ int json_c_visit(json_object *jso, int future_flags, json_c_visit_userfunc *user | |||||
default: return JSON_C_VISIT_RETURN_ERROR; | default: return JSON_C_VISIT_RETURN_ERROR; | ||||
} | } | ||||
} | } | ||||
static int _json_c_visit(json_object *jso, json_object *parent_jso, const char *jso_key, | |||||
static int _json_c_visit(json_object *jso, json_object *parent_jso, const struct json_key *jso_key, | |||||
size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg) | size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg) | ||||
{ | { | ||||
int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); | int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); | ||||
@@ -57,7 +57,7 @@ static int _json_c_visit(json_object *jso, json_object *parent_jso, const char * | |||||
case json_type_object: | case json_type_object: | ||||
{ | { | ||||
json_object_object_foreach(jso, key, child) | |||||
json_object_object_foreach_len(jso, key, child) | |||||
{ | { | ||||
userret = _json_c_visit(child, jso, key, NULL, userfunc, userarg); | userret = _json_c_visit(child, jso, key, NULL, userfunc, userarg); | ||||
if (userret == JSON_C_VISIT_RETURN_POP) | if (userret == JSON_C_VISIT_RETURN_POP) | ||||
@@ -13,7 +13,8 @@ extern "C" { | |||||
#endif | #endif | ||||
typedef int(json_c_visit_userfunc)(json_object *jso, int flags, json_object *parent_jso, | typedef int(json_c_visit_userfunc)(json_object *jso, int flags, json_object *parent_jso, | ||||
const char *jso_key, size_t *jso_index, void *userarg); | |||||
const struct json_key *jso_key, size_t *jso_index, | |||||
void *userarg); | |||||
/** | /** | ||||
* Visit each object in the JSON hierarchy starting at jso. | * Visit each object in the JSON hierarchy starting at jso. | ||||
@@ -30,6 +30,7 @@ | |||||
#endif | #endif | ||||
#include "linkhash.h" | #include "linkhash.h" | ||||
#include "math_compat.h" | |||||
#include "random_seed.h" | #include "random_seed.h" | ||||
/* hash functions */ | /* hash functions */ | ||||
@@ -485,13 +486,14 @@ static unsigned long lh_char_hash(const void *k) | |||||
random_seed = seed; /* potentially racy */ | random_seed = seed; /* potentially racy */ | ||||
#endif | #endif | ||||
} | } | ||||
return hashlittle((const char *)k, strlen((const char *)k), random_seed); | |||||
return hashlittle(json_key_data((const struct json_key *)k), | |||||
json_key_size((const struct json_key *)k), random_seed); | |||||
} | } | ||||
int lh_char_equal(const void *k1, const void *k2) | int lh_char_equal(const void *k1, const void *k2) | ||||
{ | { | ||||
return (strcmp((const char *)k1, (const char *)k2) == 0); | |||||
return json_key_size(k1) == json_key_size(k2) && | |||||
memcmp(json_key_data(k1), json_key_data(k2), json_key_size(k1)) == 0; | |||||
} | } | ||||
struct lh_table *lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, | struct lh_table *lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, | ||||
@@ -137,7 +137,14 @@ struct lh_table | |||||
* A pointer onto the function responsible for freeing an entry. | * A pointer onto the function responsible for freeing an entry. | ||||
*/ | */ | ||||
lh_entry_free_fn *free_fn; | lh_entry_free_fn *free_fn; | ||||
/** | |||||
* A function that is capable of hashing entries in this table | |||||
*/ | |||||
lh_hash_fn *hash_fn; | lh_hash_fn *hash_fn; | ||||
/** | |||||
* A function that is capable of determining if entries in this table | |||||
* are equal | |||||
*/ | |||||
lh_equal_fn *equal_fn; | lh_equal_fn *equal_fn; | ||||
}; | }; | ||||
typedef struct lh_table lh_table; | typedef struct lh_table lh_table; | ||||
@@ -311,7 +318,7 @@ int lh_table_resize(struct lh_table *t, int new_size); | |||||
/** | /** | ||||
* @deprecated Don't use this outside of linkhash.h: | * @deprecated Don't use this outside of linkhash.h: | ||||
*/ | */ | ||||
#if (defined(AIX_CC) || (defined(_MSC_VER) && (_MSC_VER <= 1800)) ) | |||||
#if (defined(AIX_CC) || (defined(_MSC_VER) && (_MSC_VER <= 1800))) | |||||
/* VS2010 can't handle inline funcs, so skip it there */ | /* VS2010 can't handle inline funcs, so skip it there */ | ||||
#define _LH_INLINE | #define _LH_INLINE | ||||
#else | #else | ||||
@@ -26,6 +26,10 @@ set(ALL_TEST_NAMES | |||||
test_int_add | test_int_add | ||||
test_locale | test_locale | ||||
test_null | test_null | ||||
test_null_keys_add | |||||
test_null_keys_get | |||||
test_null_keys_del | |||||
test_null_keys_print | |||||
test_parse | test_parse | ||||
test_parse_int64 | test_parse_int64 | ||||
test_printbuf | test_printbuf | ||||
@@ -304,9 +304,11 @@ int main(int argc, char **argv) | |||||
/*json_object_object_add(my_object, "arr", my_array);*/ | /*json_object_object_add(my_object, "arr", my_array);*/ | ||||
printf("my_object=\n"); | printf("my_object=\n"); | ||||
json_object_object_foreach(my_object, key, val) | |||||
json_object_object_foreach_len(my_object, key, val) | |||||
{ | { | ||||
printf("\t%s: %s\n", key, json_object_to_json_string(val)); | |||||
putchar('\t'); | |||||
fwrite(json_key_data(key), json_key_size(key), 1, stdout); | |||||
printf(": %s\n", json_object_to_json_string(val)); | |||||
} | } | ||||
printf("my_object.to_string()=%s\n", json_object_to_json_string(my_object)); | printf("my_object.to_string()=%s\n", json_object_to_json_string(my_object)); | ||||
@@ -37,30 +37,33 @@ int main(int argc, char **argv) | |||||
printf("==== replace-value first loop starting ====\n"); | printf("==== replace-value first loop starting ====\n"); | ||||
const char *original_key = NULL; | |||||
const struct json_key *original_key = NULL; | |||||
orig_count = 0; | orig_count = 0; | ||||
json_object_object_foreach(my_object, key, val) | |||||
json_object_object_foreach_len(my_object, key, val) | |||||
{ | { | ||||
printf("Key at index %d is [%s] %d\n", orig_count, key, (val == NULL)); | |||||
printf("Key at index %d is [%s] %d\n", orig_count, json_key_data(key), | |||||
(val == NULL)); | |||||
orig_count++; | orig_count++; | ||||
if (strcmp(key, "foo2") != 0) | |||||
if (strcmp(json_key_data(key), "foo2") != 0) | |||||
continue; | continue; | ||||
printf("replacing value for key [%s]\n", key); | |||||
printf("replacing value for key [%s]\n", json_key_data(key)); | |||||
original_key = key; | original_key = key; | ||||
json_object_object_add(my_object, key, json_object_new_string("zzz")); | |||||
json_object_object_add(my_object, json_key_data(key), | |||||
json_object_new_string("zzz")); | |||||
} | } | ||||
printf("==== second loop starting ====\n"); | printf("==== second loop starting ====\n"); | ||||
int new_count = 0; | int new_count = 0; | ||||
int retval = 0; | int retval = 0; | ||||
json_object_object_foreach(my_object, key2, val2) | |||||
json_object_object_foreach_len(my_object, key2, val2) | |||||
{ | { | ||||
printf("Key at index %d is [%s] %d\n", new_count, key2, (val2 == NULL)); | |||||
printf("Key at index %d is [%s] %d\n", new_count, json_key_data(key2), | |||||
(val2 == NULL)); | |||||
new_count++; | new_count++; | ||||
if (strcmp(key2, "foo2") != 0) | |||||
if (strcmp(json_key_data(key2), "foo2") != 0) | |||||
continue; | continue; | ||||
printf("pointer for key [%s] does %smatch\n", key2, | |||||
printf("pointer for key [%s] does %smatch\n", json_key_data(key2), | |||||
(key2 == original_key) ? "" : "NOT "); | (key2 == original_key) ? "" : "NOT "); | ||||
if (key2 != original_key) | if (key2 != original_key) | ||||
retval = 1; | retval = 1; | ||||
@@ -92,15 +92,16 @@ int my_custom_serializer(struct json_object *jso, struct printbuf *pb, int level | |||||
return 0; | return 0; | ||||
} | } | ||||
json_c_shallow_copy_fn my_shallow_copy; | |||||
int my_shallow_copy(json_object *src, json_object *parent, const char *key, size_t index, | |||||
json_c_shallow_copy_fn_len my_shallow_copy; | |||||
int my_shallow_copy(json_object *src, json_object *parent, const struct json_key *key, size_t index, | |||||
json_object **dst) | json_object **dst) | ||||
{ | { | ||||
int rc; | int rc; | ||||
rc = json_c_shallow_copy_default(src, parent, key, index, dst); | |||||
rc = json_c_shallow_copy_default_len(src, parent, key, index, dst); | |||||
if (rc < 0) | if (rc < 0) | ||||
return rc; | return rc; | ||||
if (key != NULL && strcmp(key, "with_serializer") == 0) | |||||
if (key != NULL && json_key_size(key) == 15 && | |||||
strcmp(json_key_data(key), "with_serializer") == 0) | |||||
{ | { | ||||
printf("CALLED: my_shallow_copy on with_serializer object\n"); | printf("CALLED: my_shallow_copy on with_serializer object\n"); | ||||
void *userdata = json_object_get_userdata(src); | void *userdata = json_object_get_userdata(src); | ||||
@@ -199,7 +200,7 @@ int main(int argc, char **argv) | |||||
dst1 = NULL; | dst1 = NULL; | ||||
/* With a custom serializer in use, a custom shallow_copy function must also be used */ | /* With a custom serializer in use, a custom shallow_copy function must also be used */ | ||||
assert(-1 == json_object_deep_copy(src1, &dst1, NULL)); | assert(-1 == json_object_deep_copy(src1, &dst1, NULL)); | ||||
assert(0 == json_object_deep_copy(src1, &dst1, my_shallow_copy)); | |||||
assert(0 == json_object_deep_copy_len(src1, &dst1, my_shallow_copy)); | |||||
json_object *dest_with_serializer = json_object_object_get(dst1, "with_serializer"); | json_object *dest_with_serializer = json_object_object_get(dst1, "with_serializer"); | ||||
assert(dest_with_serializer != NULL); | assert(dest_with_serializer != NULL); | ||||
@@ -0,0 +1,154 @@ | |||||
/* | |||||
* Tests if binary strings are supported. | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "json_inttypes.h" | |||||
#include "json_object.h" | |||||
#include "json_tokener.h" | |||||
#include "linkhash.h" | |||||
int main(void) | |||||
{ | |||||
/* this test has embedded null characters in the key and value. | |||||
* check that it's still included after deserializing. */ | |||||
const char *input = "{ \"foo\": 14.5, \"bar\": [] }"; | |||||
const char *foo_key = "foo"; | |||||
const char *foo_value = "14.5"; | |||||
const char *bar_key = "bar"; | |||||
const char *toadd_key = "foo\0bar"; | |||||
const int toadd_key_len = 7; | |||||
const char *toadd_key_printable = "foo\\0bar"; | |||||
const char *toadd_value = "qwerty\0asdf"; | |||||
const int toadd_value_len = 12; | |||||
const char *toadd_value_printable = "qwerty\\0asdf"; | |||||
struct json_object *parsed = json_tokener_parse(input); | |||||
printf("Parsed input: %s\n", input); | |||||
printf("Result is "); | |||||
if (parsed == NULL) | |||||
{ | |||||
printf("NULL (error!)\n"); | |||||
return 1; // Error somewhere | |||||
} | |||||
else if (!json_object_is_type(parsed, json_type_object)) | |||||
{ | |||||
printf("not `json_type_object` (error!)\n"); | |||||
return 1; // Error somewhere | |||||
} | |||||
else | |||||
{ | |||||
printf("`json_type_object`\n"); | |||||
} | |||||
// Check nothing odd happened in parsing | |||||
if (json_object_object_length(parsed) != 2) | |||||
{ | |||||
printf("Should contain two fields (has %d) (error!)", | |||||
json_object_object_length(parsed)); | |||||
return 1; | |||||
} | |||||
json_bool key_present = json_object_object_get_ex(parsed, foo_key, NULL); | |||||
if (!key_present) | |||||
{ | |||||
printf("Should contain key \"%s\", but does not (error!)", foo_key); | |||||
return 1; | |||||
} | |||||
key_present = json_object_object_get_ex(parsed, bar_key, NULL); | |||||
if (!key_present) | |||||
{ | |||||
printf("Should contain key \"%s\", but does not (error!)", bar_key); | |||||
return 1; | |||||
} | |||||
// Check the previous keys are still the same present | |||||
struct json_object *foo = json_object_object_get(parsed, foo_key); | |||||
if (!json_object_is_type(foo, json_type_double)) | |||||
{ | |||||
printf("Key \"%s\" should be `json_type_double` (%d) but was %d (error!)\n", | |||||
foo_key, (int)json_type_double, json_object_get_type(foo)); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Key \"%s\" parsed as right type\n", foo_key); | |||||
} | |||||
struct json_object *bar = json_object_object_get(parsed, bar_key); | |||||
if (!json_object_is_type(bar, json_type_array)) | |||||
{ | |||||
printf("Key \"%s\" should be `json_type_array` (%d) but was %d (error!)\n", bar_key, | |||||
(int)json_type_array, json_object_get_type(bar)); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Key \"%s\" parsed as right type\n", bar_key); | |||||
} | |||||
// Add the new key | |||||
struct json_object *new_str = json_object_new_string_len(toadd_value, toadd_value_len); | |||||
if (json_object_object_add_ex_len(parsed, toadd_key, toadd_key_len, new_str, | |||||
JSON_C_OBJECT_KEY_IS_CONSTANT) != 0) | |||||
{ | |||||
printf("An error occured adding the key \"%s\" (error!)\n", toadd_key_printable); | |||||
return 1; | |||||
} | |||||
// Check the new key was actually added | |||||
if (json_object_object_length(parsed) != 3) | |||||
{ | |||||
printf("Should contain three fields after adding new key (has %d) (error!)", | |||||
json_object_object_length(parsed)); | |||||
return 1; | |||||
} | |||||
else if (json_object_object_get_len(parsed, toadd_key, toadd_key_len) != new_str) | |||||
{ | |||||
printf("Have three keys, but don't have the right value in \"%s\" (error!)\n", | |||||
toadd_key_printable); | |||||
printf("Keys :\n"); | |||||
json_object_object_foreach_len(parsed, key, val) | |||||
{ | |||||
putchar('\"'); | |||||
fwrite(json_key_data(key), json_key_size(key), 1, stdout); | |||||
printf("\" (%zd)\n", json_key_size(key)); | |||||
} | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Added the key \"%s\" successfully\n", toadd_key_printable); | |||||
} | |||||
// Check the previous keys are still the same present | |||||
foo = json_object_object_get(parsed, foo_key); | |||||
if (!json_object_is_type(foo, json_type_double)) | |||||
{ | |||||
printf("Key \"%s\" should be `json_type_double` (%d) but was %d (error!)\n", | |||||
foo_key, (int)json_type_double, json_object_get_type(foo)); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Key \"%s\" is still the same\n", foo_key); | |||||
} | |||||
bar = json_object_object_get(parsed, bar_key); | |||||
if (!json_object_is_type(bar, json_type_array)) | |||||
{ | |||||
printf("Key \"%s\" should be `json_type_array` (%d) but was %d (error!)\n", bar_key, | |||||
(int)json_type_array, json_object_get_type(bar)); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Key \"%s\" is still the same\n", bar_key); | |||||
} | |||||
json_object_put(parsed); | |||||
printf("PASS\n"); | |||||
return 0; | |||||
} |
@@ -0,0 +1,8 @@ | |||||
Parsed input: { "foo": 14.5, "bar": [] } | |||||
Result is `json_type_object` | |||||
Key "foo" parsed as right type | |||||
Key "bar" parsed as right type | |||||
Added the key "foo\0bar" successfully | |||||
Key "foo" is still the same | |||||
Key "bar" is still the same | |||||
PASS |
@@ -0,0 +1 @@ | |||||
test_basic.test |
@@ -0,0 +1,100 @@ | |||||
/* | |||||
* Tests if binary strings are supported. | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "json_inttypes.h" | |||||
#include "json_object.h" | |||||
#include "json_tokener.h" | |||||
int main(void) | |||||
{ | |||||
/* this test has embedded null characters in the key and value. | |||||
* check that it's still included after deserializing. */ | |||||
const char *input = "{ \"biff\\u0000\": null, \"\\u0000zxcvbnm\": 178 }"; | |||||
const char *expected_biff = "biff\0"; | |||||
const int expected_biff_len = 5; | |||||
const char *expected_biff_printable = "biff\\u0000"; | |||||
const char *expected_zxcv = "\0zxcvbnm"; | |||||
const int expected_zxcv_len = 8; | |||||
const char *expected_zxcv_printable = "\\u0000zxcvbnm"; | |||||
struct json_object *parsed = json_tokener_parse(input); | |||||
struct json_object *val; | |||||
json_bool key_present; | |||||
printf("Parsed input: %s\n", input); | |||||
printf("Result is "); | |||||
if (parsed == NULL) | |||||
{ | |||||
printf("NULL (error!)\n"); | |||||
return 1; // Error somewhere | |||||
} | |||||
else if (!json_object_is_type(parsed, json_type_object)) | |||||
{ | |||||
printf("not `json_type_object` (error!)\n"); | |||||
return 1; // Error somewhere | |||||
} | |||||
else | |||||
{ | |||||
printf("`json_type_object`\n"); | |||||
} | |||||
// Check nothing odd happened in parsing | |||||
if (json_object_object_length(parsed) != 2) | |||||
{ | |||||
printf("Should contain two fields (has %d) (error!)", | |||||
json_object_object_length(parsed)); | |||||
return 1; | |||||
} | |||||
key_present = json_object_object_get_ex_len(parsed, expected_biff, expected_biff_len, &val); | |||||
if (!key_present || !json_object_is_type(val, json_type_null)) | |||||
{ | |||||
printf("The key \"%s\" should be present and should be NULL (error!)\n", | |||||
expected_biff_printable); | |||||
return 1; | |||||
} | |||||
key_present = json_object_object_get_ex_len(parsed, expected_zxcv, expected_zxcv_len, &val); | |||||
if (!key_present || !json_object_is_type(val, json_type_int) || | |||||
json_object_get_int(val) != 178) | |||||
{ | |||||
printf("The key \"%s\" should be present and should be 178 (error!)\n", | |||||
expected_zxcv_printable); | |||||
return 1; | |||||
} | |||||
printf("Expected keys (\"%s\" and \"%s\") present with expected values\n", | |||||
expected_biff_printable, expected_zxcv_printable); | |||||
// Delete one key | |||||
json_object_object_del_len(parsed, expected_zxcv, expected_zxcv_len); | |||||
// Check it is gone | |||||
if (json_object_object_length(parsed) != 1) | |||||
{ | |||||
printf("Should contain only one field (has %d) (error!)", | |||||
json_object_object_length(parsed)); | |||||
return 1; | |||||
} | |||||
key_present = json_object_object_get_ex_len(parsed, expected_biff, expected_biff_len, &val); | |||||
if (!key_present || !json_object_is_type(val, json_type_null)) | |||||
{ | |||||
printf("The key \"%s\" should be present and should be NULL (error!)\n", | |||||
expected_biff_printable); | |||||
return 1; | |||||
} | |||||
key_present = json_object_object_get_ex_len(parsed, expected_zxcv, expected_zxcv_len, &val); | |||||
if (key_present) | |||||
{ | |||||
printf("The key \"%s\" should not be present (error!)\n", expected_zxcv_printable); | |||||
return 1; | |||||
} | |||||
printf("Key deleted properly\n"); | |||||
json_object_put(parsed); | |||||
printf("PASS\n"); | |||||
return 0; | |||||
} |
@@ -0,0 +1,5 @@ | |||||
Parsed input: { "biff\u0000": null, "\u0000zxcvbnm": 178 } | |||||
Result is `json_type_object` | |||||
Expected keys ("biff\u0000" and "\u0000zxcvbnm") present with expected values | |||||
Key deleted properly | |||||
PASS |
@@ -0,0 +1 @@ | |||||
test_basic.test |
@@ -0,0 +1,139 @@ | |||||
/* | |||||
* Tests if binary strings are supported. | |||||
*/ | |||||
#include "config.h" | |||||
#include <stdio.h> | |||||
#include <string.h> | |||||
#include "json_inttypes.h" | |||||
#include "json_object.h" | |||||
#include "json_tokener.h" | |||||
int main(void) | |||||
{ | |||||
/* this test has embedded null characters in the key and value. | |||||
* check that it's still included after deserializing. */ | |||||
const char *input = "{ \"foo\\u0000bar\": \"qwerty\\u0000asdf\" }"; | |||||
const char *expected_key = "foo\0bar"; | |||||
const int expected_key_len = 7; | |||||
const char *expected_key_printable = "foo\\0bar"; | |||||
const char *expected_value = "qwerty\0asdf"; | |||||
const int expected_value_len = 11; | |||||
const char *expected_value_printable = "qwerty\\0asdf"; | |||||
struct json_object *parsed = json_tokener_parse(input); | |||||
printf("Parsed input: %s\n", input); | |||||
printf("Result is "); | |||||
if (parsed == NULL) | |||||
{ | |||||
printf("NULL (error!)\n"); | |||||
return 1; // Error somewhere | |||||
} | |||||
else if (!json_object_is_type(parsed, json_type_object)) | |||||
{ | |||||
printf("not `json_type_object` (error!)\n"); | |||||
return 1; // Error somewhere | |||||
} | |||||
else | |||||
{ | |||||
printf("`json_type_object`\n"); | |||||
} | |||||
// Check nothing odd happened in parsing | |||||
if (json_object_object_length(parsed) != 1) | |||||
{ | |||||
printf("Should contain only one field (has %d) (error!)", | |||||
json_object_object_length(parsed)); | |||||
return 1; | |||||
} | |||||
json_bool key_present = json_object_object_get_ex(parsed, expected_key, NULL); | |||||
if (key_present) | |||||
{ | |||||
printf("The key \"%s\" should not be present when calling " | |||||
"`json_object_object_get_ex` " | |||||
"(the real key contains a NUL character). (error!)\n", | |||||
expected_key); | |||||
return 1; | |||||
} | |||||
// Check the key is present | |||||
struct json_object *string; | |||||
key_present = json_object_object_get_ex_len(parsed, expected_key, 7, &string); | |||||
if (!key_present) | |||||
{ | |||||
printf("The key \"%s\" should be present when calling " | |||||
"`json_object_object_get_ex_len` (error!)\n", | |||||
expected_key_printable); | |||||
return 1; | |||||
} | |||||
else if (string == NULL) | |||||
{ | |||||
printf("The key \"%s\" was present when calling " | |||||
"`json_object_object_get_ex_len`, but got NULL (error!)\n", | |||||
expected_key_printable); | |||||
return 1; | |||||
} | |||||
struct json_object *from_get_len = json_object_object_get_len(parsed, expected_key, 7); | |||||
if (string != from_get_len) | |||||
{ | |||||
printf("The value returned from `json_object_object_get_len` should be the " | |||||
"same as the one from `json_object_object_get_ex_len` (error!)\n" | |||||
"Got %p from `json_object_object_get_ex_len` but %p from " | |||||
"`json_object_object_get_len`\n", | |||||
string, from_get_len); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Key was present and same for " | |||||
"`json_object_object_get_ex_len` and `json_object_object_get_len`\n"); | |||||
} | |||||
// Check the value is right | |||||
if (!json_object_is_type(string, json_type_string)) | |||||
{ | |||||
printf( | |||||
"Value is wrong type, expected `json_type_string` (%d) but got %d (error!)\n", | |||||
json_type_string, json_object_get_type(string)); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Value is right type, `json_type_string`\n"); | |||||
} | |||||
const int actual_value_len = json_object_get_string_len(string); | |||||
const char *actual_value = json_object_get_string(string); | |||||
if (actual_value_len != expected_value_len) | |||||
{ | |||||
printf("Value is wrong length, expected %d but got %d (error!)\n", | |||||
expected_value_len, actual_value_len); | |||||
return 1; | |||||
} | |||||
else if (memcmp(expected_value, actual_value, actual_value_len) != 0) | |||||
{ | |||||
printf("Expected \"%s\" but got \"", expected_value_printable); | |||||
for (int i = 0; i < actual_value_len; ++i) | |||||
{ | |||||
if (actual_value[i] == '\0') | |||||
{ | |||||
puts("\\0"); | |||||
} | |||||
else | |||||
{ | |||||
putchar(actual_value[i]); | |||||
} | |||||
} | |||||
printf("\"\n"); | |||||
return 1; | |||||
} | |||||
else | |||||
{ | |||||
printf("Expected value matches actual\n"); | |||||
} | |||||
json_object_put(parsed); | |||||
printf("PASS\n"); | |||||
return 0; | |||||
} |
@@ -0,0 +1,6 @@ | |||||
Parsed input: { "foo\u0000bar": "qwerty\u0000asdf" } | |||||
Result is `json_type_object` | |||||
Key was present and same for `json_object_object_get_ex_len` and `json_object_object_get_len` | |||||
Value is right type, `json_type_string` | |||||
Expected value matches actual | |||||
PASS |
@@ -0,0 +1 @@ | |||||
test_basic.test |
@@ -0,0 +1,18 @@ | |||||
#include <stddef.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "json.h" | |||||
#include "parse_flags.h" | |||||
int main(int argc, char **argv) | |||||
{ | |||||
struct json_object *new_obj; | |||||
new_obj = json_tokener_parse("{ \"foo\\u0000bar\": \"qwerty\\u0000asdf\" }"); | |||||
printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj)); | |||||
json_object_put(new_obj); | |||||
return EXIT_SUCCESS; | |||||
} |
@@ -0,0 +1 @@ | |||||
new_obj.to_string()={ "foo\u0000bar": "qwerty\u0000asdf" } |
@@ -0,0 +1 @@ | |||||
test_basic.test |
@@ -6,6 +6,7 @@ | |||||
#include "json_object.h" | #include "json_object.h" | ||||
#include "json_object_iterator.h" | #include "json_object_iterator.h" | ||||
#include "json_tokener.h" | #include "json_tokener.h" | ||||
#include "linkhash.h" | |||||
int main(int atgc, char **argv) | int main(int atgc, char **argv) | ||||
{ | { | ||||
@@ -30,7 +31,7 @@ int main(int atgc, char **argv) | |||||
while (!json_object_iter_equal(&it, &itEnd)) | while (!json_object_iter_equal(&it, &itEnd)) | ||||
{ | { | ||||
printf("%s\n", json_object_iter_peek_name(&it)); | |||||
printf("%s\n", json_key_data(json_object_iter_peek_name(&it))); | |||||
printf("%s\n", json_object_to_json_string(json_object_iter_peek_value(&it))); | printf("%s\n", json_object_to_json_string(json_object_iter_peek_value(&it))); | ||||
json_object_iter_next(&it); | json_object_iter_next(&it); | ||||
} | } | ||||
@@ -215,7 +215,7 @@ static void do_clear_serializer(json_object *jso) | |||||
} | } | ||||
static int clear_serializer(json_object *jso, int flags, json_object *parent_jso, | static int clear_serializer(json_object *jso, int flags, json_object *parent_jso, | ||||
const char *jso_key, size_t *jso_index, void *userarg) | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
if (jso) | if (jso) | ||||
json_object_set_serializer(jso, NULL, NULL, NULL); | json_object_set_serializer(jso, NULL, NULL, NULL); | ||||
@@ -68,17 +68,17 @@ int main(void) | |||||
return 0; | return 0; | ||||
} | } | ||||
static int emit_object(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int emit_object(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
printf("flags: 0x%x, key: %s, index: %ld, value: %s\n", flags, | printf("flags: 0x%x, key: %s, index: %ld, value: %s\n", flags, | ||||
(jso_key ? jso_key : "(null)"), (jso_index ? (long)*jso_index : -1L), | |||||
(jso_key ? json_key_data(jso_key) : "(null)"), (jso_index ? (long)*jso_index : -1L), | |||||
json_object_to_json_string(jso)); | json_object_to_json_string(jso)); | ||||
return JSON_C_VISIT_RETURN_CONTINUE; | return JSON_C_VISIT_RETURN_CONTINUE; | ||||
} | } | ||||
static int skip_arrays(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int skip_arrays(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | (void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | ||||
if (json_object_get_type(jso) == json_type_array) | if (json_object_get_type(jso) == json_type_array) | ||||
@@ -86,16 +86,16 @@ static int skip_arrays(json_object *jso, int flags, json_object *parent_jso, con | |||||
return JSON_C_VISIT_RETURN_CONTINUE; | return JSON_C_VISIT_RETURN_CONTINUE; | ||||
} | } | ||||
static int pop_and_stop(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int pop_and_stop(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | (void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | ||||
if (jso_key != NULL && strcmp(jso_key, "subobj1") == 0) | |||||
if (jso_key != NULL && strcmp(json_key_data(jso_key), "subobj1") == 0) | |||||
{ | { | ||||
printf("POP after handling subobj1\n"); | printf("POP after handling subobj1\n"); | ||||
return JSON_C_VISIT_RETURN_POP; | return JSON_C_VISIT_RETURN_POP; | ||||
} | } | ||||
if (jso_key != NULL && strcmp(jso_key, "obj3") == 0) | |||||
if (jso_key != NULL && strcmp(json_key_data(jso_key), "obj3") == 0) | |||||
{ | { | ||||
printf("STOP after handling obj3\n"); | printf("STOP after handling obj3\n"); | ||||
return JSON_C_VISIT_RETURN_STOP; | return JSON_C_VISIT_RETURN_STOP; | ||||
@@ -103,11 +103,11 @@ static int pop_and_stop(json_object *jso, int flags, json_object *parent_jso, co | |||||
return JSON_C_VISIT_RETURN_CONTINUE; | return JSON_C_VISIT_RETURN_CONTINUE; | ||||
} | } | ||||
static int err_on_subobj2(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int err_on_subobj2(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | (void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | ||||
if (jso_key != NULL && strcmp(jso_key, "subobj2") == 0) | |||||
if (jso_key != NULL && strcmp(json_key_data(jso_key), "subobj2") == 0) | |||||
{ | { | ||||
printf("ERROR after handling subobj1\n"); | printf("ERROR after handling subobj1\n"); | ||||
return JSON_C_VISIT_RETURN_ERROR; | return JSON_C_VISIT_RETURN_ERROR; | ||||
@@ -115,8 +115,8 @@ static int err_on_subobj2(json_object *jso, int flags, json_object *parent_jso, | |||||
return JSON_C_VISIT_RETURN_CONTINUE; | return JSON_C_VISIT_RETURN_CONTINUE; | ||||
} | } | ||||
static int pop_array(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int pop_array(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | (void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | ||||
if (jso_index != NULL && (*jso_index == 0)) | if (jso_index != NULL && (*jso_index == 0)) | ||||
@@ -127,8 +127,8 @@ static int pop_array(json_object *jso, int flags, json_object *parent_jso, const | |||||
return JSON_C_VISIT_RETURN_CONTINUE; | return JSON_C_VISIT_RETURN_CONTINUE; | ||||
} | } | ||||
static int stop_array(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int stop_array(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
(void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | (void)emit_object(jso, flags, parent_jso, jso_key, jso_index, userarg); | ||||
if (jso_index != NULL && (*jso_index == 0)) | if (jso_index != NULL && (*jso_index == 0)) | ||||
@@ -139,11 +139,11 @@ static int stop_array(json_object *jso, int flags, json_object *parent_jso, cons | |||||
return JSON_C_VISIT_RETURN_CONTINUE; | return JSON_C_VISIT_RETURN_CONTINUE; | ||||
} | } | ||||
static int err_return(json_object *jso, int flags, json_object *parent_jso, const char *jso_key, | |||||
size_t *jso_index, void *userarg) | |||||
static int err_return(json_object *jso, int flags, json_object *parent_jso, | |||||
const struct json_key *jso_key, size_t *jso_index, void *userarg) | |||||
{ | { | ||||
printf("flags: 0x%x, key: %s, index: %ld, value: %s\n", flags, | printf("flags: 0x%x, key: %s, index: %ld, value: %s\n", flags, | ||||
(jso_key ? jso_key : "(null)"), (jso_index ? (long)*jso_index : -1L), | |||||
(jso_key ? json_key_data(jso_key) : "(null)"), (jso_index ? (long)*jso_index : -1L), | |||||
json_object_to_json_string(jso)); | json_object_to_json_string(jso)); | ||||
return 100; | return 100; | ||||
} | } |