diff --git a/json_object.c b/json_object.c index e06c7ac..55f7469 100644 --- a/json_object.c +++ b/json_object.c @@ -355,7 +355,8 @@ static int json_object_object_to_json_string(struct json_object* jso, static void json_object_lh_entry_free(struct lh_entry *ent) { - free(ent->k); + if (!ent->k_is_constant) + free(ent->k); json_object_put((struct json_object*)ent->v); } @@ -396,6 +397,31 @@ struct lh_table* json_object_get_object(struct json_object *jso) } } +void json_object_object_add_ex(struct json_object* jso, + const char *const key, + struct json_object *const val, + const unsigned opts) +{ + // We lookup the entry and replace the value, rather than just deleting + // and re-adding it, so the existing key remains valid. + json_object *existing_value = NULL; + struct lh_entry *existing_entry; + const unsigned long hash = lh_get_hash(jso->o.c_object, (void*)key); + existing_entry = (opts & JSON_C_OBJECT_ADD_KEY_IS_NEW) ? NULL : + lh_table_lookup_entry_w_hash(jso->o.c_object, (void*)key, hash); + if (!existing_entry) + { + void *const k = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT) ? + (void*)key : strdup(key); + lh_table_insert_w_hash(jso->o.c_object, k, val, hash, opts); + return; + } + existing_value = (void *)existing_entry->v; + if (existing_value) + json_object_put(existing_value); + existing_entry->v = val; +} + void json_object_object_add(struct json_object* jso, const char *key, struct json_object *val) { @@ -407,7 +433,7 @@ void json_object_object_add(struct json_object* jso, const char *key, existing_entry = lh_table_lookup_entry_w_hash(jso->o.c_object, (void*)key, hash); if (!existing_entry) { - lh_table_insert_w_hash(jso->o.c_object, strdup(key), val, hash); + lh_table_insert_w_hash(jso->o.c_object, strdup(key), val, hash, 0); return; } existing_value = (void *)existing_entry->v; diff --git a/json_object.h b/json_object.h index 0dca0b1..d42c365 100644 --- a/json_object.h +++ b/json_object.h @@ -55,6 +55,36 @@ extern "C" { */ #define JSON_C_TO_STRING_NOZERO (1<<2) +/** + * A flag for the json_object_object_add_ex function which + * causes the value to be added without a check if it already exists. + * Note: it is the responsibilty of the caller to ensure that no + * key is added multiple times. If this is done, results are + * unpredictable. While this option is somewhat dangerous, it + * permits potentially large performance savings in code that + * knows for sure the key values are unique (e.g. because the + * code adds a well-known set of constant key values). + */ +#define JSON_C_OBJECT_ADD_KEY_IS_NEW (1<<1) +/** + * A flag for the json_object_object_add_ex function which + * flags the key as being constant memory. This means that + * the key will NOT be copied via strdup(), resulting in a + * potentially huge performance win (malloc, strdup and + * free are usually performance hogs). It is acceptable to + * use this flag for keys in non-constant memory blocks if + * the caller ensure that the memory holding the key lives + * longer than the corresponding json object. However, this + * is somewhat dangerous and should only be done if really + * justified. + * The general use-case for this flag is cases where the + * key is given as a real constant value in the function + * call, e.g. as in + * json_object_object_add_ex(obj, "ip", json, + * JSON_C_OBJECT_KEY_IS_CONSTANT); + */ +#define JSON_C_OBJECT_KEY_IS_CONSTANT (1<<2) + #undef FALSE #define FALSE ((json_bool)0) @@ -275,6 +305,22 @@ extern int json_object_object_length(struct json_object* obj); extern void json_object_object_add(struct json_object* obj, const char *key, struct json_object *val); +/** 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) + * @param val a json_object or NULL member to associate with the given field + * @param opts process-modifying options. To specify multiple options, use + * arithmetic or (OPT1|OPT2) + */ +extern void json_object_object_add_ex(struct json_object* obj, const char *key, + struct json_object *val, const unsigned opts); + /** Get the json_object associate with a given object field * * *No* reference counts will be changed. There is no need to manually adjust diff --git a/linkhash.c b/linkhash.c index b4763cf..f89859e 100644 --- a/linkhash.c +++ b/linkhash.c @@ -490,7 +490,7 @@ void lh_table_free(struct lh_table *t) } -int lh_table_insert_w_hash(struct lh_table *t, void *k, const void *v, const unsigned long h) +int lh_table_insert_w_hash(struct lh_table *t, void *k, const void *v, const unsigned long h, const unsigned opts) { unsigned long n; @@ -506,6 +506,7 @@ int lh_table_insert_w_hash(struct lh_table *t, void *k, const void *v, const uns } t->table[n].k = k; + t->table[n].k_is_constant = (opts & JSON_C_OBJECT_KEY_IS_CONSTANT); t->table[n].v = v; t->count++; @@ -523,7 +524,7 @@ int lh_table_insert_w_hash(struct lh_table *t, void *k, const void *v, const uns } int lh_table_insert(struct lh_table *t, void *k, const void *v) { - return lh_table_insert_w_hash(t, k, v, lh_get_hash(t, k)); + return lh_table_insert_w_hash(t, k, v, lh_get_hash(t, k), 0); } diff --git a/linkhash.h b/linkhash.h index 66f2d97..f967bf9 100644 --- a/linkhash.h +++ b/linkhash.h @@ -64,6 +64,7 @@ struct lh_entry { * The key. */ void *k; + int k_is_constant; /** * The value. */ @@ -241,8 +242,9 @@ extern int lh_table_insert(struct lh_table *t, void *k, const void *v); * @param k a pointer to the key to insert. * @param v a pointer to the value to insert. * @param h hash value of the key to insert + * @param opts opts, a subset of JSON_OBJECT_ADD_* flags is supported */ -extern int lh_table_insert_w_hash(struct lh_table *t, void *k, const void *v, const unsigned long h); +extern int lh_table_insert_w_hash(struct lh_table *t, void *k, const void *v, const unsigned long h, const unsigned opts); /**