mirror of
https://github.com/DaveGamble/cJSON.git
synced 2023-08-10 21:13:26 +03:00
Return output length from cJSON_PrintPreallocated
Adjust cJSON_PrintPreallocated() to return a size_t that provides the length of the output string. On error, it will return 0; so legacy behavior of interpreting its return as a boolean will still work as expected. In addition, a NULL buffer and zero length can be passed in to get the length of what would be the resulting string, so the needed buffer size is this plus 5 (extra that cJSON needs for printing).
This commit is contained in:
parent
203a0dec6f
commit
ed411f00e0
@ -300,7 +300,7 @@ It will allocate a string and print a JSON representation of the tree into it. O
|
||||
|
||||
If you have a rough idea of how big your resulting string will be, you can use `cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)`. `fmt` is a boolean to turn formatting with whitespace on and off. `prebuffer` specifies the first buffer size to use for printing. `cJSON_Print` currently uses 256 bytes for its first buffer size. Once printing runs out of space, a new buffer is allocated and the old gets copied over before printing is continued.
|
||||
|
||||
These dynamic buffer allocations can be completely avoided by using `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`. It takes a buffer to a pointer to print to and its length. If the length is reached, printing will fail and it returns `0`. In case of success, `1` is returned. Note that you should provide 5 bytes more than is actually needed, because cJSON is not 100% accurate in estimating if the provided memory is enough.
|
||||
These dynamic buffer allocations can be completely avoided by using `cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)`. It takes a pointer to a buffer to which to print and the length of that buffer. If the length is reached, printing will fail and it returns `0`. In case of success, the length of the resulting string is returned. Note that you should provide 5 bytes more than is actually needed, because cJSON is not 100% accurate in estimating if the provided memory is enough. In addition, a NULL buffer pointer and length of zero can be provided to calculate and return the length of the resulting string without actually outputing to a buffer.
|
||||
|
||||
### Example
|
||||
|
||||
|
303
cJSON.c
303
cJSON.c
@ -521,19 +521,6 @@ static unsigned char* ensure(printbuffer * const p, size_t needed)
|
||||
return newbuffer + p->offset;
|
||||
}
|
||||
|
||||
/* calculate the new length of the string in a printbuffer and update the offset */
|
||||
static void update_offset(printbuffer * const buffer)
|
||||
{
|
||||
const unsigned char *buffer_pointer = NULL;
|
||||
if ((buffer == NULL) || (buffer->buffer == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
buffer_pointer = buffer->buffer + buffer->offset;
|
||||
|
||||
buffer->offset += strlen((const char*)buffer_pointer);
|
||||
}
|
||||
|
||||
/* securely comparison of floating-point variables */
|
||||
static cJSON_bool compare_double(double a, double b)
|
||||
{
|
||||
@ -581,26 +568,29 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
|
||||
return false;
|
||||
}
|
||||
|
||||
/* reserve appropriate space in the output */
|
||||
output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* copy the printed number to the output and replace locale
|
||||
* dependent decimal point with '.' */
|
||||
for (i = 0; i < ((size_t)length); i++)
|
||||
{
|
||||
if (number_buffer[i] == decimal_point)
|
||||
/* reserve appropriate space in the output */
|
||||
output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
output_pointer[i] = '.';
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
|
||||
output_pointer[i] = number_buffer[i];
|
||||
/* copy the printed number to the output and replace locale
|
||||
* dependent decimal point with '.' */
|
||||
for (i = 0; i < ((size_t)length); i++)
|
||||
{
|
||||
if (number_buffer[i] == decimal_point)
|
||||
{
|
||||
output_pointer[i] = '.';
|
||||
continue;
|
||||
}
|
||||
|
||||
output_pointer[i] = number_buffer[i];
|
||||
}
|
||||
output_pointer[i] = '\0';
|
||||
}
|
||||
output_pointer[i] = '\0';
|
||||
|
||||
output_buffer->offset += (size_t)length;
|
||||
|
||||
@ -912,12 +902,16 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
|
||||
/* empty string */
|
||||
if (input == NULL)
|
||||
{
|
||||
output = ensure(output_buffer, sizeof("\"\""));
|
||||
if (output == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output = ensure(output_buffer, sizeof("\"\""));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "\"\"");
|
||||
}
|
||||
strcpy((char*)output, "\"\"");
|
||||
output_buffer->offset += strlen("\"\"");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -948,6 +942,13 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
|
||||
}
|
||||
output_length = (size_t)(input_pointer - input) + escape_characters;
|
||||
|
||||
/* if just counting length, then do not need to actually copy string */
|
||||
if (!output_buffer->buffer && !output_buffer->length)
|
||||
{
|
||||
output_buffer->offset += output_length + strlen("\"\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
output = ensure(output_buffer, output_length + sizeof("\"\""));
|
||||
if (output == NULL)
|
||||
{
|
||||
@ -961,6 +962,7 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
|
||||
memcpy(output + 1, input, output_length);
|
||||
output[output_length + 1] = '\"';
|
||||
output[output_length + 2] = '\0';
|
||||
output_buffer->offset += output_length + strlen("\"\"");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1012,6 +1014,7 @@ static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffe
|
||||
}
|
||||
output[output_length + 1] = '\"';
|
||||
output[output_length + 2] = '\0';
|
||||
output_buffer->offset += output_length + strlen("\"\"");
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1203,7 +1206,6 @@ static unsigned char *print(const cJSON * const item, cJSON_bool format, const i
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
update_offset(buffer);
|
||||
|
||||
/* check if reallocate is available */
|
||||
if (hooks->reallocate != NULL)
|
||||
@ -1285,13 +1287,13 @@ CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON
|
||||
return (char*)p.buffer;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
|
||||
CJSON_PUBLIC(size_t) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
|
||||
{
|
||||
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
|
||||
|
||||
if ((length < 0) || (buffer == NULL))
|
||||
if (length < 0)
|
||||
{
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p.buffer = (unsigned char*)buffer;
|
||||
@ -1301,7 +1303,12 @@ CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, cons
|
||||
p.format = format;
|
||||
p.hooks = global_hooks;
|
||||
|
||||
return print_value(item, &p);
|
||||
if (!print_value(item, &p))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return p.offset;
|
||||
}
|
||||
|
||||
/* Parser core - when encountering text, process appropriately. */
|
||||
@ -1363,6 +1370,7 @@ static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buf
|
||||
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
|
||||
{
|
||||
unsigned char *output = NULL;
|
||||
size_t length;
|
||||
|
||||
if ((item == NULL) || (output_buffer == NULL))
|
||||
{
|
||||
@ -1372,52 +1380,68 @@ static cJSON_bool print_value(const cJSON * const item, printbuffer * const outp
|
||||
switch ((item->type) & 0xFF)
|
||||
{
|
||||
case cJSON_NULL:
|
||||
output = ensure(output_buffer, 5);
|
||||
if (output == NULL)
|
||||
length = strlen("null");
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output = ensure(output_buffer, length + sizeof(""));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "null");
|
||||
}
|
||||
strcpy((char*)output, "null");
|
||||
output_buffer->offset += length;
|
||||
return true;
|
||||
|
||||
case cJSON_False:
|
||||
output = ensure(output_buffer, 6);
|
||||
if (output == NULL)
|
||||
length = strlen("false");
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output = ensure(output_buffer, length + sizeof(""));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "false");
|
||||
}
|
||||
strcpy((char*)output, "false");
|
||||
output_buffer->offset += length;
|
||||
return true;
|
||||
|
||||
case cJSON_True:
|
||||
output = ensure(output_buffer, 5);
|
||||
if (output == NULL)
|
||||
length = strlen("true");
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output = ensure(output_buffer, length + sizeof(""));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
strcpy((char*)output, "true");
|
||||
}
|
||||
strcpy((char*)output, "true");
|
||||
output_buffer->offset += length;
|
||||
return true;
|
||||
|
||||
case cJSON_Number:
|
||||
return print_number(item, output_buffer);
|
||||
|
||||
case cJSON_Raw:
|
||||
{
|
||||
size_t raw_length = 0;
|
||||
if (item->valuestring == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
raw_length = strlen(item->valuestring) + sizeof("");
|
||||
output = ensure(output_buffer, raw_length);
|
||||
if (output == NULL)
|
||||
length = strlen(item->valuestring);
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output = ensure(output_buffer, length + sizeof(""));
|
||||
if (output == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
memcpy(output, item->valuestring, length + sizeof(""));
|
||||
}
|
||||
memcpy(output, item->valuestring, raw_length);
|
||||
output_buffer->offset += length;
|
||||
return true;
|
||||
}
|
||||
|
||||
case cJSON_String:
|
||||
return print_string(item, output_buffer);
|
||||
@ -1545,13 +1569,16 @@ static cJSON_bool print_array(const cJSON * const item, printbuffer * const outp
|
||||
|
||||
/* Compose the output array. */
|
||||
/* opening square bracket */
|
||||
output_pointer = ensure(output_buffer, 1);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
output_pointer = ensure(output_buffer, 1);
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*output_pointer = '[';
|
||||
*output_pointer = '[';
|
||||
}
|
||||
output_buffer->offset++;
|
||||
output_buffer->depth++;
|
||||
|
||||
@ -1561,33 +1588,40 @@ static cJSON_bool print_array(const cJSON * const item, printbuffer * const outp
|
||||
{
|
||||
return false;
|
||||
}
|
||||
update_offset(output_buffer);
|
||||
|
||||
if (current_element->next)
|
||||
{
|
||||
length = (size_t) (output_buffer->format ? 2 : 1);
|
||||
output_pointer = ensure(output_buffer, length + 1);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output_pointer = ensure(output_buffer, length + sizeof(""));
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*output_pointer++ = ',';
|
||||
if(output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = ' ';
|
||||
}
|
||||
*output_pointer = '\0';
|
||||
}
|
||||
*output_pointer++ = ',';
|
||||
if(output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = ' ';
|
||||
}
|
||||
*output_pointer = '\0';
|
||||
output_buffer->offset += length;
|
||||
}
|
||||
current_element = current_element->next;
|
||||
}
|
||||
|
||||
output_pointer = ensure(output_buffer, 2);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
output_pointer = ensure(output_buffer, 2);
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*output_pointer++ = ']';
|
||||
*output_pointer = '\0';
|
||||
}
|
||||
*output_pointer++ = ']';
|
||||
*output_pointer = '\0';
|
||||
output_buffer->offset++;
|
||||
output_buffer->depth--;
|
||||
|
||||
return true;
|
||||
@ -1720,33 +1754,39 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
|
||||
|
||||
/* Compose the output: */
|
||||
length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
|
||||
output_pointer = ensure(output_buffer, length + 1);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
output_pointer = ensure(output_buffer, length + 1);
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*output_pointer++ = '{';
|
||||
output_buffer->depth++;
|
||||
if (output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = '\n';
|
||||
*output_pointer++ = '{';
|
||||
if (output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = '\n';
|
||||
}
|
||||
}
|
||||
output_buffer->offset += length;
|
||||
output_buffer->depth++;
|
||||
|
||||
while (current_item)
|
||||
{
|
||||
if (output_buffer->format)
|
||||
{
|
||||
size_t i;
|
||||
output_pointer = ensure(output_buffer, output_buffer->depth);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < output_buffer->depth; i++)
|
||||
{
|
||||
*output_pointer++ = '\t';
|
||||
size_t i;
|
||||
output_pointer = ensure(output_buffer, output_buffer->depth);
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < output_buffer->depth; i++)
|
||||
{
|
||||
*output_pointer++ = '\t';
|
||||
}
|
||||
}
|
||||
output_buffer->offset += output_buffer->depth;
|
||||
}
|
||||
@ -1756,18 +1796,20 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
|
||||
{
|
||||
return false;
|
||||
}
|
||||
update_offset(output_buffer);
|
||||
|
||||
length = (size_t) (output_buffer->format ? 2 : 1);
|
||||
output_pointer = ensure(output_buffer, length);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*output_pointer++ = ':';
|
||||
if (output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = '\t';
|
||||
output_pointer = ensure(output_buffer, length);
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*output_pointer++ = ':';
|
||||
if (output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = '\t';
|
||||
}
|
||||
}
|
||||
output_buffer->offset += length;
|
||||
|
||||
@ -1776,45 +1818,52 @@ static cJSON_bool print_object(const cJSON * const item, printbuffer * const out
|
||||
{
|
||||
return false;
|
||||
}
|
||||
update_offset(output_buffer);
|
||||
|
||||
/* print comma if not last */
|
||||
length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
|
||||
output_pointer = ensure(output_buffer, length + 1);
|
||||
if (output_pointer == NULL)
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (current_item->next)
|
||||
{
|
||||
*output_pointer++ = ',';
|
||||
}
|
||||
output_pointer = ensure(output_buffer, length + 1);
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (current_item->next)
|
||||
{
|
||||
*output_pointer++ = ',';
|
||||
}
|
||||
|
||||
if (output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = '\n';
|
||||
if (output_buffer->format)
|
||||
{
|
||||
*output_pointer++ = '\n';
|
||||
}
|
||||
*output_pointer = '\0';
|
||||
}
|
||||
*output_pointer = '\0';
|
||||
output_buffer->offset += length;
|
||||
|
||||
current_item = current_item->next;
|
||||
}
|
||||
|
||||
output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
|
||||
if (output_pointer == NULL)
|
||||
length = output_buffer->format ? output_buffer->depth : 1;
|
||||
if (output_buffer->buffer || output_buffer->length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (output_buffer->format)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < (output_buffer->depth - 1); i++)
|
||||
output_pointer = ensure(output_buffer, length + sizeof(""));
|
||||
if (output_pointer == NULL)
|
||||
{
|
||||
*output_pointer++ = '\t';
|
||||
return false;
|
||||
}
|
||||
if (output_buffer->format)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < (output_buffer->depth - 1); i++)
|
||||
{
|
||||
*output_pointer++ = '\t';
|
||||
}
|
||||
}
|
||||
*output_pointer++ = '}';
|
||||
*output_pointer = '\0';
|
||||
}
|
||||
*output_pointer++ = '}';
|
||||
*output_pointer = '\0';
|
||||
output_buffer->offset += length;
|
||||
output_buffer->depth--;
|
||||
|
||||
return true;
|
||||
|
5
cJSON.h
5
cJSON.h
@ -158,9 +158,10 @@ CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns length of resulting string on success and 0 on failure. */
|
||||
/* A NULL buffer and zero length can also be pass in to this function to calculate the resulting string length (excluding null terminator), so the needed buffer size is this plus 5 (see next line). */
|
||||
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
CJSON_PUBLIC(size_t) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||
/* Delete a cJSON entity and all subentities. */
|
||||
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||
|
||||
|
15
test.c
15
test.c
@ -48,14 +48,15 @@ static int print_preallocated(cJSON *root)
|
||||
char *buf_fail = NULL;
|
||||
size_t len = 0;
|
||||
size_t len_fail = 0;
|
||||
size_t len_ret = 0;
|
||||
|
||||
/* formatted print */
|
||||
out = cJSON_Print(root);
|
||||
|
||||
/* create buffer to succeed */
|
||||
/* the extra 5 bytes are because of inaccuracies when reserving memory */
|
||||
len = strlen(out) + 5;
|
||||
buf = (char*)malloc(len);
|
||||
len = strlen(out);
|
||||
buf = (char*)malloc(len + 5);
|
||||
if (buf == NULL)
|
||||
{
|
||||
printf("Failed to allocate memory.\n");
|
||||
@ -71,8 +72,16 @@ static int print_preallocated(cJSON *root)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
len_ret = cJSON_PrintPreallocated(root, NULL, 0, 1);
|
||||
if (len_ret != len) {
|
||||
printf("cJSON_PrintPreallocated length calculation failed!\n");
|
||||
printf("cJSON_Print result length:%d\n", (int)(len));
|
||||
printf("cJSON_PrintPreallocated length:%d\n", (int)(len_ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Print to buffer */
|
||||
if (!cJSON_PrintPreallocated(root, buf, (int)len, 1)) {
|
||||
if (!cJSON_PrintPreallocated(root, buf, (int)(len + 5), 1)) {
|
||||
printf("cJSON_PrintPreallocated failed!\n");
|
||||
if (strcmp(out, buf) != 0) {
|
||||
printf("cJSON_PrintPreallocated not the same as cJSON_Print!\n");
|
||||
|
Loading…
Reference in New Issue
Block a user