diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f6cfec..1814fb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -493,6 +493,7 @@ if (NOT JANSSON_WITHOUT_TESTS) test_equal test_load test_loadb + test_load_callback test_number test_object test_pack diff --git a/src/hashtable.h b/src/hashtable.h index d4c32ae..c112834 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -55,7 +55,7 @@ typedef struct hashtable { * * Returns 0 on success, -1 on error (out of memory). */ -int hashtable_init(hashtable_t *hashtable); +int hashtable_init(hashtable_t *hashtable) JANSSON_ATTRS(warn_unused_result); /** * hashtable_close - Release all resources used by a hashtable object diff --git a/src/jansson.h b/src/jansson.h index f8d57bd..430e8c7 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -39,6 +39,12 @@ extern "C" { #define JANSSON_THREAD_SAFE_REFCOUNT 1 #endif +#if defined(__GNUC__) || defined(__clang__) +#define JANSSON_ATTRS(...) __attribute__((__VA_ARGS__)) +#else +#define JANSSON_ATTRS(...) +#endif + /* types */ typedef enum { @@ -185,7 +191,7 @@ static JSON_INLINE enum json_error_code json_error_code(const json_error_t *e) { void json_object_seed(size_t seed); size_t json_object_size(const json_t *object); -json_t *json_object_get(const json_t *object, const char *key); +json_t *json_object_get(const json_t *object, const char *key) JANSSON_ATTRS(warn_unused_result); int json_object_set_new(json_t *object, const char *key, json_t *value); int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value); int json_object_del(json_t *object, const char *key); @@ -237,7 +243,7 @@ int json_object_iter_set(json_t *object, void *iter, json_t *value) } size_t json_array_size(const json_t *array); -json_t *json_array_get(const json_t *array, size_t index); +json_t *json_array_get(const json_t *array, size_t index) JANSSON_ATTRS(warn_unused_result); int json_array_set_new(json_t *array, size_t index, json_t *value); int json_array_append_new(json_t *array, json_t *value); int json_array_insert_new(json_t *array, size_t index, json_t *value); @@ -278,9 +284,9 @@ int json_real_set(json_t *real, double value); /* pack, unpack */ -json_t *json_pack(const char *fmt, ...); -json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...); -json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap); +json_t *json_pack(const char *fmt, ...) JANSSON_ATTRS(warn_unused_result); +json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) JANSSON_ATTRS(warn_unused_result); +json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) JANSSON_ATTRS(warn_unused_result); #define JSON_VALIDATE_ONLY 0x1 #define JSON_STRICT 0x2 @@ -291,8 +297,8 @@ int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char /* sprintf */ -json_t *json_sprintf(const char *fmt, ...); -json_t *json_vsprintf(const char *fmt, va_list ap); +json_t *json_sprintf(const char *fmt, ...) JANSSON_ATTRS(warn_unused_result, format(printf, 1, 2)); +json_t *json_vsprintf(const char *fmt, va_list ap) JANSSON_ATTRS(warn_unused_result, format(printf, 1, 0)); /* equality */ @@ -302,8 +308,8 @@ int json_equal(const json_t *value1, const json_t *value2); /* copying */ -json_t *json_copy(json_t *value); -json_t *json_deep_copy(const json_t *value); +json_t *json_copy(json_t *value) JANSSON_ATTRS(warn_unused_result); +json_t *json_deep_copy(const json_t *value) JANSSON_ATTRS(warn_unused_result); /* decoding */ @@ -316,12 +322,12 @@ json_t *json_deep_copy(const json_t *value); typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data); -json_t *json_loads(const char *input, size_t flags, json_error_t *error); -json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error); -json_t *json_loadf(FILE *input, size_t flags, json_error_t *error); -json_t *json_loadfd(int input, size_t flags, json_error_t *error); -json_t *json_load_file(const char *path, size_t flags, json_error_t *error); -json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error); +json_t *json_loads(const char *input, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); +json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); +json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); +json_t *json_loadfd(int input, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); +json_t *json_load_file(const char *path, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); +json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error) JANSSON_ATTRS(warn_unused_result); /* encoding */ @@ -339,7 +345,7 @@ json_t *json_load_callback(json_load_callback_t callback, void *data, size_t fla typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data); -char *json_dumps(const json_t *json, size_t flags); +char *json_dumps(const json_t *json, size_t flags) JANSSON_ATTRS(warn_unused_result); size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags); int json_dumpf(const json_t *json, FILE *output, size_t flags); int json_dumpfd(const json_t *json, int output, size_t flags); diff --git a/src/jansson_private.h b/src/jansson_private.h index d19cf9c..bf86c57 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -84,11 +84,11 @@ int jsonp_strtod(strbuffer_t *strbuffer, double *out); int jsonp_dtostr(char *buffer, size_t size, double value, int prec); /* Wrappers for custom memory functions */ -void* jsonp_malloc(size_t size); +void* jsonp_malloc(size_t size) JANSSON_ATTRS(warn_unused_result); void jsonp_free(void *ptr); -char *jsonp_strndup(const char *str, size_t length); -char *jsonp_strdup(const char *str); -char *jsonp_strndup(const char *str, size_t len); +char *jsonp_strndup(const char *str, size_t length) JANSSON_ATTRS(warn_unused_result); +char *jsonp_strdup(const char *str) JANSSON_ATTRS(warn_unused_result); +char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS(warn_unused_result); /* Windows compatibility */ diff --git a/src/pack_unpack.c b/src/pack_unpack.c index 19dbf93..4026fd9 100644 --- a/src/pack_unpack.c +++ b/src/pack_unpack.c @@ -511,48 +511,34 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap) if(root && strict == 1) { /* We need to check that all non optional items have been parsed */ const char *key; - int have_unrecognized_keys = 0; + /* keys_res is 1 for uninitialized, 0 for success, -1 for error. */ + int keys_res = 1; strbuffer_t unrecognized_keys; json_t *value; long unpacked = 0; - if (gotopt) { - /* We have optional keys, we need to iter on each key */ + + if (gotopt || json_object_size(root) != key_set.size) { json_object_foreach(root, key, value) { if(!hashtable_get(&key_set, key)) { unpacked++; /* Save unrecognized keys for the error message */ - if (!have_unrecognized_keys) { - strbuffer_init(&unrecognized_keys); - have_unrecognized_keys = 1; - } else { - strbuffer_append_bytes(&unrecognized_keys, ", ", 2); + if (keys_res == 1) { + keys_res = strbuffer_init(&unrecognized_keys); + } else if (!keys_res) { + keys_res = strbuffer_append_bytes(&unrecognized_keys, ", ", 2); } - strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); + + if (!keys_res) + keys_res = strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); } } - } else { - /* No optional keys, we can just compare the number of items */ - unpacked = (long)json_object_size(root) - (long)key_set.size; } if (unpacked) { - if (!gotopt) { - /* Save unrecognized keys for the error message */ - json_object_foreach(root, key, value) { - if(!hashtable_get(&key_set, key)) { - if (!have_unrecognized_keys) { - strbuffer_init(&unrecognized_keys); - have_unrecognized_keys = 1; - } else { - strbuffer_append_bytes(&unrecognized_keys, ", ", 2); - } - strbuffer_append_bytes(&unrecognized_keys, key, strlen(key)); - } - } - } set_error(s, "", json_error_end_of_input_expected, "%li object item(s) left unpacked: %s", - unpacked, strbuffer_value(&unrecognized_keys)); + unpacked, + keys_res ? "" : strbuffer_value(&unrecognized_keys)); strbuffer_close(&unrecognized_keys); goto out; } diff --git a/src/strbuffer.h b/src/strbuffer.h index 615b7f5..a0276d4 100644 --- a/src/strbuffer.h +++ b/src/strbuffer.h @@ -16,7 +16,7 @@ typedef struct { size_t size; /* bytes allocated */ } strbuffer_t; -int strbuffer_init(strbuffer_t *strbuff); +int strbuffer_init(strbuffer_t *strbuff) JANSSON_ATTRS(warn_unused_result); void strbuffer_close(strbuffer_t *strbuff); void strbuffer_clear(strbuffer_t *strbuff); diff --git a/test/suites/api/test_chaos.c b/test/suites/api/test_chaos.c index e65f2c7..82776a6 100644 --- a/test/suites/api/test_chaos.c +++ b/test/suites/api/test_chaos.c @@ -43,6 +43,53 @@ void chaos_free(void *obj) #define chaos_loop_new_value(json, initcall) \ chaos_loop(!json, json = initcall;, json_decref(json); json = NULL;) +int test_unpack() +{ + int ret = -1; + int v1; + int v2; + json_error_t error; + json_t *root = json_pack("{s:i, s:i, s:i, s:i}", "n1", 1, "n2", 2, "n3", 3, "n4", 4); + + if (!root) + return -1; + + if (!json_unpack_ex(root, &error, JSON_STRICT, "{s:i, s:i}", "n1", &v1, "n2", &v2)) + fail("Unexpected success"); + + if (json_error_code(&error) != json_error_end_of_input_expected) { + if (json_error_code(&error) != json_error_out_of_memory) + fail("Unexpected error code"); + + goto out; + } + + if (strcmp(error.text, "2 object item(s) left unpacked: n3, n4")) + goto out; + + ret = 0; + +out: + json_decref(root); + return ret; +} + +int dump_chaos_callback(const char *buffer, size_t size, void *data) +{ + json_t *obj = json_object(); + + (void)buffer; + (void)size; + (void)data; + + if (!obj) + return -1; + + json_decref(obj); + + return 0; +} + static void test_chaos() { json_malloc_t orig_malloc; @@ -54,9 +101,13 @@ static void test_chaos() json_t *txt = json_string("test"); json_t *intnum = json_integer(1); json_t *dblnum = json_real(0.5); + char *dumptxt = NULL; + json_t *dumpobj = json_pack("{s:[iiis], s:s}", + "key1", 1, 2, 3, "txt", + "key2", "v2"); int keyno; - if (!obj || !arr1 || !arr2 || !txt || !intnum || !dblnum) + if (!obj || !arr1 || !arr2 || !txt || !intnum || !dblnum || !dumpobj) fail("failed to allocate basic objects"); json_get_alloc_funcs(&orig_malloc, &orig_free); @@ -73,6 +124,12 @@ static void test_chaos() "another long string to force yet another reallocation of the string because " "that's what we are testing.")); + chaos_loop(test_unpack(),,); + + chaos_loop(json_dump_callback(dumpobj, dump_chaos_callback, NULL, JSON_INDENT(1)),,); + chaos_loop(json_dump_callback(dumpobj, dump_chaos_callback, NULL, JSON_INDENT(1) | JSON_SORT_KEYS),,); + chaos_loop(!dumptxt, dumptxt = json_dumps(dumpobj, JSON_COMPACT);, free(dumptxt); dumptxt = NULL;); + chaos_loop_new_value(json, json_copy(obj)); chaos_loop_new_value(json, json_deep_copy(obj)); @@ -83,6 +140,10 @@ static void test_chaos() chaos_loop_new_value(json, json_copy(intnum)); chaos_loop_new_value(json, json_copy(dblnum)); +#define JSON_LOAD_TXT "{\"n\":[1,2,3,4,5,6,7,8,9,10]}" + chaos_loop_new_value(json, json_loads(JSON_LOAD_TXT, 0, NULL)); + chaos_loop_new_value(json, json_loadb(JSON_LOAD_TXT, strlen(JSON_LOAD_TXT), 0, NULL)); + chaos_loop_new_value(json, json_sprintf("%s", "string")); for (keyno = 0; keyno < 100; ++keyno) { @@ -107,6 +168,7 @@ static void test_chaos() json_decref(txt); json_decref(intnum); json_decref(dblnum); + json_decref(dumpobj); } static void run_tests()