diff --git a/src/execute.c b/src/execute.c index 9ef8368329..806d0090dd 100644 --- a/src/execute.c +++ b/src/execute.c @@ -166,7 +166,7 @@ void stack_push(jq_state *jq, jv val) { assert(jv_is_valid(val)); jq->stk_top = stack_push_block(&jq->stk, jq->stk_top, sizeof(jv)); jv* sval = stack_block(&jq->stk, jq->stk_top); - *sval = val; + *sval = jv_return(val); } jv stack_pop(jq_state *jq) { @@ -1274,7 +1274,7 @@ jv jq_get_lib_dirs(jq_state *jq) { void jq_set_attrs(jq_state *jq, jv attrs) { assert(jv_get_kind(attrs) == JV_KIND_OBJECT); jv_free(jq->attrs); - jq->attrs = attrs; + jq->attrs = jv_return(attrs); } void jq_set_attr(jq_state *jq, jv attr, jv val) { @@ -1324,8 +1324,8 @@ jq_halt(jq_state *jq, jv exit_code, jv error_message) { assert(!jq->halted); jq->halted = 1; - jq->exit_code = exit_code; - jq->error_message = error_message; + jq->exit_code = jv_return(exit_code); + jq->error_message = jv_return(error_message); } int diff --git a/src/jq_test.c b/src/jq_test.c index 39456861a5..b422d6fcf6 100644 --- a/src/jq_test.c +++ b/src/jq_test.c @@ -490,4 +490,59 @@ static void jv_test() { //jv_dump(jv_copy(o2), 0); printf("\n"); jv_free(o2); } + + /// Borrowing + { + jv input = jv_string("some"); + jv_copy(jv_borrow(input)); + jv_free(jv_borrow(input)); + jv_free(jv_unborrow(jv_borrow(input))); + jv_free(jv_unborrow(input)); + jv_free(input); + + jv key = jv_string("key"); + jv val = jv_string("value"); + jv object = JV_OBJECT(jv_borrow(key), jv_borrow(val)); + jv_free(key); + jv_free(val); + jv modified = jv_object_set(jv_borrow(object), jv_string("key"), jv_string("other")); + assert(!jv_is_borrowed(modified)); + jv original_value = jv_getpath(object, JV_ARRAY(jv_string("key"))); + assert(jv_equal(original_value, jv_string("value"))); + jv modified_value = jv_getpath(modified, JV_ARRAY(jv_string("key"))); + assert(!jv_is_borrowed(modified_value)); + assert(jv_equal(modified_value, jv_string("other"))); + + jv array_value = jv_string("value"); + jv array = JV_ARRAY(jv_borrow(array_value)); + jv_free(array_value); + jv modified_array = jv_array_set(jv_borrow(array), 0, jv_string("other")); + assert(!jv_is_borrowed(modified_array)); + jv original_array_value = jv_array_get(array, 0); + assert(jv_equal(original_array_value, jv_string("value"))); + jv modified_array_value = jv_array_get(modified_array, 0); + assert(!jv_is_borrowed(modified_array_value)); + assert(jv_equal(modified_array_value, jv_string("other"))); + + jv invalid_msg = jv_string("message"); + jv invalid = jv_invalid_with_msg(jv_borrow(invalid_msg)); + jv_free(invalid_msg); + jv obtained_msg = jv_invalid_get_msg(invalid); + assert(!jv_is_borrowed(obtained_msg)); + assert(jv_equal(obtained_msg, jv_string("message"))); + + jv input_string = jv_string("test"); + jv modified_string = jv_string_append_buf(jv_borrow(input_string), "ing", 3); + assert(!jv_is_borrowed(input_string)); + assert(jv_equal(modified_string, jv_string("testing"))); + jv_free(input_string); + + jv string_a = jv_string("some"); + jv string_b = jv_string("thing"); + jv concat_string = jv_string_concat(jv_borrow(string_a), jv_borrow(string_b)); + jv_free(string_a); + jv_free(string_b); + assert(!jv_is_borrowed(concat_string)); + assert(jv_equal(concat_string, jv_string("something"))); + } } diff --git a/src/jv.c b/src/jv.c index e23d8ec124..90c2416c73 100644 --- a/src/jv.c +++ b/src/jv.c @@ -114,10 +114,10 @@ const char* jv_kind_name(jv_kind k) { return ""; } -const jv JV_NULL = {JVP_FLAGS_NULL, 0, 0, 0, {0}}; -const jv JV_INVALID = {JVP_FLAGS_INVALID, 0, 0, 0, {0}}; -const jv JV_FALSE = {JVP_FLAGS_FALSE, 0, 0, 0, {0}}; -const jv JV_TRUE = {JVP_FLAGS_TRUE, 0, 0, 0, {0}}; +const jv JV_NULL = {JVP_FLAGS_NULL, 0, 0, 0, 0, {0}}; +const jv JV_INVALID = {JVP_FLAGS_INVALID, 0, 0, 0, 0, {0}}; +const jv JV_FALSE = {JVP_FLAGS_FALSE, 0, 0, 0, 0, {0}}; +const jv JV_TRUE = {JVP_FLAGS_TRUE, 0, 0, 0, 0, {0}}; jv jv_true() { return JV_TRUE; @@ -149,9 +149,10 @@ typedef struct { jv jv_invalid_with_msg(jv err) { jvp_invalid* i = jv_mem_alloc(sizeof(jvp_invalid)); i->refcnt = JV_REFCNT_INIT; - i->errmsg = err; - jv x = {JVP_FLAGS_INVALID_MSG, 0, 0, 0, {&i->refcnt}}; + i->errmsg = jv_return(err); + + jv x = {JVP_FLAGS_INVALID_MSG, 0, 0, 0, 0, {&i->refcnt}}; return x; } @@ -590,7 +591,7 @@ static jv jvp_literal_number_new(const char * literal) { return JV_INVALID; } - jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, JV_NUMBER_SIZE_INIT, {&n->refcnt}}; + jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, 0, JV_NUMBER_SIZE_INIT, {&n->refcnt}}; return r; } @@ -674,7 +675,7 @@ jv jv_number(double x) { #else JV_KIND_NUMBER, #endif - 0, 0, 0, {.number = x} + 0, 0, 0, 0, {.number = x} }; return j; } @@ -806,7 +807,7 @@ static jvp_array* jvp_array_alloc(unsigned size) { } static jv jvp_array_new(unsigned size) { - jv r = {JVP_FLAGS_ARRAY, 0, 0, 0, {&jvp_array_alloc(size)->refcnt}}; + jv r = {JVP_FLAGS_ARRAY, 0, 0, 0, 0, {&jvp_array_alloc(size)->refcnt}}; return r; } @@ -869,7 +870,7 @@ static jv* jvp_array_write(jv* a, int i) { } new_array->length = new_length; jvp_array_free(*a); - jv new_jv = {JVP_FLAGS_ARRAY, 0, 0, new_length, {&new_array->refcnt}}; + jv new_jv = {JVP_FLAGS_ARRAY, 0, 0, 0, new_length, {&new_array->refcnt}}; *a = new_jv; return &new_array->elements[i]; } @@ -985,6 +986,8 @@ jv jv_array_get(jv j, int idx) { jv jv_array_set(jv j, int idx, jv val) { assert(JVP_HAS_KIND(j, JV_KIND_ARRAY)); + j = jv_return(j); + if (idx < 0) idx = jvp_array_length(j) + idx; if (idx < 0) { @@ -995,8 +998,8 @@ jv jv_array_set(jv j, int idx, jv val) { // copy/free of val,j coalesced jv* slot = jvp_array_write(&j, idx); jv_free(*slot); - *slot = val; - return j; + *slot = jv_return(val); + return jv_return(j); } jv jv_array_append(jv j, jv val) { @@ -1091,7 +1094,7 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) { length = out - s->data; s->data[length] = 0; s->length_hashed = length << 1; - jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, 0, {&s->refcnt}}; return r; } @@ -1102,7 +1105,7 @@ static jv jvp_string_new(const char* data, uint32_t length) { if (data != NULL) memcpy(s->data, data, length); s->data[length] = 0; - jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, 0, {&s->refcnt}}; return r; } @@ -1110,7 +1113,7 @@ static jv jvp_string_empty_new(uint32_t length) { jvp_string* s = jvp_string_alloc(length); s->length_hashed = 0; memset(s->data, 0, length); - jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, 0, {&s->refcnt}}; return r; } @@ -1153,7 +1156,7 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) { memcpy(news->data + currlen, data, len); news->data[currlen + len] = 0; jvp_string_free(string); - jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&news->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, 0, {&news->refcnt}}; return r; } } @@ -1421,7 +1424,7 @@ jv jv_string_slice(jv j, int start, int end) { } jv jv_string_concat(jv a, jv b) { - a = jvp_string_append(a, jv_string_value(b), + a = jvp_string_append(jv_return(a), jv_string_value(b), jvp_string_length(jvp_string_ptr(b))); jv_free(b); return a; @@ -1429,7 +1432,7 @@ jv jv_string_concat(jv a, jv b) { jv jv_string_append_buf(jv a, const char* buf, int len) { if (jvp_utf8_is_valid(buf, buf+len)) { - a = jvp_string_append(a, buf, len); + a = jvp_string_append(jv_return(a), buf, len); } else { jv b = jvp_string_copy_replace_bad(buf, len); a = jv_string_concat(a, b); @@ -1440,7 +1443,7 @@ jv jv_string_append_buf(jv a, const char* buf, int len) { jv jv_string_append_codepoint(jv a, uint32_t c) { char buf[5]; int len = jvp_utf8_encode(c, buf); - a = jvp_string_append(a, buf, len); + a = jvp_string_append(jv_return(a), buf, len); return a; } @@ -1521,7 +1524,7 @@ static jv jvp_object_new(int size) { for (int i=0; irefcnt}}; + jv r = {JVP_FLAGS_OBJECT, 0, 0, 0, size, {&obj->refcnt}}; return r; } @@ -1711,8 +1714,7 @@ static int jvp_object_equal(jv o1, jv o2) { if (jv_get_kind(slot->string) == JV_KIND_NULL) continue; jv* slot2 = jvp_object_read(o2, slot->string); if (!slot2) return 0; - // FIXME: do less refcounting here - if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0; + if (!jv_equal(jv_borrow(slot->value), jv_borrow(*slot2))) return 0; len1++; } return len1 == len2; @@ -1766,22 +1768,25 @@ int jv_object_has(jv object, jv key) { return res; } -jv jv_object_set(jv object, jv key, jv value) { - assert(JVP_HAS_KIND(object, JV_KIND_OBJECT)); +jv jv_object_set(jv input, jv key, jv value) { + assert(JVP_HAS_KIND(input, JV_KIND_OBJECT)); assert(JVP_HAS_KIND(key, JV_KIND_STRING)); + + input = jv_return(input); // copy/free of object, key, value coalesced - jv* slot = jvp_object_write(&object, key); + jv* slot = jvp_object_write(&input, jv_return(key)); jv_free(*slot); - *slot = value; - return object; + *slot = jv_return(value); + return input; } -jv jv_object_delete(jv object, jv key) { - assert(JVP_HAS_KIND(object, JV_KIND_OBJECT)); +jv jv_object_delete(jv input, jv key) { + assert(JVP_HAS_KIND(input, JV_KIND_OBJECT)); assert(JVP_HAS_KIND(key, JV_KIND_STRING)); - jvp_object_delete(&object, key); + input = jv_return(input); + jvp_object_delete(&input, key); jv_free(key); - return object; + return input; } int jv_object_length(jv object) { @@ -1862,14 +1867,14 @@ jv jv_object_iter_value(jv object, int iter) { /* * Memory management */ -jv jv_copy(jv j) { - if (JVP_IS_ALLOCATED(j)) { +jv jv__copy(jv j) { + if (JVP_IS_ALLOCATED(j) && !j.borrowed) { jvp_refcnt_inc(j.u.ptr); } return j; } -void jv_free(jv j) { +void jv__free(jv j) { switch(JVP_KIND(j)) { case JV_KIND_ARRAY: jvp_array_free(j); diff --git a/src/jv.h b/src/jv.h index 083509ec26..c0635936ca 100644 --- a/src/jv.h +++ b/src/jv.h @@ -33,7 +33,8 @@ struct jv_refcnt; Really. Do not play with them. */ typedef struct { unsigned char kind_flags; - unsigned char pad_; + unsigned char pad_:7; + unsigned char borrowed:1; unsigned short offset; /* array offsets */ int size; union { @@ -51,8 +52,15 @@ 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 jv_copy(jv); -void jv_free(jv); +void jv__free(jv); +jv jv__copy(jv); + +static inline jv jv_copy(jv a){if(a.borrowed) return a; return jv__copy(a);} +static inline void jv_free(jv a){if(!a.borrowed) jv__free(a);} + +static inline jv jv_borrow(jv a){a.borrowed = 1; return a;} +static inline jv jv_unborrow(jv a){a.borrowed = 0; return jv_copy(a);} +static inline jv jv_return(jv a){if(a.borrowed) return jv_unborrow(a); return a;} int jv_get_refcnt(jv); @@ -70,6 +78,8 @@ jv jv_true(void); jv jv_false(void); jv jv_bool(int); +static inline int jv_is_borrowed(jv a){return a.borrowed;} + jv jv_number(double); jv jv_number_with_literal(const char*); double jv_number_value(jv); diff --git a/src/jv_aux.c b/src/jv_aux.c index 6004799c6a..b5572067d8 100644 --- a/src/jv_aux.c +++ b/src/jv_aux.c @@ -151,6 +151,7 @@ jv jv_get(jv t, jv k) { jv_free(t); jv_free(k); } + return v; } @@ -158,7 +159,7 @@ jv jv_set(jv t, jv k, jv v) { if (!jv_is_valid(v)) { jv_free(t); jv_free(k); - return v; + return jv_return(v); } int isnull = jv_get_kind(t) == JV_KIND_NULL; if (jv_get_kind(k) == JV_KIND_STRING && @@ -385,12 +386,12 @@ jv jv_setpath(jv root, jv path, jv value) { if (!jv_is_valid(root)){ jv_free(value); jv_free(path); - return root; + return jv_return(root); } if (jv_array_length(jv_copy(path)) == 0) { jv_free(path); jv_free(root); - return value; + return jv_return(value); } jv pathcurr = jv_array_get(jv_copy(path), 0); jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path))); @@ -435,11 +436,11 @@ jv jv_getpath(jv root, jv path) { } if (!jv_is_valid(root)) { jv_free(path); - return root; + return jv_return(root); } if (jv_array_length(jv_copy(path)) == 0) { jv_free(path); - return root; + return jv_return(root); } jv pathcurr = jv_array_get(jv_copy(path), 0); jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path))); @@ -491,7 +492,7 @@ static jv delpaths_sorted(jv object, jv paths, int start) { object = jv_dels(object, delkeys); else jv_free(delkeys); - return object; + return jv_return(object); } jv jv_delpaths(jv object, jv paths) { @@ -515,7 +516,7 @@ jv jv_delpaths(jv object, jv paths) { if (jv_array_length(jv_copy(paths)) == 0) { // nothing is being deleted jv_free(paths); - return object; + return jv_return(object); } if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) { // everything is being deleted