From 3c0a85c9e8c60b892def49f1f16a1fa22d8d7d10 Mon Sep 17 00:00:00 2001 From: HexTheDragon Date: Mon, 5 Jul 2021 17:14:14 -0800 Subject: [PATCH] Additional changes to json to contain null characters --- json-c.sym | 6 +++- json_object.c | 69 ++++++++++++++++++++++++++++++++++-------- json_object.h | 28 ++++++++++++----- json_object_iterator.c | 4 +-- json_object_iterator.h | 3 +- json_types.h | 2 +- json_visit.c | 4 +-- json_visit.h | 3 +- linkhash.c | 7 ++++- linkhash.h | 9 ++++++ 10 files changed, 106 insertions(+), 29 deletions(-) diff --git a/json-c.sym b/json-c.sym index 71c0585..e69e18b 100644 --- a/json-c.sym +++ b/json-c.sym @@ -3,7 +3,7 @@ * Symbol versioning for libjson-c. * All exported symbols must be listed here. * - * See + * See * https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf */ @@ -166,8 +166,12 @@ JSONC_0.15 { JSONC_0.16 { global: + lh_string_data; + lh_string_size; + lh_string_print; json_object_object_add_len; json_object_object_add_ex_len; + json_object_object_del_len; json_object_object_get_len; json_object_object_get_ex_len; } JSONC_0.15; diff --git a/json_object.c b/json_object.c index ad40fa9..cae67dd 100644 --- a/json_object.c +++ b/json_object.c @@ -116,6 +116,14 @@ static inline const struct json_object_string *JC_STRING_C(const struct json_obj static inline struct json_object *json_object_new(enum json_type o_type, size_t alloc_size, json_object_to_json_string_fn *to_json_string); +static int json_object_object_add_internal(struct json_object *jso, const struct lh_string *key, + struct json_object *const val, const unsigned opts); +static int json_object_object_del_internal(struct json_object *jso_base, + const struct lh_string *key); +static json_bool json_object_object_get_internal(const struct json_object *jso, + const struct lh_string *key, + struct json_object **value); + static void json_object_object_delete(struct json_object *jso_base); static void json_object_string_delete(struct json_object *jso); static void json_object_array_delete(struct json_object *jso); @@ -506,7 +514,7 @@ static int json_object_object_to_json_string(struct json_object *jso, struct pri printbuf_strappend(pb, " "); indent(pb, level + 1, flags); printbuf_strappend(pb, "\""); - json_escape_str(pb, iter.key, strlen(iter.key), flags); + json_escape_str(pb, lh_string_data(iter.key), lh_string_size(iter.key), flags); if (flags & JSON_C_TO_STRING_SPACED) printbuf_strappend(pb, "\": "); else @@ -576,12 +584,21 @@ int json_object_object_add_ex(struct json_object *jso, const char *const key, int json_object_object_add_ex_len(struct json_object *jso, const char *const key, const int len, struct json_object *const val, const unsigned opts) +{ + // Created on the stack rather than calling `lh_string_new_ptr` + // or `lh_string_new_imm` since this saves copying `key` if it turns + // out the value already exists in the hash table + const struct lh_string hashable = {.length = len, .str = {.pdata = key}}; + return json_object_object_add_internal(jso, &hashable, val, opts); +} + +int json_object_object_add_internal(struct json_object *jso, const struct lh_string *key, + struct json_object *const val, const unsigned opts) { struct json_object *existing_value; struct lh_entry *existing_entry; unsigned long hash; /** Required due to the `lh_get_hash` function wanting a `const struct lh_string *` */ - const struct lh_string hashable = {.length = len, .str = {.pdata = key}}; assert(json_object_get_type(jso) == json_type_object); @@ -594,7 +611,7 @@ int json_object_object_add_ex_len(struct json_object *jso, const char *const key // We lookup the entry and replace the value, rather than just deleting // and re-adding it, so the existing key remains valid. - hash = lh_get_hash(JC_OBJECT(jso)->c_object, (const void *)&hashable); + hash = lh_get_hash(JC_OBJECT(jso)->c_object, key); existing_entry = (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) ? NULL @@ -602,9 +619,15 @@ int json_object_object_add_ex_len(struct json_object *jso, const char *const key if (existing_entry == NULL) { - const struct lh_string *k = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT) - ? lh_string_new_ptr(len, key) - : lh_string_new_imm(len, key); + // need to copy `key` because the caller might have created it + // on the stack, which would be more efficient than copying + // `lh_string_data` into `key->string.idata` if it had happened + // that `existing_entry` wasn't NULL. + const struct lh_string *k = + (opts & JSON_C_OBJECT_KEY_IS_CONSTANT) + ? lh_string_new_ptr(lh_string_size(key), lh_string_data(key)) + : lh_string_new_imm(lh_string_size(key), lh_string_data(key)); + // TODO some optimization where (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) if (k == NULL) { return -1; @@ -681,8 +704,10 @@ json_bool json_object_object_get_ex_len(const struct json_object *jso, const cha switch (jso->o_type) { case json_type_object: - return lh_table_lookup_ex(JC_OBJECT_C(jso)->c_object, (const void *)key, - (void **)value); + { + const struct lh_string hashable = {.length = len, .str = {.pdata = key}}; + return json_object_object_get_internal(jso, &hashable, value); + } default: if (value != NULL) *value = NULL; @@ -690,10 +715,28 @@ json_bool json_object_object_get_ex_len(const struct json_object *jso, const cha } } +json_bool json_object_object_get_internal(const struct json_object *jso, + const struct lh_string *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) +{ + json_object_object_del_len(jso, key, strlen(key)); +} + +void json_object_object_del_len(struct json_object *jso, const char *key, const int len) +{ + const struct lh_string hashable = {.length = len, .str = {.pdata = key}}; + json_object_object_del_internal(jso, &hashable); +} + +int json_object_object_del_internal(struct json_object *jso, const struct lh_string *key) { 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 */ @@ -1680,7 +1723,7 @@ static int json_object_copy_serializer_data(struct json_object *src, struct json * * This always returns -1 or 1. It will never return 2 since it does not copy the serializer. */ -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 struct lh_string *key, size_t index, json_object **dst) { switch (src->o_type) @@ -1728,8 +1771,8 @@ int json_c_shallow_copy_default(json_object *src, json_object *parent, const cha * Note: caller is responsible for freeing *dst if this fails and returns -1. */ static int json_object_deep_copy_recursive(struct json_object *src, struct json_object *parent, - const char *key_in_parent, size_t index_in_parent, - struct json_object **dst, + const struct lh_string *key_in_parent, + size_t index_in_parent, struct json_object **dst, json_c_shallow_copy_fn *shallow_copy) { struct json_object_iter iter; @@ -1761,7 +1804,7 @@ static int json_object_deep_copy_recursive(struct json_object *src, struct json_ return -1; } - if (json_object_object_add(*dst, iter.key, jso) < 0) + if (json_object_object_add_internal(*dst, iter.key, jso, 0) < 0) { json_object_put(jso); return -1; diff --git a/json_object.h b/json_object.h index e3d4d56..6cf2594 100644 --- a/json_object.h +++ b/json_object.h @@ -547,6 +547,19 @@ JSON_EXPORT json_bool json_object_object_get_ex_len(const struct json_object *ob */ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key); +/** 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 + */ +void json_object_object_del_len(struct json_object *jso, const char *key, const int len); + /** * Iterate through all keys and values of an object. * @@ -560,17 +573,18 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key * @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) +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) #define json_object_object_foreach(obj, key, val) \ - char *key = NULL; \ + struct lh_string *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 = (char *)lh_entry_k(entry##key); \ + key = (struct lh_string *)lh_entry_k(entry##key); \ val = (struct json_object *)lh_entry_v(entry##key); \ entry_next##key = entry##key->next; \ }; \ @@ -600,7 +614,7 @@ JSON_EXPORT void json_object_object_del(struct json_object *obj, const char *key */ #define json_object_object_foreachC(obj, iter) \ for (iter.entry = json_object_get_object(obj)->head; \ - (iter.entry ? (iter.key = (char *)lh_entry_k(iter.entry), \ + (iter.entry ? (iter.key = (struct lh_string *)lh_entry_k(iter.entry), \ iter.val = (struct json_object *)lh_entry_v(iter.entry), iter.entry) \ : 0); \ iter.entry = iter.entry->next) @@ -746,7 +760,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. * 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 - * 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 * json_type_array and json_type_object objects. * @@ -1114,8 +1128,8 @@ JSON_EXPORT int json_object_equal(struct json_object *obj1, struct json_object * * * @return On success 1 or 2, -1 on errors */ -typedef int(json_c_shallow_copy_fn)(json_object *src, json_object *parent, const char *key, - size_t index, json_object **dst); +typedef int(json_c_shallow_copy_fn)(json_object *src, json_object *parent, + const struct lh_string *key, size_t index, json_object **dst); /** * The default shallow copy implementation for use with json_object_deep_copy(). diff --git a/json_object_iterator.c b/json_object_iterator.c index 1c2b3f2..0f065a4 100644 --- a/json_object_iterator.c +++ b/json_object_iterator.c @@ -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 lh_string *json_object_iter_peek_name(const struct json_object_iterator *iter) { JASSERT(NULL != iter); JASSERT(kObjectEndIterValue != iter->opaque_); - return (const char *)(((const struct lh_entry *)iter->opaque_)->k); + return (const struct lh_string *)(((const struct lh_entry *)iter->opaque_)->k); } /** diff --git a/json_object_iterator.h b/json_object_iterator.h index a9b1433..8b56479 100644 --- a/json_object_iterator.h +++ b/json_object_iterator.h @@ -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 * freed by the user. */ -JSON_EXPORT const char *json_object_iter_peek_name(const struct json_object_iterator *iter); +JSON_EXPORT const struct lh_string * +json_object_iter_peek_name(const struct json_object_iterator *iter); /** Returns a pointer to the json-c instance representing the * value of the referenced name/value pair, without altering diff --git a/json_types.h b/json_types.h index b7e55ad..2892ef3 100644 --- a/json_types.h +++ b/json_types.h @@ -33,7 +33,7 @@ struct printbuf; */ struct json_object_iter { - char *key; + struct lh_string *key; struct json_object *val; struct lh_entry *entry; }; diff --git a/json_visit.c b/json_visit.c index fb16fa6..43f2ec0 100644 --- a/json_visit.c +++ b/json_visit.c @@ -13,7 +13,7 @@ #include "json_visit.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 lh_string *jso_key, 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) @@ -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; } } -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 lh_string *jso_key, size_t *jso_index, json_c_visit_userfunc *userfunc, void *userarg) { int userret = userfunc(jso, 0, parent_jso, jso_key, jso_index, userarg); diff --git a/json_visit.h b/json_visit.h index 35c46f5..21f0013 100644 --- a/json_visit.h +++ b/json_visit.h @@ -13,7 +13,8 @@ extern "C" { #endif 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 lh_string *jso_key, size_t *jso_index, + void *userarg); /** * Visit each object in the JSON hierarchy starting at jso. diff --git a/linkhash.c b/linkhash.c index 36dd014..bc4c155 100644 --- a/linkhash.c +++ b/linkhash.c @@ -506,6 +506,11 @@ size_t lh_string_size(const struct lh_string *str) return (str->length > 0) ? (size_t)str->length : (size_t)(-(str->length)); } +size_t lh_string_print(const struct lh_string *key, FILE *stream) +{ + return fwrite(lh_string_data(key), lh_string_size(key), 1, stream); +} + const struct lh_string *lh_string_new_ptr(const size_t length, const char *data) { struct lh_string *result = malloc(sizeof(struct lh_string)); @@ -535,7 +540,7 @@ const struct lh_string *lh_string_new_imm(const size_t length, const char *data) result->length = -length; char *unconst = _LH_UNCONST(result->str.idata); memcpy(unconst, data, length); - unconst = '\0'; + unconst[length] = '\0'; return result; } diff --git a/linkhash.h b/linkhash.h index e304117..045474c 100644 --- a/linkhash.h +++ b/linkhash.h @@ -20,6 +20,7 @@ #define _json_c_linkhash_h_ #include "json_object.h" +#include #ifdef __cplusplus extern "C" { @@ -206,6 +207,14 @@ extern const char *lh_string_data(const struct lh_string *str); */ extern size_t lh_string_size(const struct lh_string *str); +/** + * @brief Print a `struct lh_string` to a given stream + * + * @param str value to print + * @param stream Stream to write data to + */ +extern size_t lh_string_print(const struct lh_string *str, FILE *stream); + /** * @brief Creates a new `struct lh_string` using the `pdata` field. *