Merge pull request #114 from DaveGamble/simplify-print

Simplify print functions
This commit is contained in:
Max Bruckner
2017-02-20 23:09:06 +01:00
committed by GitHub
2 changed files with 197 additions and 279 deletions

509
cJSON.c
View File

@@ -236,7 +236,7 @@ typedef struct
} printbuffer; } printbuffer;
/* realloc printbuffer if necessary to have at least "needed" bytes more */ /* realloc printbuffer if necessary to have at least "needed" bytes more */
static unsigned char* ensure(printbuffer *p, size_t needed) static unsigned char* ensure(printbuffer * const p, size_t needed)
{ {
unsigned char *newbuffer = NULL; unsigned char *newbuffer = NULL;
size_t newsize = 0; size_t newsize = 0;
@@ -306,76 +306,68 @@ static unsigned char* ensure(printbuffer *p, size_t needed)
return newbuffer + p->offset; return newbuffer + p->offset;
} }
/* calculate the new length of the string in a printbuffer */ /* calculate the new length of the string in a printbuffer and update the offset */
static size_t update(const printbuffer *p) static void update_offset(printbuffer * const buffer)
{ {
const unsigned char *str = NULL; const unsigned char *buffer_pointer = NULL;
if (!p || !p->buffer) if ((buffer == NULL) || (buffer->buffer == NULL))
{ {
return 0; return;
} }
str = p->buffer + p->offset; buffer_pointer = buffer->buffer + buffer->offset;
return p->offset + strlen((const char*)str); buffer->offset += strlen((const char*)buffer_pointer);
} }
/* Render the number nicely from the given item into a string. */ /* Render the number nicely from the given item into a string. */
static unsigned char *print_number(const cJSON *item, printbuffer *p) static unsigned char *print_number(const cJSON * const item, printbuffer * const output_buffer)
{ {
unsigned char *str = NULL; unsigned char *output_pointer = NULL;
double d = item->valuedouble; double d = item->valuedouble;
if (p == NULL) if (output_buffer == NULL)
{ {
return NULL; return NULL;
} }
/* special case for 0. */
if (d == 0)
{
str = ensure(p, 2);
if (str != NULL)
{
strcpy((char*)str,"0");
}
}
/* value is an int */ /* value is an int */
else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN))
{ {
/* 2^64+1 can be represented in 21 chars. */ /* 2^64+1 can be represented in 21 chars. */
str = ensure(p, 21); output_pointer = ensure(output_buffer, 21);
if (str != NULL) if (output_pointer != NULL)
{ {
sprintf((char*)str, "%d", item->valueint); sprintf((char*)output_pointer, "%d", item->valueint);
} }
} }
/* value is a floating point number */ /* value is a floating point number */
else else
{ {
/* This is a nice tradeoff. */ /* This is a nice tradeoff. */
str = ensure(p, 64); output_pointer = ensure(output_buffer, 64);
if (str != NULL) if (output_pointer != NULL)
{ {
/* This checks for NaN and Infinity */ /* This checks for NaN and Infinity */
if ((d * 0) != 0) if ((d * 0) != 0)
{ {
sprintf((char*)str, "null"); sprintf((char*)output_pointer, "null");
} }
else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
{ {
sprintf((char*)str, "%.0f", d); sprintf((char*)output_pointer, "%.0f", d);
} }
else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
{ {
sprintf((char*)str, "%e", d); sprintf((char*)output_pointer, "%e", d);
} }
else else
{ {
sprintf((char*)str, "%f", d); sprintf((char*)output_pointer, "%f", d);
} }
} }
} }
return str;
return output_pointer;
} }
/* parse 4 digit hexadecimal number */ /* parse 4 digit hexadecimal number */
@@ -671,149 +663,130 @@ fail:
} }
/* Render the cstring provided to an escaped version that can be printed. */ /* Render the cstring provided to an escaped version that can be printed. */
static unsigned char *print_string_ptr(const unsigned char *str, printbuffer *p) static unsigned char *print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
{ {
const unsigned char *ptr = NULL; const unsigned char *input_pointer = NULL;
unsigned char *ptr2 = NULL; unsigned char *output = NULL;
unsigned char *out = NULL; unsigned char *output_pointer = NULL;
size_t len = 0; size_t output_length = 0;
cjbool flag = false; /* numbers of additional characters needed for escaping */
unsigned char token = '\0'; size_t escape_characters = 0;
if (p == NULL) if (output_buffer == NULL)
{ {
return NULL; return NULL;
} }
/* empty string */ /* empty string */
if (!str) if (input == NULL)
{ {
out = ensure(p, 3); output = ensure(output_buffer, sizeof("\"\""));
if (out == NULL) if (output == NULL)
{ {
return NULL; return NULL;
} }
strcpy((char*)out, "\"\""); strcpy((char*)output, "\"\"");
return out; return output;
} }
/* set "flag" to 1 if something needs to be escaped */ /* set "flag" to 1 if something needs to be escaped */
for (ptr = str; *ptr; ptr++) for (input_pointer = input; *input_pointer; input_pointer++)
{ {
flag |= (((*ptr > 0) && (*ptr < 32)) /* unprintable characters */ if (strchr("\"\\\b\f\n\r\t", *input_pointer))
|| (*ptr == '\"') /* double quote */ {
|| (*ptr == '\\')) /* backslash */ /* one character escape sequence */
? 1 escape_characters++;
: 0;
} }
else if (*input_pointer < 32)
{
/* UTF-16 escape sequence uXXXX */
escape_characters += 5;
}
}
output_length = (size_t)(input_pointer - input) + escape_characters;
output = ensure(output_buffer, output_length + sizeof("\"\""));
if (output == NULL)
{
return NULL;
}
/* no characters have to be escaped */ /* no characters have to be escaped */
if (!flag) if (escape_characters == 0)
{ {
len = (size_t)(ptr - str); output[0] = '\"';
memcpy(output + 1, input, output_length);
output[output_length + 1] = '\"';
output[output_length + 2] = '\0';
out = ensure(p, len + 3); return output;
if (out == NULL)
{
return NULL;
} }
ptr2 = out; output[0] = '\"';
*ptr2++ = '\"'; output_pointer = output + 1;
strcpy((char*)ptr2, (const char*)str);
ptr2[len] = '\"';
ptr2[len + 1] = '\0';
return out;
}
ptr = str;
/* calculate additional space that is needed for escaping */
while ((token = *ptr))
{
++len;
if (strchr("\"\\\b\f\n\r\t", token))
{
len++; /* +1 for the backslash */
}
else if (token < 32)
{
len += 5; /* +5 for \uXXXX */
}
ptr++;
}
out = ensure(p, len + 3);
if (out == NULL)
{
return NULL;
}
ptr2 = out;
ptr = str;
*ptr2++ = '\"';
/* copy the string */ /* copy the string */
while (*ptr) for (input_pointer = input; *input_pointer != '\0'; input_pointer++, output_pointer++)
{ {
if ((*ptr > 31) && (*ptr != '\"') && (*ptr != '\\')) if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
{ {
/* normal character, copy */ /* normal character, copy */
*ptr2++ = *ptr++; *output_pointer = *input_pointer;
} }
else else
{ {
/* character needs to be escaped */ /* character needs to be escaped */
*ptr2++ = '\\'; *output_pointer++ = '\\';
switch (token = *ptr++) switch (*input_pointer)
{ {
case '\\': case '\\':
*ptr2++ = '\\'; *output_pointer = '\\';
break; break;
case '\"': case '\"':
*ptr2++ = '\"'; *output_pointer = '\"';
break; break;
case '\b': case '\b':
*ptr2++ = 'b'; *output_pointer = 'b';
break; break;
case '\f': case '\f':
*ptr2++ = 'f'; *output_pointer = 'f';
break; break;
case '\n': case '\n':
*ptr2++ = 'n'; *output_pointer = 'n';
break; break;
case '\r': case '\r':
*ptr2++ = 'r'; *output_pointer = 'r';
break; break;
case '\t': case '\t':
*ptr2++ = 't'; *output_pointer = 't';
break; break;
default: default:
/* escape and print as unicode codepoint */ /* escape and print as unicode codepoint */
sprintf((char*)ptr2, "u%04x", token); sprintf((char*)output_pointer, "u%04x", *input_pointer);
ptr2 += 5; output_pointer += 4;
break; break;
} }
} }
} }
*ptr2++ = '\"'; output[output_length + 1] = '\"';
*ptr2++ = '\0'; output[output_length + 2] = '\0';
return out; return output;
} }
/* Invoke print_string_ptr (which is useful) on an item. */ /* Invoke print_string_ptr (which is useful) on an item. */
static unsigned char *print_string(const cJSON *item, printbuffer *p) static unsigned char *print_string(const cJSON * const item, printbuffer * const p)
{ {
return print_string_ptr((unsigned char*)item->valuestring, p); return print_string_ptr((unsigned char*)item->valuestring, p);
} }
/* Predeclare these prototypes. */ /* Predeclare these prototypes. */
static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const ep); static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const ep);
static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p); static unsigned char *print_value(const cJSON * const item, const size_t depth, const cjbool format, printbuffer * const output_buffer);
static const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const ep); static const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const ep);
static unsigned char *print_array(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p); static unsigned char *print_array(const cJSON * const item, const size_t depth, const cjbool format, printbuffer * const output_buffer);
static const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const ep); static const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const ep);
static unsigned char *print_object(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p); static unsigned char *print_object(const cJSON * const item, const size_t depth, const cjbool format, printbuffer * const output_buffer);
/* Utility to jump whitespace and cr/lf */ /* Utility to jump whitespace and cr/lf */
static const unsigned char *skip_whitespace(const unsigned char *in) static const unsigned char *skip_whitespace(const unsigned char *in)
@@ -893,7 +866,7 @@ static unsigned char *print(const cJSON * const item, cjbool format)
{ {
goto fail; goto fail;
} }
buffer->offset = update(buffer); /* update the length of the string */ update_offset(buffer);
/* copy the buffer over to a new one */ /* copy the buffer over to a new one */
printed = (unsigned char*) cJSON_malloc(buffer->offset + 1); printed = (unsigned char*) cJSON_malloc(buffer->offset + 1);
@@ -1027,16 +1000,11 @@ static const unsigned char *parse_value(cJSON * const item, const unsigned char
} }
/* Render a value to text. */ /* Render a value to text. */
static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p) static unsigned char *print_value(const cJSON * const item, const size_t depth, const cjbool format, printbuffer * const output_buffer)
{ {
unsigned char *out = NULL; unsigned char *output = NULL;
if (!item) if ((item == NULL) || (output_buffer == NULL))
{
return NULL;
}
if (p == NULL)
{ {
return NULL; return NULL;
} }
@@ -1044,65 +1012,65 @@ static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, p
switch ((item->type) & 0xFF) switch ((item->type) & 0xFF)
{ {
case cJSON_NULL: case cJSON_NULL:
out = ensure(p, 5); output = ensure(output_buffer, 5);
if (out != NULL) if (output != NULL)
{ {
strcpy((char*)out, "null"); strcpy((char*)output, "null");
} }
break; break;
case cJSON_False: case cJSON_False:
out = ensure(p, 6); output = ensure(output_buffer, 6);
if (out != NULL) if (output != NULL)
{ {
strcpy((char*)out, "false"); strcpy((char*)output, "false");
} }
break; break;
case cJSON_True: case cJSON_True:
out = ensure(p, 5); output = ensure(output_buffer, 5);
if (out != NULL) if (output != NULL)
{ {
strcpy((char*)out, "true"); strcpy((char*)output, "true");
} }
break; break;
case cJSON_Number: case cJSON_Number:
out = print_number(item, p); output = print_number(item, output_buffer);
break; break;
case cJSON_Raw: case cJSON_Raw:
{ {
size_t raw_length = 0; size_t raw_length = 0;
if (item->valuestring == NULL) if (item->valuestring == NULL)
{ {
if (!p->noalloc) if (!output_buffer->noalloc)
{ {
cJSON_free(p->buffer); cJSON_free(output_buffer->buffer);
} }
out = NULL; output = NULL;
break; break;
} }
raw_length = strlen(item->valuestring) + sizeof('\0'); raw_length = strlen(item->valuestring) + sizeof('\0');
out = ensure(p, raw_length); output = ensure(output_buffer, raw_length);
if (out != NULL) if (output != NULL)
{ {
memcpy(out, item->valuestring, raw_length); memcpy(output, item->valuestring, raw_length);
} }
break; break;
} }
case cJSON_String: case cJSON_String:
out = print_string(item, p); output = print_string(item, output_buffer);
break; break;
case cJSON_Array: case cJSON_Array:
out = print_array(item, depth, fmt, p); output = print_array(item, depth, format, output_buffer);
break; break;
case cJSON_Object: case cJSON_Object:
out = print_object(item, depth, fmt, p); output = print_object(item, depth, format, output_buffer);
break; break;
default: default:
out = NULL; output = NULL;
break; break;
} }
return out; return output;
} }
/* Build an array from input text. */ /* Build an array from input text. */
@@ -1184,87 +1152,68 @@ fail:
} }
/* Render an array to text */ /* Render an array to text */
static unsigned char *print_array(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p) static unsigned char *print_array(const cJSON * const item, const size_t depth, const cjbool format, printbuffer * const output_buffer)
{ {
unsigned char *out = NULL; unsigned char *output = NULL;
unsigned char *ptr = NULL; unsigned char *output_pointer = NULL;
size_t len = 5; size_t length = 0;
cJSON *child = item->child; cJSON *current_element = item->child;
size_t numentries = 0; size_t output_offset = 0;
size_t i = 0;
cjbool fail = false;
if (p == NULL) if (output_buffer == NULL)
{ {
return NULL; return NULL;
} }
/* How many entries in the array? */
while (child)
{
numentries++;
child = child->next;
}
/* Explicitly handle numentries == 0 */
if (!numentries)
{
out = ensure(p, 3);
if (out != NULL)
{
strcpy((char*)out, "[]");
}
return out;
}
/* Compose the output array. */ /* Compose the output array. */
/* opening square bracket */ /* opening square bracket */
i = p->offset; output_offset = output_buffer->offset;
ptr = ensure(p, 1); output_pointer = ensure(output_buffer, 1);
if (ptr == NULL) if (output_pointer == NULL)
{ {
return NULL; return NULL;
} }
*ptr = '[';
p->offset++;
child = item->child; *output_pointer = '[';
while (child && !fail) output_buffer->offset++;
{
if (!print_value(child, depth + 1, fmt, p))
{
return NULL;
}
p->offset = update(p);
if (child->next)
{
len = fmt ? 2 : 1;
ptr = ensure(p, len + 1);
if (ptr == NULL)
{
return NULL;
}
*ptr++ = ',';
if(fmt)
{
*ptr++ = ' ';
}
*ptr = '\0';
p->offset += len;
}
child = child->next;
}
ptr = ensure(p, 2);
if (ptr == NULL)
{
return NULL;
}
*ptr++ = ']';
*ptr = '\0';
out = (p->buffer) + i;
return out; current_element = item->child;
while (current_element != NULL)
{
if (print_value(current_element, depth + 1, format, output_buffer) == NULL)
{
return NULL;
}
update_offset(output_buffer);
if (current_element->next)
{
length = format ? 2 : 1;
output_pointer = ensure(output_buffer, length + 1);
if (output_pointer == NULL)
{
return NULL;
}
*output_pointer++ = ',';
if(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)
{
return NULL;
}
*output_pointer++ = ']';
*output_pointer = '\0';
output = output_buffer->buffer + output_offset;
return output;
} }
/* Build an object from the text. */ /* Build an object from the text. */
@@ -1363,152 +1312,120 @@ fail:
} }
/* Render an object to text. */ /* Render an object to text. */
static unsigned char *print_object(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p) static unsigned char *print_object(const cJSON * const item, const size_t depth, const cjbool format, printbuffer * const output_buffer)
{ {
unsigned char *out = NULL; unsigned char *output = NULL;
unsigned char *ptr = NULL; unsigned char *output_pointer = NULL;
size_t len = 7; size_t length = 0;
size_t i = 0; size_t output_offset = 0;
size_t j = 0; cJSON *current_item = item->child;
cJSON *child = item->child;
size_t numentries = 0;
if (p == NULL) if (output_buffer == NULL)
{ {
return NULL; return NULL;
} }
/* Count the number of entries. */
while (child)
{
numentries++;
child = child->next;
}
/* Explicitly handle empty object case */
if (!numentries)
{
out = ensure(p, fmt ? depth + 4 : 3);
if (out == NULL)
{
return NULL;
}
ptr = out;
*ptr++ = '{';
if (fmt) {
*ptr++ = '\n';
for (i = 0; i < depth; i++)
{
*ptr++ = '\t';
}
}
*ptr++ = '}';
*ptr++ = '\0';
return out;
}
/* Compose the output: */ /* Compose the output: */
i = p->offset; output_offset = output_buffer->offset;
len = fmt ? 2 : 1; /* fmt: {\n */ length = format ? 2 : 1; /* fmt: {\n */
ptr = ensure(p, len + 1); output_pointer = ensure(output_buffer, length + 1);
if (ptr == NULL) if (output_pointer == NULL)
{ {
return NULL; return NULL;
} }
*ptr++ = '{'; *output_pointer++ = '{';
if (fmt) if (format)
{ {
*ptr++ = '\n'; *output_pointer++ = '\n';
} }
*ptr = '\0'; output_buffer->offset += length;
p->offset += len;
child = item->child; current_item = item->child;
depth++; while (current_item)
while (child)
{ {
if (fmt) if (format)
{ {
ptr = ensure(p, depth); size_t i;
if (ptr == NULL) output_pointer = ensure(output_buffer, depth + 1);
if (output_pointer == NULL)
{ {
return NULL; return NULL;
} }
for (j = 0; j < depth; j++) for (i = 0; i < depth + 1; i++)
{ {
*ptr++ = '\t'; *output_pointer++ = '\t';
} }
p->offset += depth; output_buffer->offset += depth + 1;
} }
/* print key */ /* print key */
if (!print_string_ptr((unsigned char*)child->string, p)) if (print_string_ptr((unsigned char*)current_item->string, output_buffer) == NULL)
{ {
return NULL; return NULL;
} }
p->offset = update(p); update_offset(output_buffer);
len = fmt ? 2 : 1; length = format ? 2 : 1;
ptr = ensure(p, len); output_pointer = ensure(output_buffer, length);
if (ptr == NULL) if (output_pointer == NULL)
{ {
return NULL; return NULL;
} }
*ptr++ = ':'; *output_pointer++ = ':';
if (fmt) if (format)
{ {
*ptr++ = '\t'; *output_pointer++ = '\t';
} }
p->offset+=len; output_buffer->offset += length;
/* print value */ /* print value */
if (!print_value(child, depth, fmt, p)) if (!print_value(current_item, depth + 1, format, output_buffer))
{ {
return NULL; return NULL;
} }
p->offset = update(p); update_offset(output_buffer);
/* print comma if not last */ /* print comma if not last */
len = (size_t) (fmt ? 1 : 0) + (child->next ? 1 : 0); length = (size_t) (format ? 1 : 0) + (current_item->next ? 1 : 0);
ptr = ensure(p, len + 1); output_pointer = ensure(output_buffer, length + 1);
if (ptr == NULL) if (output_pointer == NULL)
{ {
return NULL; return NULL;
} }
if (child->next) if (current_item->next)
{ {
*ptr++ = ','; *output_pointer++ = ',';
} }
if (fmt) if (format)
{ {
*ptr++ = '\n'; *output_pointer++ = '\n';
} }
*ptr = '\0'; *output_pointer = '\0';
p->offset += len; output_buffer->offset += length;
child = child->next; current_item = current_item->next;
} }
ptr = ensure(p, fmt ? (depth + 1) : 2); output_pointer = ensure(output_buffer, format ? (depth + 2) : 2);
if (ptr == NULL) if (output_pointer == NULL)
{ {
return NULL; return NULL;
} }
if (fmt) if (format)
{ {
for (i = 0; i < (depth - 1); i++) size_t i;
for (i = 0; i < (depth); i++)
{ {
*ptr++ = '\t'; *output_pointer++ = '\t';
} }
} }
*ptr++ = '}'; *output_pointer++ = '}';
*ptr = '\0'; *output_pointer = '\0';
out = (p->buffer) + i; output = (output_buffer->buffer) + output_offset;
return out; return output;
} }
/* Get Array size/item / object item. */ /* Get Array size/item / object item. */

View File

@@ -40,6 +40,7 @@ static void assert_print_string(const char *expected, const char *input)
static void print_string_should_print_empty_strings(void) static void print_string_should_print_empty_strings(void)
{ {
assert_print_string("\"\"", ""); assert_print_string("\"\"", "");
assert_print_string("\"\"", NULL);
} }
static void print_string_should_print_ascii(void) static void print_string_should_print_ascii(void)