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
json_object: implement json_object_deep_copy() function #328
Conversation
I thought a bit about the I don't feel this should be a problem in the whole JSON context. But, if there's another opinion here, I'm open for discussion. |
tests/test_null.c
Outdated
@@ -31,7 +30,7 @@ int main(void) | |||
printf("parsed string: "); | |||
puts(json); | |||
puts("FAIL"); | |||
retval=1; | |||
return 1; |
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 are you bailing early? I expect that this will cause memory leak errors.
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 idea here was that if the test fails here, stop it here, so that we don't generate more output
but, since the output is compared against an expected output the test should fail;
will fix
tests/test_null.c
Outdated
printf("%s\n", json_object_get_string(jso)); | ||
|
||
json_object_put(null_object); | ||
puts("PASS"); |
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.
This should be left out, since whether it actually passed depends on whether all of the output matches what's in the .expected file.
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.
Or rather, we should adjust the "PASS" lines here to more accurately indicate that it's just the individual sections of the test that passed.
json_object.c
Outdated
{ | ||
int rc; | ||
/* Nothing to do, so return succes */ | ||
if (!src && !dst) |
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.
IMO !dst should always be an error. If you're not providing some place to put the copied object you're doing something wrong.
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.
ack
json_object.c
Outdated
} | ||
|
||
/* Free previous destination object */ | ||
json_object_put(*dst); |
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.
skip this if *dst == NULL
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.
json_object_put()
handles null;
adding a null-check here may yield a small performance improvement, in the sense that a jump to the json_object_put()
block, won't happen if that is null
most modern compilers would probably optimize this call
not sure about older compilers & deployment [of json-c on them]
hmm ; i guess i'm neutral about adding or not-adding it;
instead of over-thinking it, i'll just add it
json_object.c
Outdated
return 0; | ||
|
||
/* Check if arguments are sane ; refcount must not be higher than 1 */ | ||
if (!src || !dst || (*dst && (*dst)->_ref_count > 1)) { |
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 we're going to allow *dst to be set at all, then it seems unnecessarily limiting to have the ref count restriction here. I'd say just json_object_put it if there's something there, do a *dst = NULL, and continue on.
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.
but, if refcount > 1
, then it implies that the object may be shared by some other entity;
overwriting it in that case may cause weird/unexpected behavior for any other entity holding a refcount to this object
we can drop the refcount check [if you really insist] ;
i'd feel a bit uncomfortable about it, though, but i could live with it
Having 'null' in the JSON that you parse turn into something other than a C NULL is a fairly significant API change. If we were to really switch to having 'null' values show up as json_type_null C objects then we need to go whole-hog and start treating NULL as an invalid value in many places (such as json_object_object_add), and have a major library version bump. |
json_object.h
Outdated
* If the destination object is non-NULL, it will be overwrriten | ||
* completely, if the refcount is 1. | ||
* | ||
* Note: the same result could be achieved by using |
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.
This could be reworded a bit. Perhaps just:
This does roughly the same thing as
json_tokener_parse(json_object_get_string(src))
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.
ack
json_object.h
Outdated
/** | ||
* Copy the contents of the JSON object. | ||
* If the destination object is NULL, it will be allocated. | ||
* If the destination object is non-NULL, it will be overwrriten |
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.
How about:
If *dst is non-NULL, it will be passed to json_object_put (which may free it if the refcount drops to 0)
and a completely new object will be created and assigned to *dst.
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.
before addressing this, let's resolve this comment first :)
9f420cd#r126346219
/* errno should be set by the function that faulted */ | ||
if (!*dst) | ||
return -1; | ||
|
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.
This needs to copy over at least the serializer and data used by json_object_new_double_s(), and ideally any user-specified ones. However, for user-specified ones we have the problem that we can free userdata, but have no way to copy it. hmm...
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.
ack
i had no idea about that serializer data;
i think i could implement a deep-copy that may work without this null JSON object change ; the null object change, is mostly a personal ambition that i tried to do ; |
Still need to resolve the discussion [and related comment] if |
json_object.c
Outdated
if (src->o_type != json_type_double) | ||
return 0; | ||
/* Nothing to do */ | ||
if (!src->_userdata) |
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.
_userdata doesn't actually need to be non-NULL. You can set a serializer that doesn't need userdata.
json_object.c
Outdated
/* Nothing to do */ | ||
if (!src->_userdata) | ||
return 0; | ||
if (!src->_user_delete) { |
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.
Please drop this check too. As with _userdata, _user_delete is optional.
json_object.c
Outdated
static int json_object_copy_serializer_data(struct json_object *src, struct json_object *dst) | ||
{ | ||
/* `src` and `dst` were validated by now ==> no null check for them */ | ||
if (src->o_type != json_type_double) |
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.
Although we currently only use json_object_userdata_to_json_string for double's, there's no particular reason it couldn't be used for other types as well, and if it's in place we really don't care what type the object is, so please drop this check.
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.
so, regarding other/external serializers, maybe a _user_copy
function would be needed
json_object.c
Outdated
json_object_object_foreachC(src, iter) { | ||
/* This handles the `json_type_null` case */ | ||
if (!iter.val) { | ||
json_object_object_add(*dst, iter.key, NULL); |
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.
How about have this line be "jso = NULL;", drop the continue, and change 1331 to "else if ...". That way we have a single call to json_object_object_add, and the return value is checked.
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.
ack
json_object.c
Outdated
json_object_object_add(*dst, iter.key, NULL); | ||
continue; | ||
} | ||
if ((rc = json_object_deep_copy_recursive(iter.val, &jso)) < 0) |
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.
Did you intend to do something with rc before returning? If not, don't bother using it. It looks like the only place it's actually used at all is right at the end of this function, and even there it's not really needed.
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.
ack;
not sure anymore ;
the rc
is mostly out of habit, when I try to use more goto
statements ; which is not the case
will remove rc
altogether
json_object.c
Outdated
struct json_object *jso1 = json_object_array_get_idx(src, i); | ||
/* This handles the `json_type_null` case */ | ||
if (!jso1) { | ||
json_object_array_add(*dst, NULL); |
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.
Same comment here as for the json_type_object case.
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.
ack
tests/test_deep_copy.test
Outdated
@@ -0,0 +1,12 @@ | |||
#!/bin/sh |
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.
Please change this to be a symlink to the newly added test_basic.test
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.
ack ;
missed it through all the rebasing
Did you respond about the (*dst)->_refcount > 1 check somewhere? Github isn't showing that to me (nor my original comment :( ) |
It was this comment series:
Github is kinda crappy after a long review ; Bitbucket is s bit better, but not by much.
Regarding For the
So, in this case you can rewrite the But I can understand [what you're saying] that, as a standalone function, |
what if we change
to
? |
re-spinned with review comments addressed |
Because doing `json_tokener_parse(json_object_get_string(src))` feels sloppy, dirty, and makes me want to cry at night sometimes. Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Seems to perform better than outputting to string and re-parsing it. BENCHMARK - 1000000 iterations of 'dst2 = json_tokener_parse(json_object_get_string(src2))' took 20 seconds BENCHMARK - 1000000 iterations of 'dst2 = json_tokener_parse(json_object_get_string(src2))' took 7 seconds It should make a difference on embedded systems. The test was performed on a i5 desktop CPU [~3.5 years of age]. Signed-off-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Seems to perform better than outputting to string and re-parsing it.
This function will be needed for JSON patch [RFC 6902] ; before applying a patch, a backup of the original object will be made ; in case any patch operation fails, the JSON object should not be modified at all.
This is covered in section
5. Error Handling
This PR also adds the
json_object_new_null()
function (which is requested here #226 ).While copying the JSON object, it's easier to have an object-ual representation for the JSON
null
.Signed-off-by: Alexandru Ardelean ardeleanalex@gmail.com