diff --git a/src/jansson_private.h b/src/jansson_private.h index ea2593cd..b7dbdf92 100644 --- a/src/jansson_private.h +++ b/src/jansson_private.h @@ -60,11 +60,16 @@ typedef struct { json_int_t value; } json_integer_t; +typedef struct { + json_t json; +} json_simple_t; + #define json_to_object(json_) container_of(json_, json_object_t, json) #define json_to_array(json_) container_of(json_, json_array_t, json) #define json_to_string(json_) container_of(json_, json_string_t, json) #define json_to_real(json_) container_of(json_, json_real_t, json) #define json_to_integer(json_) container_of(json_, json_integer_t, json) +#define json_to_simple(json_) container_of(json_, json_simple_t, json) /* Create a string by taking ownership of an existing buffer */ json_t *jsonp_stringn_nocheck_own(const char *value, size_t len); @@ -94,6 +99,9 @@ char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS((warn_unused_resu int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size, size_t *key_len_out); +/* Helpers for location information */ +json_t *jsonp_simple(json_t *json, size_t flags); + /* Windows compatibility */ #if defined(_WIN32) || defined(WIN32) #if defined(_MSC_VER) /* MS compiller */ diff --git a/src/load.c b/src/load.c index 8ae7abd1..da8cd030 100644 --- a/src/load.c +++ b/src/load.c @@ -812,15 +812,15 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error) { } case TOKEN_TRUE: - json = json_true(); + json = jsonp_simple(json_true(), flags); break; case TOKEN_FALSE: - json = json_false(); + json = jsonp_simple(json_false(), flags); break; case TOKEN_NULL: - json = json_null(); + json = jsonp_simple(json_null(), flags); break; case '{': diff --git a/src/value.c b/src/value.c index d5a11fe5..e151191e 100644 --- a/src/value.c +++ b/src/value.c @@ -997,6 +997,33 @@ json_t *json_null(void) { return &the_null; } +/*** refcounted simple values ***/ + +json_t *jsonp_simple(json_t *json, size_t flags) +{ + json_simple_t *simple; + + /* pointless if not recording object location */ + if (!(flags & JSON_STORE_LOCATION)) + return json; + + simple = jsonp_malloc(sizeof(json_simple_t)); + if (!simple) + return NULL; + + simple->json.type = json->type; + simple->json.refcount = 1; + + return &simple->json; +} + +static void json_delete_simple(json_simple_t *simple) +{ + /* catch accidental calls for singletons */ + if (simple && simple->json.refcount == 0) + jsonp_free(simple); +} + /*** deletion ***/ void json_delete(json_t *json) { @@ -1019,11 +1046,14 @@ void json_delete(json_t *json) { case JSON_REAL: json_delete_real(json_to_real(json)); break; + case JSON_TRUE: + case JSON_FALSE: + case JSON_NULL: + json_delete_simple(json_to_simple(json)); + break; default: return; } - - /* json_delete is not called for true, false or null */ } /*** equality ***/ diff --git a/test/suites/api/test_load.c b/test/suites/api/test_load.c index 1c64b0c8..c1adc9ef 100644 --- a/test/suites/api/test_load.c +++ b/test/suites/api/test_load.c @@ -224,6 +224,35 @@ static void error_code() { fail("json_loads returned incorrect error code"); } +static void singleton_or_not() { + const char *simple_types[] = { "true", "false", "null", NULL }; + size_t flags; + json_t *json; + int i; + + for (i = 0; simple_types[i]; i++) { + flags = JSON_DECODE_ANY; + json = json_loads(simple_types[i], flags, NULL); + if (!json) + fail_args("json_loads failed for %s", simple_types[i]); + if (json->refcount != (size_t)-1) + fail_args("expected singleton when loading %s", simple_types[i]); + json_delete(json); + + flags |= JSON_STORE_LOCATION; + json = json_loads(simple_types[i], flags, NULL); + if (!json) + fail_args("json_loads failed for %s when storing location", + simple_types[i]); + if (json->refcount != 1) + fail_args("unexpected singleton when loading %s and storing location", + simple_types[i]); + json_delete(json); + json_decref(json); + } +} + + static void run_tests() { file_not_found(); very_long_file_name(); @@ -235,4 +264,5 @@ static void run_tests() { load_wrong_args(); position(); error_code(); + singleton_or_not(); }