-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Added a function to print to pre-allocated buffer #72
Conversation
p.buffer = buf; | ||
p.length = len; | ||
p.offset = 0; | ||
out = print_value(item,0,fmt,&p); |
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.
By doing this you risk crashes, because print_value
will try to reallocate the printbuffer when it is too short.
I understand why a function like that can be useful, but the implementation should be more carefully considered. The name should be more like If you don't do multithreading, there is a (quite hacky, I admit that) workaround to do what you want on the current version of cJSON btw. /* consider this as pseudo code because I haven't actually compiled and tested it */
static int run_already = 0;
static char *buffer = NULL;
static size_t buffer_length = 0;
void *allocator(size_t size)
{
if (run_already || (size > buffer_length))
{
return NULL;
}
run_already = 1;
return buffer;
}
void deallocator(void *buffer)
{
return;
}
cJSON_Hooks static_allocators = { allocator, deallocator };
int main(void)
{
/*... */
char local_buffer[100];
buffer = local_buffer;
buffer_length = 100;
run_already = 0;
cJSON_InitHooks(&static_allocators);
cJSON_PrintBuffered(item, 100, 0);
buffer = NULL;
buffer_length = 0;
run_already = 0;
/* ... */
return 0;
} Yes, it's really ugly ... If one would actually implement this functionality in cJSON, it would probably be best to add a flag to We can have a discussion at least! |
Seems like quite a bit of of user code to perform what seems like a simple idea. I really like idea of adding a flag to the I found a bug: If the supplied buffer was too small by 1 or 2 bytes, the last closing curly bracket is omitted and no error is detected. I made a change to the source in the last commit which adds a check for a NULL pointer returned from |
@@ -244,8 +244,10 @@ typedef struct | |||
char *buffer; | |||
int length; | |||
int offset; | |||
int noalloc; |
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 use cjbool
and use true
and false
when assigning to it.
} printbuffer; | ||
|
||
|
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.
Don't add random whitespace.
p.offset = 0; | ||
p.noalloc = 1; | ||
out = print_value(item,0,fmt,&p); | ||
return (out != buf ? -1 : 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.
This should return out
like the other print functions. It will be NULL
if it failed.
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 this function returns a char pointer (effectively providing an alias to the input char *buf
or a NULL pointer), it might falsely imply a new dynamically allocated string was created. Do you think it might be more intuitive to use the pointer to char* buf
after the function is called if no error was returned, or is consistency of return values with all the cJSON_Print
functions more important?
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.
Hm. Good point. Considering this, your way of returning true or false might actually be better. An API should be hard to misuse in a dangerous way (like having a double free because of the alias).
The question is how far one is willing to protect the users from themselves.
This will then not allow the use of cJSON_PrintPreallocated
as an argument to another function (do_something(cJSON_PrintPreallocated(...))
)
If you do it the way you suggested, you can just return print_value(...) != 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.
That would be more concise. Made the change.
if (!ptr) | ||
{ | ||
return 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.
Change this to:
if (!print_value(child, depth + 1, fmt, p))
{
return NULL;
}
Thank you for finding this problem btw. after that I was taking a closer look to the rest of the library and there are quite some cases where out of memory errors are not handled properly.
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 will also fix these other cases in the next release.
|
||
return print_value(item, 0, fmt, &p); | ||
} | ||
|
||
int cJSON_PrintPreallocated(cJSON *item,char *buf, const size_t len, const cjbool fmt) |
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.
About the size_t
. I agree that size_t
should be used everywhere instead of int
, and it will be in v2, but currently ensure
and printbuffer
and all the other internal functions use int
for sizes, so using size_t
can create the illusion that numbers exceeding the integer range are supported, but they're not.
So please change to int
😢.
|
||
printf("Test cJSON_PrintPreallocated:\n"); | ||
/* create buffer */ | ||
size_t len = strlen(out) + 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.
It is not allowed to mix code and declarations in C89. I have a bug in the Makefile and CMakelists.txt so that the compiler option is not enforced, sorry for that.
I suggest you create a new function just for testing preallocated printing and call it from main (you can pass one of the texts to it).
Thanks for the thorough feedback, I'll make the requested changes soon :) |
Okay made the changes. Let me know what you think. |
@@ -23,7 +23,7 @@ INSTALL_LIBRARY_PATH = $(DESTDIR)$(PREFIX)/$(LIBRARY_PATH) | |||
|
|||
INSTALL ?= cp -a | |||
|
|||
R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings $(CFLAGS) | |||
R_CFLAGS = -fPIC -std=c89 -pedantic -Wall -Werror -Wstrict-prototypes -Wwrite-strings -Wshadow -Winit-self -Wcast-align -Wformat=2 -Wmissing-prototypes $(CFLAGS) | |||
|
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 don't try to readd changes that I merged to the master branch already. I will handle merge conflicts myself.
p.length = len; | ||
p.offset = 0; | ||
p.noalloc = true; | ||
return (print_value(item,0,fmt,&p) != NULL ? 0 : -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.
I meant it exactly the other way around. Think of the return value as a bool. So return print_value(...) != NULL
. It returns 1 if it succeeds and 0 if it doesn't.
I think I will manually merge in your changes. |
I merged your changes, see bf17703 |
I added a function called
cJSON_PrintMallocedBuffer
to print output to a buffer instead of allocating memory internally. I'm made it so there aren't any calls to malloc when printing out a cJSON item.I also fixed the Makefile to have the appropriate dependencies and only source files (no headers) being compiled for the tests. Otherwise, clang on Mac (and perhaps elsewhere) complains.