diff --git a/json_object.c b/json_object.c index 544542f..ad40fa9 100644 --- a/json_object.c +++ b/json_object.c @@ -39,18 +39,6 @@ #error "The long long type isn't 64-bits" #endif -#ifndef SSIZE_T_MAX -#if SIZEOF_SSIZE_T == SIZEOF_INT -#define SSIZE_T_MAX INT_MAX -#elif SIZEOF_SSIZE_T == SIZEOF_LONG -#define SSIZE_T_MAX LONG_MAX -#elif SIZEOF_SSIZE_T == SIZEOF_LONG_LONG -#define SSIZE_T_MAX LLONG_MAX -#else -#error Unable to determine size of ssize_t -#endif -#endif - // Don't define this. It's not thread-safe. /* #define REFCOUNT_DEBUG 1 */ @@ -589,38 +577,53 @@ 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) { - struct json_object *existing_value = NULL; + 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); + // 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 // 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, (const void *)&hashable); existing_entry = (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) ? NULL : 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); + const struct lh_string *k = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT) + ? lh_string_new_ptr(len, key) + : lh_string_new_imm(len, key); if (k == NULL) + { 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 lh_string` 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) @@ -761,7 +764,7 @@ struct json_object *json_object_new_int(int32_t i) int32_t json_object_get_int(const struct json_object *jso) { - int64_t cint64=0; + int64_t cint64 = 0; double cdouble; enum json_type o_type; @@ -1751,8 +1754,8 @@ static int json_object_deep_copy_recursive(struct json_object *src, struct json_ /* This handles the `json_type_null` case */ if (!iter.val) 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, iter.key, UINT_MAX, + &jso, shallow_copy) < 0) { json_object_put(jso); return -1; diff --git a/json_object_private.h b/json_object_private.h index e143b46..4f5e7c4 100644 --- a/json_object_private.h +++ b/json_object_private.h @@ -98,6 +98,9 @@ struct json_object_string void _json_c_set_last_err(const char *err_fmt, ...); +/** + * The characters that can make up hexadecimal numbers + */ extern const char *json_hex_chars; #ifdef __cplusplus diff --git a/linkhash.c b/linkhash.c index b021ef1..36dd014 100644 --- a/linkhash.c +++ b/linkhash.c @@ -30,6 +30,7 @@ #endif #include "linkhash.h" +#include "math_compat.h" #include "random_seed.h" /* hash functions */ @@ -485,13 +486,57 @@ static unsigned long lh_char_hash(const void *k) random_seed = seed; /* potentially racy */ #endif } - - return hashlittle((const char *)k, strlen((const char *)k), random_seed); + return hashlittle(lh_string_data((const struct lh_string *)k), + lh_string_size((const struct lh_string *)k), random_seed); } int lh_char_equal(const void *k1, const void *k2) { - return (strcmp((const char *)k1, (const char *)k2) == 0); + return lh_string_size(k1) == lh_string_size(k2) && + memcmp(lh_string_data(k1), lh_string_data(k2), lh_string_size(k1)) == 0; +} + +const char *lh_string_data(const struct lh_string *str) +{ + return (str->length > 0) ? str->str.pdata : str->str.idata; +} + +size_t lh_string_size(const struct lh_string *str) +{ + return (str->length > 0) ? (size_t)str->length : (size_t)(-(str->length)); +} + +const struct lh_string *lh_string_new_ptr(const size_t length, const char *data) +{ + struct lh_string *result = malloc(sizeof(struct lh_string)); + if (result == NULL) + { + return NULL; + } + result->length = length; + result->str.pdata = data; + return result; +} + +const struct lh_string *lh_string_new_imm(const size_t length, const char *data) +{ + struct lh_string *result; + if (length > + SSIZE_T_MAX - (sizeof(struct lh_string) - sizeof(((struct lh_string *)NULL)->str)) - 1) + { + return NULL; + } + result = + malloc(sizeof(struct lh_string) - sizeof(((struct lh_string *)NULL)->str) + length + 1); + if (result == NULL) + { + return NULL; + } + result->length = -length; + char *unconst = _LH_UNCONST(result->str.idata); + memcpy(unconst, data, length); + unconst = '\0'; + return result; } struct lh_table *lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, diff --git a/linkhash.h b/linkhash.h index 0ddfa54..e304117 100644 --- a/linkhash.h +++ b/linkhash.h @@ -79,6 +79,34 @@ typedef unsigned long(lh_hash_fn)(const void *k); */ typedef int(lh_equal_fn)(const void *k1, const void *k2); +/** + * @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 lh_string +{ + /** + * @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 + { + const char *pdata; + const char idata[0]; + } str; +}; + /** * An entry in the hash table */ @@ -137,7 +165,14 @@ struct lh_table * A pointer onto the function responsible for freeing an entry. */ lh_entry_free_fn *free_fn; + /** + * A function that is capable of hashing entries in this table + */ lh_hash_fn *hash_fn; + /** + * A function that is capable of determining if entries in this table + * are equal + */ lh_equal_fn *equal_fn; }; typedef struct lh_table lh_table; @@ -157,6 +192,41 @@ typedef struct lh_table lh_table; #define lh_foreach_safe(table, entry, tmp) \ for (entry = table->head; entry && ((tmp = entry->next) || 1); entry = tmp) +/** + * @brief Get the data from a `struct lh_string *` + * + * @param str value to retrieve the data from + */ +extern const char *lh_string_data(const struct lh_string *str); + +/** + * @brief Get the length of the data stored in a `struct lh_string *` + * + * @param str value to retrieve the length of + */ +extern size_t lh_string_size(const struct lh_string *str); + +/** + * @brief Creates a new `struct lh_string` using 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 `NULL` on error + */ +extern const struct lh_string *lh_string_new_ptr(const size_t length, const char *data); + +/** + * @brief Creates a new `struct lh_string` using 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 `NULL` on error + */ +extern const struct lh_string *lh_string_new_imm(const size_t length, const char *data); + /** * Create a new linkhash table. * @@ -311,7 +381,7 @@ int lh_table_resize(struct lh_table *t, int new_size); /** * @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 */ #define _LH_INLINE #else diff --git a/math_compat.h b/math_compat.h index 2382fe1..9894776 100644 --- a/math_compat.h +++ b/math_compat.h @@ -40,4 +40,17 @@ #define HAVE_DECL_NAN #endif +#include +#ifndef SSIZE_T_MAX +#if SIZEOF_SSIZE_T == SIZEOF_INT +#define SSIZE_T_MAX INT_MAX +#elif SIZEOF_SSIZE_T == SIZEOF_LONG +#define SSIZE_T_MAX LONG_MAX +#elif SIZEOF_SSIZE_T == SIZEOF_LONG_LONG +#define SSIZE_T_MAX LLONG_MAX +#else +#error Unable to determine size of ssize_t +#endif +#endif + #endif