-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds jv_unshare(), jv_is_unshared() #3109
base: master
Are you sure you want to change the base?
Conversation
src/jv.c
Outdated
@@ -1862,6 +1862,63 @@ jv jv_object_iter_value(jv object, int iter) { | |||
/* | |||
* Memory management | |||
*/ | |||
jv jv_new(jv input){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the point of the function is to make a deep copy that don't share any memory with the input i think it probably should have a more clear name (jv_copy_deep
etc?) and also a comment clarifying it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@emanuele6 proposed jv_clone()
. I am quite open to different names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm seem to be how rust uses copy/clone, but i'm always confusing them :) a very clear name could be something like jv_unshare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I think proposed deepclone
like in Java if I proposed anything at all; jv_clone
would also be confusing because in Java clone only copies one level
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then I guess jv_unshare()
would be the best option. Then again apparently words don't have meaning in this industry so who knows if there is some conflict there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wader I've never heard unshare
used for deepclone, but I think it's a very good name for it :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:) made it up right now but mostly based on @MaxBrandtner use case for it. I think something deep copy or unshare works. but maybe deep copy is a more well known concept?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really quite like that idea. I shall implement it right away
Is there a way to make |
src/jv.c
Outdated
output_object,key, | ||
jv_unshare( | ||
jv_getpath(jv_copy(output_object), jv_copy(key)) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how much faster using jv_array_set
/jv_object_set
would be, could skip building a path array at least?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the main reason I did it the way I did it was to save on code, but yes doing it specific for arrays and objects would probably be faster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, i would probably try benchmark some (hopefully) real world like use cases if performance is an issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then it would probably be better to separate between JV_KIND_OBJECT AND JV_KIND_ARRAY
src/jv.c
Outdated
if(jv_get_kind(input) == JV_KIND_OBJECT){ | ||
output_object = jv_object(); | ||
}else{ | ||
output_object = jv_array(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use jv_array_sized
as we know size?
@@ -1862,6 +1862,68 @@ jv jv_object_iter_value(jv object, int iter) { | |||
/* | |||
* Memory management | |||
*/ | |||
jv jv_unshare(jv input){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how ugly but i guess one could save on jv_free
calls by having different ownership rule for unshare? too big foot gun?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
even within the implementation of jv_unshare()
we benefit from it dereferencing its input. We could have jv_unshare()
not consume its memory, but at the cost of having to write a lot of stuff to buffers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is jv_unshare()
supposed to consume a reference to its argument? I think it should.
As it pertains to unit-tests. Especially with |
the checks to test whether the various subsections are not identical fail. Does |
src/jv_aux.c
Outdated
@@ -427,6 +427,70 @@ jv jv_setpath(jv root, jv path, jv value) { | |||
return jv_set(root, pathcurr, jv_setpath(subroot, pathrest, value)); | |||
} | |||
|
|||
jv jv_addpath(jv root, jv path, jv add){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a comment on what this is supposed to do? Is it different from jv_setpath()
?
src/jv_aux.c
Outdated
@@ -538,6 +602,47 @@ static int string_cmp(const void* pa, const void* pb){ | |||
return r; | |||
} | |||
|
|||
jv jv_paths(jv input){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a comment on what this is supposed to do? Is it output an array of paths to scalar values?
Can you update the PR's title to mention Please update the description to explain what these functions do (or comment in the sources) and the motivation? I'm quite happy to have |
My fault, but i did suggested |
|
No, I rather like |
src/jq_test.c
Outdated
|
||
assert(jv_equal(jv_copy(initial), jv_copy(new))); | ||
assert(!jv_identical(jv_copy(initial), jv_copy(new))); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be good to have a jv_unshared()
function that returns true if the given value is recursively unshared in that every value down from it has jv_get_refcount()
of 1
. Such a jv_unshared()
function would have to not consume a reference to its argument, naturally, or maybe it should check that the top-level value has 2
refcounts and all values below have 1
refcounts.
Then we could use jv_getpath()
on initial
to acquire an extra refcount on a value far from the initial
root, and then we could check that neither initial
nor that value obtained with jv_getpath()
are jv_unshared()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that idea. It would make testing easier as well. (the name might just be a bit too similar so simmering like jv_is_unshared() might be better)
src/jq_test.c
Outdated
|
||
{ | ||
assert(jv_equal( | ||
jv_addpath( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think jv_addpath()
is the same as jv_setpath()
, so let's drop it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no it's not
jv_setpath() replaces the entire path with the value you specify
jv_addpath() overwrites in the path you specify the keys specified in the replace value keeping keys that are not mentioned in the replace value intact
src/jq_test.c
Outdated
|
||
{ | ||
assert(jv_equal( | ||
jv_paths( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we want to use an assert()
with jv_paths()
? Why not just assert that the two values are equal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I'm not saying I don't want jv_paths()
, I'm just saying that I think we don't need jv_paths()
for testing jv_unshared()
, so jv_paths()
-if we need it at all- can be in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just did it out of convenience since I iterate through every path in the unshared value to see whether it really has no shared memory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be faster to just recurse in jv_is_unshared()
.
Thank you, We'll probably not use I think I also think Lastly it'd be great if you could add a |
and yet in the unit-test for jv_unshare() jv_identical() returns true once I start checking recursively and I don't know why |
src/jv_aux.c
Outdated
if(jv_get_kind(root) != JV_KIND_OBJECT && jv_get_kind(root) != JV_KIND_ARRAY){ | ||
jv_free(root); | ||
|
||
if(!jv_equal(jv_copy(path), jv_array())){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's much faster to check if path
has length 0.
src/jv_aux.c
Outdated
return add; | ||
} | ||
|
||
if(!jv_equal(jv_copy(path), jv_array())){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
src/jv_aux.c
Outdated
|
||
jv_free(path); | ||
|
||
return add; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get this case.
Also, what if root
is a jv_null()
? Should we treat it as being a container-like value, like jq
does?
src/jv_aux.c
Outdated
} | ||
|
||
if(!jv_equal(jv_copy(path), jv_array())){ | ||
return jv_setpath(root, path, jv_addpath(jv_getpath(jv_copy(root), jv_copy(path)), jv_array(), add)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the difference between jv_setpath()
and jv_addpath()
now, but I'd still like a comment, and also if we don't need jv_addpath()
then I'd rather not have it.
|
I've reduced the pr to
As it pertains to |
return 1; | ||
} | ||
if(jv_get_kind(a) == JV_KIND_OBJECT){ | ||
jv keys = jv_keys(jv_copy(a)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know much about the jv
internals but i suspect that maybe jv_is_unshared
is suitable to be implemented using a private new jvp_*
helper function that uses some internal details, similar to how jvp_object_equal
etc is implemented? then you can probably save on some allocation and refcounting.
@nicowilliams does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for my inactivity for the past month on the thread. While having a jvp internal representation would get rid of some of the ref-counting it will add more complexity. Then again if you think it's worth I could implement jvp_object_is_unshared()
, jvp_array_is_unshared()
(I would refrain from adding jvp functions for the non-recursive options as I don't think there is much benefit to be had in these senarios)
Or do you think I should add string, number, true, false, null, invalid variants as well?
Still need to add some more tests. I would especially apprecieate some feedback on jv_new(), as this can perhaps be handled more efficiently using jvp