diff --git a/json-c.sym b/json-c.sym index 2dedc6e..71c0585 100644 --- a/json-c.sym +++ b/json-c.sym @@ -165,6 +165,9 @@ JSONC_0.15 { } JSONC_0.14; JSONC_0.16 { -# global: -# ...new symbols here... + global: + json_object_object_add_len; + json_object_object_add_ex_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 c15a477..544542f 100644 --- a/json_object.c +++ b/json_object.c @@ -582,6 +582,12 @@ 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, struct json_object *const val, const unsigned opts) +{ + 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 int len, + struct json_object *const val, const unsigned opts) { struct json_object *existing_value = NULL; struct lh_entry *existing_entry; @@ -619,7 +625,13 @@ int json_object_object_add_ex(struct json_object *jso, const char *const key, 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 int 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) @@ -636,12 +648,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 *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 int len) +{ + struct json_object *result = NULL; + json_object_object_get_ex_len(jso, key, len, &result); return result; } json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, 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 int len, struct json_object **value) { if (value != NULL) *value = NULL; diff --git a/json_object.h b/json_object.h index d67f384..e3d4d56 100644 --- a/json_object.h +++ b/json_object.h @@ -385,6 +385,36 @@ 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, struct json_object *val); +/** 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 int len, + 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 @@ -397,13 +427,51 @@ 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 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(struct json_object *obj, const char *const key, struct json_object *const val, const unsigned opts); +/** 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 int len, 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. * + * 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); +/** Get the json_object associate with a given object field. + * 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 * the field is not found, or if obj is not a json_type_object. If you * need to distinguish between these cases, use json_object_object_get_ex(). @@ -419,11 +487,31 @@ JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *c * or transfer ownership to prevent a memory leak). * * @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 */ -JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj, - const char *key); + +JSON_EXPORT struct json_object *json_object_object_get_len(const struct json_object *obj, + const char *key, const int len); + +/** 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); /** Get the json_object associated with a given object field. * @@ -436,15 +524,17 @@ 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. * * @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 * associated with the given field name. - * + * \n * It is safe to pass a NULL value. * @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 int len, struct json_object **value); /** Delete the given json_object field * diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d7abf51..7d84442 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,6 +26,8 @@ set(ALL_TEST_NAMES test_int_add test_locale test_null + test_null_keys_add + test_null_keys_get test_parse test_parse_int64 test_printbuf diff --git a/tests/test_null_keys_add.c b/tests/test_null_keys_add.c new file mode 100644 index 0000000..ce31582 --- /dev/null +++ b/tests/test_null_keys_add.c @@ -0,0 +1,123 @@ +/* + * Tests if binary strings are supported. + */ + +#include "config.h" +#include +#include + +#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\": 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; + } + + // 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(parsed, toadd_key, new_str, + JSON_C_OBJECT_ADD_KEY_IS_NEW | + 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); + return 1; + } + else + { + printf("Added the key \"%s\" successfully\n", toadd_key_printable); + } + + // 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\" is still the same\n", foo_key); + } + struct json_object *bar = json_object_object_get(parsed, bar_key); + if (!json_object_is_type(foo, 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(foo)); + return 1; + } + else + { + printf("Key \"%s\" is still the same\n", bar_key); + } + + json_object_put(parsed); + printf("PASS\n"); + return 0; +} diff --git a/tests/test_null_keys_add.expected b/tests/test_null_keys_add.expected new file mode 100644 index 0000000..5e9a9c1 --- /dev/null +++ b/tests/test_null_keys_add.expected @@ -0,0 +1,6 @@ +Parsed input: { "foo": 14.5, "bar": [] } +Result is `json_type_object` +Added the key "foo\0bar" successfully +Key "foo" is still the same +Key "bar" is still the same +PASS diff --git a/tests/test_null_keys_add.test b/tests/test_null_keys_add.test new file mode 120000 index 0000000..58a13f4 --- /dev/null +++ b/tests/test_null_keys_add.test @@ -0,0 +1 @@ +test_basic.test \ No newline at end of file diff --git a/tests/test_null_keys_get.c b/tests/test_null_keys_get.c new file mode 100644 index 0000000..d63f579 --- /dev/null +++ b/tests/test_null_keys_get.c @@ -0,0 +1,139 @@ +/* + * Tests if binary strings are supported. + */ + +#include "config.h" +#include +#include + +#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 = 12; + 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; +} diff --git a/tests/test_null_keys_get.expected b/tests/test_null_keys_get.expected new file mode 100644 index 0000000..6a3c86d --- /dev/null +++ b/tests/test_null_keys_get.expected @@ -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 diff --git a/tests/test_null_keys_get.test b/tests/test_null_keys_get.test new file mode 120000 index 0000000..58a13f4 --- /dev/null +++ b/tests/test_null_keys_get.test @@ -0,0 +1 @@ +test_basic.test \ No newline at end of file