| @@ -39,18 +39,6 @@ | |||||
| #error "The long long type isn't 64-bits" | #error "The long long type isn't 64-bits" | ||||
| #endif | #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. | // Don't define this. It's not thread-safe. | ||||
| /* #define REFCOUNT_DEBUG 1 */ | /* #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, | 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 *const val, const unsigned opts) | ||||
| { | { | ||||
| struct json_object *existing_value = NULL; | |||||
| 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 lh_string *` */ | |||||
| const struct lh_string hashable = {.length = len, .str = {.pdata = 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, (const void *)&hashable); | |||||
| 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); | |||||
| 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) | 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 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) | 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) | int32_t json_object_get_int(const struct json_object *jso) | ||||
| { | { | ||||
| int64_t cint64=0; | |||||
| int64_t cint64 = 0; | |||||
| double cdouble; | double cdouble; | ||||
| enum json_type o_type; | 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 */ | /* 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, iter.key, UINT_MAX, | |||||
| &jso, shallow_copy) < 0) | |||||
| { | { | ||||
| json_object_put(jso); | json_object_put(jso); | ||||
| return -1; | return -1; | ||||
| @@ -98,6 +98,9 @@ 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; | ||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||
| @@ -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,57 @@ 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(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) | 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, | struct lh_table *lh_table_new(int size, lh_entry_free_fn *free_fn, lh_hash_fn *hash_fn, | ||||
| @@ -79,6 +79,34 @@ typedef unsigned long(lh_hash_fn)(const void *k); | |||||
| */ | */ | ||||
| typedef int(lh_equal_fn)(const void *k1, const void *k2); | 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 | * An entry in the hash table | ||||
| */ | */ | ||||
| @@ -137,7 +165,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; | ||||
| @@ -157,6 +192,41 @@ typedef struct lh_table lh_table; | |||||
| #define lh_foreach_safe(table, entry, tmp) \ | #define lh_foreach_safe(table, entry, tmp) \ | ||||
| for (entry = table->head; entry && ((tmp = entry->next) || 1); 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. | * 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: | * @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 | ||||
| @@ -40,4 +40,17 @@ | |||||
| #define HAVE_DECL_NAN | #define HAVE_DECL_NAN | ||||
| #endif | #endif | ||||
| #include <limits.h> | |||||
| #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 | #endif | ||||