diff --git a/src/jq_test.c b/src/jq_test.c index 39456861a5..a83640c4fa 100644 --- a/src/jq_test.c +++ b/src/jq_test.c @@ -490,4 +490,19 @@ static void jv_test() { //jv_dump(jv_copy(o2), 0); printf("\n"); jv_free(o2); } + + { + jv test_unshared = JV_OBJECT(jv_string("some"), JV_ARRAY(jv_string("other"), jv_true())); + + assert(jv_is_unshared(test_unshared)); + jv_free(test_unshared); + + jv initial = jv_parse("{\"test\":[{\"some\":\"value\"}, 1, true, false, null]}"); + jv new = jv_unshare(jv_copy(initial)); + + assert(jv_equal(jv_copy(initial), jv_copy(new))); + assert(jv_is_unshared(new)); + jv_free(initial); + jv_free(new); + } } diff --git a/src/jv.c b/src/jv.c index e23d8ec124..cc42ea226a 100644 --- a/src/jv.c +++ b/src/jv.c @@ -1862,6 +1862,132 @@ jv jv_object_iter_value(jv object, int iter) { /* * Memory management */ +jv jv_unshare(jv input){ + switch(jv_get_kind(input)){ + case JV_KIND_INVALID: + { + if(!jv_invalid_has_msg(jv_copy(input))){ + jv_free(input); + return jv_invalid(); + } + return jv_invalid_with_msg(jv_unshare(jv_invalid_get_msg(jv_copy(input)))); + } + case JV_KIND_OBJECT: + { + jv keys = jv_keys(jv_copy(input)); + size_t keys_length = jv_array_length(jv_copy(keys)); + + jv output_object = jv_object(); + + for(size_t i = 0; i < keys_length; i++){ + jv key = jv_array_get(jv_copy(keys), i); + output_object = jv_object_set( + output_object, jv_unshare(key), + jv_unshare( + jv_object_get( + jv_copy(input), + jv_copy(key) + ) + ) + ); + } + + jv_free(keys); + jv_free(input); + return output_object; + } + case JV_KIND_ARRAY: + { + size_t amount = jv_array_length(jv_copy(input)); + + jv output_array = jv_array_sized(amount); + + for(size_t i = 0; i < amount; i++){ + output_array = jv_array_set( + output_array, + i, + jv_unshare( + jv_array_get( + jv_copy(input), + i + ) + ) + ); + } + + jv_free(input); + + return output_array; + } + case JV_KIND_STRING: + { + jv output_string = jv_string(jv_string_value(input)); + jv_free(input); + return output_string; + } + case JV_KIND_NUMBER: + { + double val = jv_number_value(input); + jv_free(input); + return jv_number(val); + } + case JV_KIND_TRUE: + jv_free(input); + return jv_true(); + case JV_KIND_FALSE: + jv_free(input); + return jv_false(); + case JV_KIND_NULL: + jv_free(input); + return jv_null(); + default: + return jv_invalid(); + } +} + +int jv_is_unshared(jv a){ + if(jv_get_refcnt(a) != 1){ + fprintf(stderr, "input refcnt != 1\n"); + return 1; + } + if(jv_get_kind(a) == JV_KIND_OBJECT){ + jv keys = jv_keys(jv_copy(a)); + size_t keys_length = jv_array_length(jv_copy(keys)); + for(size_t i = 0; i < keys_length; i++){ + jv key = jv_array_get(jv_copy(keys), i); + if(jv_get_refcnt(key) > 3){ + fprintf(stderr, "key in object does not have refcnt 1, %d\n", jv_get_refcnt(key)); + jv_free(key); + jv_free(keys); + return 0; + } + + jv value = jv_object_get(jv_copy(a), key); + jv_free(value); + if(!jv_is_unshared(value)){ + fprintf(stderr, "failed on object\n"); + jv_free(keys); + return 0; + } + } + + jv_free(keys); + + }else if(jv_get_kind(a) == JV_KIND_ARRAY){ + size_t a_length = jv_array_length(jv_copy(a)); + for(size_t i = 0; i < a_length; i++){ + jv value = jv_array_get(jv_copy(a), i); + jv_free(value); + if(!jv_is_unshared(value)){ + fprintf(stderr, "failed on array value\n"); + return 0; + } + } + } + + return 1; +} + jv jv_copy(jv j) { if (JVP_IS_ALLOCATED(j)) { jvp_refcnt_inc(j.u.ptr); diff --git a/src/jv.h b/src/jv.h index 083509ec26..d31f5b1ff8 100644 --- a/src/jv.h +++ b/src/jv.h @@ -51,6 +51,9 @@ jv_kind jv_get_kind(jv); const char* jv_kind_name(jv_kind); static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; } +//jv_unshare() creates a deep copy of the input aka the content of the output will be identical to the input, but no shared memory exists between them +jv jv_unshare(jv); +int jv_is_unshared(jv); jv jv_copy(jv); void jv_free(jv); diff --git a/src/jv_aux.c b/src/jv_aux.c index 6004799c6a..24ffb032c0 100644 --- a/src/jv_aux.c +++ b/src/jv_aux.c @@ -427,6 +427,8 @@ jv jv_setpath(jv root, jv path, jv value) { return jv_set(root, pathcurr, jv_setpath(subroot, pathrest, value)); } + + jv jv_getpath(jv root, jv path) { if (jv_get_kind(path) != JV_KIND_ARRAY) { jv_free(root); @@ -538,6 +540,7 @@ static int string_cmp(const void* pa, const void* pb){ return r; } + jv jv_keys_unsorted(jv x) { if (jv_get_kind(x) != JV_KIND_OBJECT) return jv_keys(x);