From 24dbf293603a4accf5d559416bb53c722c4c7914 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 01:55:45 +0100 Subject: [PATCH 01/10] parse_array: simplify to one do-while loop --- cJSON.c | 79 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/cJSON.c b/cJSON.c index 884e25d..1e2091e 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1027,68 +1027,69 @@ static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, p } /* Build an array from input text. */ -static const unsigned char *parse_array(cJSON *item, const unsigned char *value, const unsigned char **ep) +static const unsigned char *parse_array(cJSON *item, const unsigned char *value, const unsigned char **error_pointer) { cJSON *head = NULL; /* head of the linked list */ - cJSON *child = NULL; + cJSON *current_item = NULL; + if (*value != '[') { - /* not an array! */ - *ep = value; + /* not an array */ + *error_pointer = value; goto fail; } - value = skip(value + 1); + value = skip(value + 1); /* skip whitespace */ if (*value == ']') { - /* empty array. */ + /* empty array */ goto success; } - head = child = cJSON_New_Item(); - if (!child) - { - /* memory fail */ - goto fail; - } - /* skip any spacing, get the value. */ - value = skip(parse_value(child, skip(value), ep)); - if (!value) - { - goto fail; - } - + /* step back to character in front of the first element */ + value--; /* loop through the comma separated array elements */ - while (*value == ',') + do { - cJSON *new_item = NULL; - if (!(new_item = cJSON_New_Item())) + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(); + if (new_item == NULL) { - /* memory fail */ - goto fail; + goto fail; /* allocation failure */ } - /* add new item to end of the linked list */ - child->next = new_item; - new_item->prev = child; - child = new_item; - /* go to the next comma */ - value = skip(parse_value(child, skip(value + 1), ep)); - if (!value) + /* attach next item to list */ + if (head == NULL) { - /* memory fail */ - goto fail; + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + value = skip(value + 1); /* skip whitespace before value */ + value = parse_value(current_item, value, error_pointer); + value = skip(value); /* skip whitespace after value */ + if (value == NULL) + { + goto fail; /* failed to parse value */ } } + while (*value == ','); if (*value == ']') { - /* end of array */ - goto success; + goto success; /* end of array */ } - /* malformed. */ - *ep = value; + /* malformed array */ + *error_pointer = value; goto fail; success: @@ -1098,9 +1099,9 @@ success: return value + 1; fail: - if (child != NULL) + if (head != NULL) { - cJSON_Delete(child); + cJSON_Delete(head); } return NULL; From 03f23738bbc15298bc3a5980f159b260cb9d9b0a Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 02:28:32 +0100 Subject: [PATCH 02/10] parse_object: simplify to one do-while loop --- cJSON.c | 122 ++++++++++++++++++++++++++------------------------------ 1 file changed, 57 insertions(+), 65 deletions(-) diff --git a/cJSON.c b/cJSON.c index 1e2091e..89ea669 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1277,96 +1277,88 @@ static unsigned char *print_array(const cJSON *item, size_t depth, cjbool fmt, p } /* Build an object from the text. */ -static const unsigned char *parse_object(cJSON *item, const unsigned char *value, const unsigned char **ep) +static const unsigned char *parse_object(cJSON *item, const unsigned char *value, const unsigned char **error_pointer) { cJSON *head = NULL; /* linked list head */ - cJSON *child = NULL; + cJSON *current_item = NULL; + if (*value != '{') { - /* not an object! */ - *ep = value; + /* not an object */ + *error_pointer = value; goto fail; } - value = skip(value + 1); + value = skip(value + 1); /* skip whitespace */ if (*value == '}') { - /* empty object. */ - goto success; + goto success; /* empty object */ } - head = child = cJSON_New_Item(); - if (!child) + /* step back to character in front of the first element */ + value--; + /* loop through the comma separated array elements */ + do { - goto fail; - } - /* parse first key */ - value = skip(parse_string(child, skip(value), ep)); - if (!value) - { - goto fail; - } - /* use string as key, not value */ - child->string = child->valuestring; - child->valuestring = NULL; - - if (*value != ':') - { - /* invalid object. */ - *ep = value; - goto fail; - } - /* skip any spacing, get the value. */ - value = skip(parse_value(child, skip(value + 1), ep)); - if (!value) - { - goto fail; - } - - while (*value == ',') - { - cJSON *new_item = NULL; - if (!(new_item = cJSON_New_Item())) + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(); + if (new_item == NULL) { - /* memory fail */ - goto fail; - } - /* add to linked list */ - child->next = new_item; - new_item->prev = child; - - child = new_item; - value = skip(parse_string(child, skip(value + 1), ep)); - if (!value) - { - goto fail; + goto fail; /* allocation failure */ } - /* use string as key, not value */ - child->string = child->valuestring; - child->valuestring = NULL; + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + value = skip(value + 1); /* skip whitespaces before name */ + value = parse_string(current_item, value, error_pointer); + value = skip(value); /* skip whitespaces after name */ + if (value == NULL) + { + goto fail; /* faile to parse name */ + } + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; if (*value != ':') { - /* invalid object. */ - *ep = value; + /* invalid object */ + *error_pointer = value; goto fail; } - /* skip any spacing, get the value. */ - value = skip(parse_value(child, skip(value + 1), ep)); - if (!value) + + /* parse the value */ + value = skip(value + 1); /* skip whitespaces before value */ + value = parse_value(current_item, value, error_pointer); + value = skip(value); /* skip whitespaces after the value */ + if (value == NULL) { - goto fail; + goto fail; /* failed to parse value */ } } - /* end of object */ + while (*value == ','); + if (*value == '}') { - goto success; + goto success; /* end of object */ } - /* malformed */ - *ep = value; + /* malformed object */ + *error_pointer = value; goto fail; success: @@ -1376,9 +1368,9 @@ success: return value + 1; fail: - if (child != NULL) + if (head != NULL) { - cJSON_Delete(child); + cJSON_Delete(head); } return NULL; From ace5047782c0eb1f428d6566c9e6ed263ff9fa28 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 18:55:27 +0100 Subject: [PATCH 03/10] parse_string: reduce mental burden when reading the code This restructures parse_string in a way, that you need to keep less state in your head to understand the code. This is achieved by: * only changing the input pointer (current position) at a few places (not all throughout) * splitting out the UTF16 handling into a separate function * renaming the variables so you know what they do without additional context --- cJSON.c | 345 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 201 insertions(+), 144 deletions(-) diff --git a/cJSON.c b/cJSON.c index 89ea669..d6918fa 100644 --- a/cJSON.c +++ b/cJSON.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "cJSON.h" /* define our own boolean type */ @@ -403,201 +404,257 @@ static unsigned parse_hex4(const unsigned char *str) return h; } -/* first bytes of UTF8 encoding for a given length in bytes */ -static const unsigned char firstByteMark[5] = +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static uint8_t utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer, const unsigned char **error_pointer) { - 0x00, /* should never happen */ - 0x00, /* 0xxxxxxx */ - 0xC0, /* 110xxxxx */ - 0xE0, /* 1110xxxx */ - 0xF0 /* 11110xxx */ -}; - -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char *parse_string(cJSON *item, const unsigned char *str, const unsigned char **ep) -{ - const unsigned char *ptr = str + 1; - const unsigned char *end_ptr = str + 1; - unsigned char *ptr2 = NULL; - unsigned char *out = NULL; - size_t len = 0; - unsigned uc = 0; - unsigned uc2 = 0; - - /* not a string! */ - if (*str != '\"') + /* first bytes of UTF8 encoding for a given length in bytes */ + static const unsigned char firstByteMark[5] = { - *ep = str; + 0x00, /* should never happen */ + 0x00, /* 0xxxxxxx */ + 0xC0, /* 110xxxxx */ + 0xE0, /* 1110xxxx */ + 0xF0 /* 11110xxx */ + }; + + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + uint8_t utf8_length = 0; + uint8_t sequence_length = 0; + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + *error_pointer = first_sequence; goto fail; } - while ((*end_ptr != '\"') && *end_ptr) + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)) || (first_code == 0)) { - if (*end_ptr++ == '\\') + *error_pointer = first_sequence; + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) { - if (*end_ptr == '\0') - { - /* prevent buffer overflow when last input character is a backslash */ - goto fail; - } - /* Skip escaped quotes. */ - end_ptr++; + /* input ends unexpectedly */ + *error_pointer = first_sequence; + goto fail; } - len++; + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + *error_pointer = first_sequence; + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + *error_pointer = first_sequence; + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; } - /* This is at most how long we need for the string, roughly. */ - out = (unsigned char*)cJSON_malloc(len + 1); - if (!out) + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + } + else + { + /* invalid unicode codepoint */ + *error_pointer = first_sequence; goto fail; } - ptr2 = out; - /* loop through the string literal */ - while (ptr < end_ptr) + /* encode as utf8 */ + switch (utf8_length) { - if (*ptr != '\\') + case 4: + /* 10xxxxxx */ + (*output_pointer)[3] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + case 3: + /* 10xxxxxx */ + (*output_pointer)[2] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + case 2: + (*output_pointer)[1] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + case 1: + /* depending on the length in bytes this determines the + encoding of the first UTF8 byte */ + (*output_pointer)[0] = (unsigned char)((codepoint | firstByteMark[utf8_length]) & 0xFF); + break; + default: + *error_pointer = first_sequence; + goto fail; + } + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static const unsigned char *parse_string(cJSON *item, const unsigned char *input, const unsigned char **error_pointer) +{ + const unsigned char *input_pointer = input + 1; + const unsigned char *input_end = input + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (*input != '\"') + { + *error_pointer = input; + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while ((*input_end != '\"') && (*input_end != '\0')) { - *ptr2++ = *ptr++; + /* is escape sequence */ + if (input_end[0] == '\\') + { + if (input_end[1] == '\0') + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (*input_end == '\0') + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - input) - skipped_bytes; + output = (unsigned char*)cJSON_malloc(allocation_length + sizeof('\0')); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; } /* escape sequence */ else { - ptr++; - switch (*ptr) + uint8_t sequence_length = 2; + switch (input_pointer[1]) { case 'b': - *ptr2++ = '\b'; + *output_pointer++ = '\b'; break; case 'f': - *ptr2++ = '\f'; + *output_pointer++ = '\f'; break; case 'n': - *ptr2++ = '\n'; + *output_pointer++ = '\n'; break; case 'r': - *ptr2++ = '\r'; + *output_pointer++ = '\r'; break; case 't': - *ptr2++ = '\t'; + *output_pointer++ = '\t'; break; case '\"': case '\\': case '/': - *ptr2++ = *ptr; + *output_pointer++ = input_pointer[1]; break; + + /* UTF-16 literal */ case 'u': - /* transcode utf16 to utf8. See RFC2781 and RFC3629. */ - uc = parse_hex4(ptr + 1); /* get the unicode char. */ - ptr += 4; - if (ptr >= end_ptr) + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer, error_pointer); + if (sequence_length == 0) { - /* invalid */ - *ep = str; + /* failed to convert UTF16-literal to UTF-8 */ goto fail; } - /* check for invalid. */ - if (((uc >= 0xDC00) && (uc <= 0xDFFF)) || (uc == 0)) - { - *ep = str; - goto fail; - } - - /* UTF16 surrogate pairs. */ - if ((uc >= 0xD800) && (uc<=0xDBFF)) - { - if ((ptr + 6) > end_ptr) - { - /* invalid */ - *ep = str; - goto fail; - } - if ((ptr[1] != '\\') || (ptr[2] != 'u')) - { - /* missing second-half of surrogate. */ - *ep = str; - goto fail; - } - uc2 = parse_hex4(ptr + 3); - ptr += 6; /* \uXXXX */ - if ((uc2 < 0xDC00) || (uc2 > 0xDFFF)) - { - /* invalid second-half of surrogate. */ - *ep = str; - goto fail; - } - /* calculate unicode codepoint from the surrogate pair */ - uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); - } - - /* encode as UTF8 - * takes at maximum 4 bytes to encode: - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ - len = 4; - if (uc < 0x80) - { - /* normal ascii, encoding 0xxxxxxx */ - len = 1; - } - else if (uc < 0x800) - { - /* two bytes, encoding 110xxxxx 10xxxxxx */ - len = 2; - } - else if (uc < 0x10000) - { - /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ - len = 3; - } - ptr2 += len; - - switch (len) { - case 4: - /* 10xxxxxx */ - *--ptr2 = (unsigned char)((uc | 0x80) & 0xBF); - uc >>= 6; - case 3: - /* 10xxxxxx */ - *--ptr2 = (unsigned char)((uc | 0x80) & 0xBF); - uc >>= 6; - case 2: - /* 10xxxxxx */ - *--ptr2 = (unsigned char)((uc | 0x80) & 0xBF); - uc >>= 6; - case 1: - /* depending on the length in bytes this determines the - * encoding ofthe first UTF8 byte */ - *--ptr2 = (unsigned char)((uc | firstByteMark[len]) & 0xFF); - break; - default: - *ep = str; - goto fail; - } - ptr2 += len; break; + default: - *ep = str; + *error_pointer = input_pointer; goto fail; } - ptr++; + input_pointer += sequence_length; } } - *ptr2 = '\0'; - if (*ptr == '\"') - { - ptr++; - } + + /* zero terminate the output */ + *output_pointer = '\0'; item->type = cJSON_String; - item->valuestring = (char*)out; + item->valuestring = (char*)output; - return ptr; + return input_end + 1; fail: - if (out != NULL) + if (output != NULL) { - cJSON_free(out); + cJSON_free(output); } return NULL; From efb5e1bc93b199a42430797e2d88c60d96c24012 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 20:00:06 +0100 Subject: [PATCH 04/10] parse_string: Improve const correctnes of pointers --- cJSON.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cJSON.c b/cJSON.c index d6918fa..03cfd7a 100644 --- a/cJSON.c +++ b/cJSON.c @@ -543,7 +543,7 @@ fail: } /* Parse the input text into an unescaped cinput, and populate item. */ -static const unsigned char *parse_string(cJSON *item, const unsigned char *input, const unsigned char **error_pointer) +static const unsigned char *parse_string(cJSON * const item, const unsigned char * const input, const unsigned char ** const error_pointer) { const unsigned char *input_pointer = input + 1; const unsigned char *input_end = input + 1; From 15592c50f634e494d441ab2ddc4811463a3fad68 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 20:02:18 +0100 Subject: [PATCH 05/10] parse_number: improve variable names + const correctness --- cJSON.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cJSON.c b/cJSON.c index 03cfd7a..ff69c9c 100644 --- a/cJSON.c +++ b/cJSON.c @@ -154,21 +154,20 @@ void cJSON_Delete(cJSON *c) } /* Parse the input text to generate a number, and populate the result into item. */ -static const unsigned char *parse_number(cJSON *item, const unsigned char *num) +static const unsigned char *parse_number(cJSON * const item, const unsigned char * const input) { double number = 0; - unsigned char *endpointer = NULL; + unsigned char *after_end = NULL; - if (num == NULL) + if (input == NULL) { return NULL; } - number = strtod((const char*)num, (char**)&endpointer); - if ((num == endpointer) || (num == NULL)) + number = strtod((const char*)input, (char**)&after_end); + if (input == after_end) { - /* parse_error */ - return NULL; + return NULL; /* parse_error */ } item->valuedouble = number; @@ -186,9 +185,10 @@ static const unsigned char *parse_number(cJSON *item, const unsigned char *num) { item->valueint = (int)number; } + item->type = cJSON_Number; - return endpointer; + return after_end; } /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ From 3dc6339025411a33431ff546d8c8b87ca527a144 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 20:08:53 +0100 Subject: [PATCH 06/10] parse_array: improve variable names + const correctness --- cJSON.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/cJSON.c b/cJSON.c index ff69c9c..4daa1cb 100644 --- a/cJSON.c +++ b/cJSON.c @@ -815,7 +815,7 @@ static unsigned char *print_string(const cJSON *item, printbuffer *p) /* Predeclare these prototypes. */ static const unsigned char *parse_value(cJSON *item, const unsigned char *value, const unsigned char **ep); static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p); -static const unsigned char *parse_array(cJSON *item, const unsigned char *value, const unsigned char **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 const unsigned char *parse_object(cJSON *item, const unsigned char *value, const unsigned char **ep); static unsigned char *print_object(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p); @@ -1084,27 +1084,27 @@ static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, p } /* Build an array from input text. */ -static const unsigned char *parse_array(cJSON *item, const unsigned char *value, const unsigned char **error_pointer) +static const unsigned char *parse_array(cJSON * const item, const unsigned char *input, const unsigned char ** const error_pointer) { cJSON *head = NULL; /* head of the linked list */ cJSON *current_item = NULL; - if (*value != '[') + if (*input != '[') { /* not an array */ - *error_pointer = value; + *error_pointer = input; goto fail; } - value = skip(value + 1); /* skip whitespace */ - if (*value == ']') + input = skip(input + 1); /* skip whitespace */ + if (*input == ']') { /* empty array */ goto success; } /* step back to character in front of the first element */ - value--; + input--; /* loop through the comma separated array elements */ do { @@ -1130,30 +1130,27 @@ static const unsigned char *parse_array(cJSON *item, const unsigned char *value, } /* parse next value */ - value = skip(value + 1); /* skip whitespace before value */ - value = parse_value(current_item, value, error_pointer); - value = skip(value); /* skip whitespace after value */ - if (value == NULL) + input = skip(input + 1); /* skip whitespace before value */ + input = parse_value(current_item, input, error_pointer); + input = skip(input); /* skip whitespace after value */ + if (input == NULL) { goto fail; /* failed to parse value */ } } - while (*value == ','); + while (*input == ','); - if (*value == ']') + if (*input != ']') { - goto success; /* end of array */ + *error_pointer = input; + goto fail; /* expected end of array */ } - /* malformed array */ - *error_pointer = value; - goto fail; - success: item->type = cJSON_Array; item->child = head; - return value + 1; + return input + 1; fail: if (head != NULL) From 698dc528f452398db4588983cfeb37ce0546795e Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 20:14:34 +0100 Subject: [PATCH 07/10] parse_object: improve variable names + const correctness --- cJSON.c | 53 ++++++++++++++++++++++++----------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/cJSON.c b/cJSON.c index 4daa1cb..672bab5 100644 --- a/cJSON.c +++ b/cJSON.c @@ -817,7 +817,7 @@ static const unsigned char *parse_value(cJSON *item, const unsigned char *value, static unsigned char *print_value(const cJSON *item, size_t depth, cjbool fmt, printbuffer *p); 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 const unsigned char *parse_object(cJSON *item, const unsigned char *value, const unsigned char **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); /* Utility to jump whitespace and cr/lf */ @@ -1331,26 +1331,25 @@ static unsigned char *print_array(const cJSON *item, size_t depth, cjbool fmt, p } /* Build an object from the text. */ -static const unsigned char *parse_object(cJSON *item, const unsigned char *value, const unsigned char **error_pointer) +static const unsigned char *parse_object(cJSON * const item, const unsigned char *input, const unsigned char ** const error_pointer) { cJSON *head = NULL; /* linked list head */ cJSON *current_item = NULL; - if (*value != '{') + if (*input != '{') { - /* not an object */ - *error_pointer = value; - goto fail; + *error_pointer = input; + goto fail; /* not an object */ } - value = skip(value + 1); /* skip whitespace */ - if (*value == '}') + input = skip(input + 1); /* skip whitespace */ + if (*input == '}') { goto success; /* empty object */ } /* step back to character in front of the first element */ - value--; + input--; /* loop through the comma separated array elements */ do { @@ -1376,10 +1375,10 @@ static const unsigned char *parse_object(cJSON *item, const unsigned char *value } /* parse the name of the child */ - value = skip(value + 1); /* skip whitespaces before name */ - value = parse_string(current_item, value, error_pointer); - value = skip(value); /* skip whitespaces after name */ - if (value == NULL) + input = skip(input + 1); /* skip whitespaces before name */ + input = parse_string(current_item, input, error_pointer); + input = skip(input); /* skip whitespaces after name */ + if (input == NULL) { goto fail; /* faile to parse name */ } @@ -1388,38 +1387,34 @@ static const unsigned char *parse_object(cJSON *item, const unsigned char *value current_item->string = current_item->valuestring; current_item->valuestring = NULL; - if (*value != ':') + if (*input != ':') { - /* invalid object */ - *error_pointer = value; - goto fail; + *error_pointer = input; + goto fail; /* invalid object */ } /* parse the value */ - value = skip(value + 1); /* skip whitespaces before value */ - value = parse_value(current_item, value, error_pointer); - value = skip(value); /* skip whitespaces after the value */ - if (value == NULL) + input = skip(input + 1); /* skip whitespaces before value */ + input = parse_value(current_item, input, error_pointer); + input = skip(input); /* skip whitespaces after the value */ + if (input == NULL) { goto fail; /* failed to parse value */ } } - while (*value == ','); + while (*input == ','); - if (*value == '}') + if (*input != '}') { - goto success; /* end of object */ + *error_pointer = input; + goto fail; /* expected end of object */ } - /* malformed object */ - *error_pointer = value; - goto fail; - success: item->type = cJSON_Object; item->child = head; - return value + 1; + return input + 1; fail: if (head != NULL) From ead389aba22e200ceb3f875b388bd23e69750184 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 21:01:50 +0100 Subject: [PATCH 08/10] parse_value: improve variable names + const correctness --- cJSON.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/cJSON.c b/cJSON.c index 672bab5..7be47b1 100644 --- a/cJSON.c +++ b/cJSON.c @@ -813,7 +813,7 @@ static unsigned char *print_string(const cJSON *item, printbuffer *p) } /* Predeclare these prototypes. */ -static const unsigned char *parse_value(cJSON *item, const unsigned char *value, const unsigned char **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 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); @@ -927,50 +927,56 @@ int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cjbool } /* Parser core - when encountering text, process appropriately. */ -static const unsigned char *parse_value(cJSON *item, const unsigned char *value, const unsigned char **ep) +static const unsigned char *parse_value(cJSON * const item, const unsigned char * const input, const unsigned char ** const error_pointer) { - if (!value) + if (input == NULL) { - /* Fail on null. */ - return NULL; + return NULL; /* no input */ } /* parse the different types of values */ - if (!strncmp((const char*)value, "null", 4)) + /* null */ + if (!strncmp((const char*)input, "null", 4)) { item->type = cJSON_NULL; - return value + 4; + return input + 4; } - if (!strncmp((const char*)value, "false", 5)) + /* false */ + if (!strncmp((const char*)input, "false", 5)) { item->type = cJSON_False; - return value + 5; + return input + 5; } - if (!strncmp((const char*)value, "true", 4)) + /* true */ + if (!strncmp((const char*)input, "true", 4)) { item->type = cJSON_True; item->valueint = 1; - return value + 4; + return input + 4; } - if (*value == '\"') + /* string */ + if (*input == '\"') { - return parse_string(item, value, ep); + return parse_string(item, input, error_pointer); } - if ((*value == '-') || ((*value >= '0') && (*value <= '9'))) + /* number */ + if ((*input == '-') || ((*input >= '0') && (*input <= '9'))) { - return parse_number(item, value); + return parse_number(item, input); } - if (*value == '[') + /* array */ + if (*input == '[') { - return parse_array(item, value, ep); + return parse_array(item, input, error_pointer); } - if (*value == '{') + /* object */ + if (*input == '{') { - return parse_object(item, value, ep); + return parse_object(item, input, error_pointer); } /* failure. */ - *ep = value; + *error_pointer = input; return NULL; } From 0f271dcf6383d487c62e71e682fa85f273015d0f Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 20:26:40 +0100 Subject: [PATCH 09/10] parse_hex4: make input pointer const --- cJSON.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cJSON.c b/cJSON.c index 7be47b1..aef9824 100644 --- a/cJSON.c +++ b/cJSON.c @@ -368,7 +368,7 @@ static unsigned char *print_number(const cJSON *item, printbuffer *p) } /* parse 4 digit hexadecimal number */ -static unsigned parse_hex4(const unsigned char *str) +static unsigned parse_hex4(const unsigned char * const input) { unsigned int h = 0; size_t i = 0; @@ -376,17 +376,17 @@ static unsigned parse_hex4(const unsigned char *str) for (i = 0; i < 4; i++) { /* parse digit */ - if ((*str >= '0') && (*str <= '9')) + if ((input[i] >= '0') && (input[i] <= '9')) { - h += (unsigned int) (*str) - '0'; + h += (unsigned int) input[i] - '0'; } - else if ((*str >= 'A') && (*str <= 'F')) + else if ((input[i] >= 'A') && (input[i] <= 'F')) { - h += (unsigned int) 10 + (*str) - 'A'; + h += (unsigned int) 10 + input[i] - 'A'; } - else if ((*str >= 'a') && (*str <= 'f')) + else if ((input[i] >= 'a') && (input[i] <= 'f')) { - h += (unsigned int) 10 + (*str) - 'a'; + h += (unsigned int) 10 + input[i] - 'a'; } else /* invalid */ { @@ -397,7 +397,6 @@ static unsigned parse_hex4(const unsigned char *str) { /* shift left to make place for the next nibble */ h = h << 4; - str++; } } From b41264d1647da7a7479c858c975b6d1f2e035856 Mon Sep 17 00:00:00 2001 From: Max Bruckner Date: Thu, 16 Feb 2017 21:22:43 +0100 Subject: [PATCH 10/10] Remove uint8_t because it's not part of C89 --- cJSON.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cJSON.c b/cJSON.c index aef9824..9bc94ba 100644 --- a/cJSON.c +++ b/cJSON.c @@ -30,7 +30,6 @@ #include #include #include -#include #include "cJSON.h" /* define our own boolean type */ @@ -405,7 +404,7 @@ static unsigned parse_hex4(const unsigned char * const input) /* converts a UTF-16 literal to UTF-8 * A literal can be one or two sequences of the form \uXXXX */ -static uint8_t utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer, const unsigned char **error_pointer) +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer, const unsigned char **error_pointer) { /* first bytes of UTF8 encoding for a given length in bytes */ static const unsigned char firstByteMark[5] = @@ -420,8 +419,8 @@ static uint8_t utf16_literal_to_utf8(const unsigned char * const input_pointer, long unsigned int codepoint = 0; unsigned int first_code = 0; const unsigned char *first_sequence = input_pointer; - uint8_t utf8_length = 0; - uint8_t sequence_length = 0; + unsigned char utf8_length = 0; + unsigned char sequence_length = 0; /* get the first utf16 sequence */ first_code = parse_hex4(first_sequence + 2); @@ -600,7 +599,7 @@ static const unsigned char *parse_string(cJSON * const item, const unsigned char /* escape sequence */ else { - uint8_t sequence_length = 2; + unsigned char sequence_length = 2; switch (input_pointer[1]) { case 'b':