From 1360eac0bcddb404df8b04da04c18d8f3db95603 Mon Sep 17 00:00:00 2001 From: Ian Mobley Date: Wed, 4 Jan 2017 15:54:37 -0800 Subject: [PATCH 1/5] WIP: Get some stubbs set up for streaming Just jotting down the first steps in streaming a serialized cJSON object. The point being that instead of allocating and/or reallocating a potentially large buffer, the serialized data just gets periodically written out. It's similar to the printbuffer, but instead of reallocating when the buffer is full, it just flushes the current string out to a user provided callback. --- cJSON.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cJSON.h | 14 ++++++ 2 files changed, 154 insertions(+) diff --git a/cJSON.c b/cJSON.c index d20341a5..1f714625 100644 --- a/cJSON.c +++ b/cJSON.c @@ -904,6 +904,146 @@ int cJSON_PrintPreallocated(cJSON *item,char *buf, const int len, const cjbool f return print_value(item,0,fmt,&p) != NULL; } +void cJSON_StreamInit(cJSON_Stream *stream) +{ + memset(stream, 0, sizeof(cJSON_Stream)); +} + +void cJSON_StreamDeInit(cJSON_Stream *stream) +{ + /* Do nothing, reserved for later */ + (void)stream; +} + +typedef struct Stream_Data +{ + char buffer[256]; +} Stream_Data; + +static int stream_number(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) +{ + double d; + + (void)stream; + (void)item; + (void)data; + + d = item->valuedouble; + + if (d == 0) + { + strcpy(data->buffer, "0"); + } + else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) + { + sprintf(data->buffer, "%d", item->valueint); + } + else + { + if ((d * 0) != 0) + { + sprintf(data->buffer, "null"); + } + else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) + { + sprintf(data->buffer, "%.0f", d); + } + else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) + { + sprintf(data->buffer, "%e", d); + } + else + { + sprintf(data->buffer, "%f", d); + } + } + + stream->cb(data->buffer, strlen(data->buffer), stream->cb_data); + + return 0; +} + +static int stream_string(const cJSON_Stream *stream, const char *str, Stream_Data *data) +{ + (void)stream; + (void)str; + (void)data; + + return 0; +} + +static int stream_array(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) +{ + (void)stream; + (void)item; + (void)data; + + return 0; +} + +static int stream_object(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) +{ + int numentries; + + (void)stream; + (void)item; + (void)data; + + numentries = cJSON_GetArraySize(item); + + if (!numentries) + { + return 0; + } + + return 0; +} + +static int stream_value(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) +{ + int count = 0; + + if (!stream || !item) + { + return -1; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + strcpy(data->buffer, "null"); + break; + case cJSON_False: + strcpy(data->buffer, "false"); + break; + case cJSON_True: + strcpy(data->buffer, "true"); + break; + case cJSON_Number: + count += stream_number(stream, item, data); + break; + case cJSON_String: + count += stream_string(stream, item->valuestring, data); + break; + case cJSON_Array: + count += stream_array(stream, item, data); + break; + case cJSON_Object: + count += stream_object(stream, item, data); + break; + } + + return count; +} + +int cJSON_PrintStream(const cJSON_Stream *stream, const cJSON *item) +{ + Stream_Data data; + + return stream_value(stream, item, &data); +} + + /* Parser core - when encountering text, process appropriately. */ static const char *parse_value(cJSON *item, const char *value, const char **ep) { diff --git a/cJSON.h b/cJSON.h index 2b45b5a5..c4956fa4 100644 --- a/cJSON.h +++ b/cJSON.h @@ -65,6 +65,20 @@ typedef struct cJSON char *string; } cJSON; +typedef int (*cjson_callback)(const char *ptr, size_t length, void *cb_data); + +typedef struct cJSON_Stream +{ + cjson_callback cb; + void *cb_data; + + int fmt; +} cJSON_Stream; + +void cJSON_StreamInit(cJSON_Stream *stream); +int cJSON_PrintStream(const cJSON_Stream *stream, const cJSON *item); +void cJSON_StreamDeInit(cJSON_Stream *stream); + typedef struct cJSON_Hooks { void *(*malloc_fn)(size_t sz); From 3133fb4f93f19930c3845fb7a4f6d25956d9be75 Mon Sep 17 00:00:00 2001 From: Ian Mobley Date: Thu, 5 Jan 2017 10:12:06 -0800 Subject: [PATCH 2/5] Flesh out all of the stream functions Still need to handle strings larger than the stream buffer and some kind of error handling. Still need to fix tab depth for objects and arrays. Also need to consider adding a function for simply adding characters to the stream since that would be a simpler to use for the array and object functions. --- cJSON.c | 375 +++++++++++++++++++++++++++++++++++--------------------- cJSON.h | 6 +- 2 files changed, 240 insertions(+), 141 deletions(-) diff --git a/cJSON.c b/cJSON.c index 1f714625..8abef21f 100644 --- a/cJSON.c +++ b/cJSON.c @@ -904,146 +904,6 @@ int cJSON_PrintPreallocated(cJSON *item,char *buf, const int len, const cjbool f return print_value(item,0,fmt,&p) != NULL; } -void cJSON_StreamInit(cJSON_Stream *stream) -{ - memset(stream, 0, sizeof(cJSON_Stream)); -} - -void cJSON_StreamDeInit(cJSON_Stream *stream) -{ - /* Do nothing, reserved for later */ - (void)stream; -} - -typedef struct Stream_Data -{ - char buffer[256]; -} Stream_Data; - -static int stream_number(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) -{ - double d; - - (void)stream; - (void)item; - (void)data; - - d = item->valuedouble; - - if (d == 0) - { - strcpy(data->buffer, "0"); - } - else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) - { - sprintf(data->buffer, "%d", item->valueint); - } - else - { - if ((d * 0) != 0) - { - sprintf(data->buffer, "null"); - } - else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) - { - sprintf(data->buffer, "%.0f", d); - } - else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) - { - sprintf(data->buffer, "%e", d); - } - else - { - sprintf(data->buffer, "%f", d); - } - } - - stream->cb(data->buffer, strlen(data->buffer), stream->cb_data); - - return 0; -} - -static int stream_string(const cJSON_Stream *stream, const char *str, Stream_Data *data) -{ - (void)stream; - (void)str; - (void)data; - - return 0; -} - -static int stream_array(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) -{ - (void)stream; - (void)item; - (void)data; - - return 0; -} - -static int stream_object(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) -{ - int numentries; - - (void)stream; - (void)item; - (void)data; - - numentries = cJSON_GetArraySize(item); - - if (!numentries) - { - return 0; - } - - return 0; -} - -static int stream_value(const cJSON_Stream *stream, const cJSON *item, Stream_Data *data) -{ - int count = 0; - - if (!stream || !item) - { - return -1; - } - - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - strcpy(data->buffer, "null"); - break; - case cJSON_False: - strcpy(data->buffer, "false"); - break; - case cJSON_True: - strcpy(data->buffer, "true"); - break; - case cJSON_Number: - count += stream_number(stream, item, data); - break; - case cJSON_String: - count += stream_string(stream, item->valuestring, data); - break; - case cJSON_Array: - count += stream_array(stream, item, data); - break; - case cJSON_Object: - count += stream_object(stream, item, data); - break; - } - - return count; -} - -int cJSON_PrintStream(const cJSON_Stream *stream, const cJSON *item) -{ - Stream_Data data; - - return stream_value(stream, item, &data); -} - - /* Parser core - when encountering text, process appropriately. */ static const char *parse_value(cJSON *item, const char *value, const char **ep) { @@ -2400,3 +2260,238 @@ void cJSON_Minify(char *json) /* and null-terminate. */ *into = '\0'; } + +#ifndef STREAM_SIZE +#define STREAM_SIZE 256 +#endif + +struct StreamData +{ + char buffer[STREAM_SIZE]; + size_t offset; +}; + +static void StreamData_Init(StreamData *data); +static int stream_value(cJSON_Stream *stream, const cJSON *item, int depth); +static int stream_string(cJSON_Stream *stream, const char *str); +static int stream_number(cJSON_Stream *stream, const cJSON *item); +static int stream_array(cJSON_Stream *stream, const cJSON *item, int depth); +static int stream_object(cJSON_Stream *stream, const cJSON *item, int depth); + +void cJSON_StreamInit(cJSON_Stream *stream) +{ + memset(stream, 0, sizeof(cJSON_Stream)); +} + +void cJSON_StreamDeInit(cJSON_Stream *stream) +{ + /* Do nothing, reserved for later */ + (void)stream; +} + +int cJSON_PrintStream(cJSON_Stream *stream, const cJSON *item) +{ + int out; + StreamData data; + + StreamData_Init(&data); + + stream->data = &data; + + out = stream_value(stream, item, 0); + + return out; +} + +static void StreamData_Init(StreamData *data) +{ + memset(data, 0, sizeof(StreamData)); +} + +static int stream_value(cJSON_Stream *stream, const cJSON *item, int depth) +{ + int out = 0; + + if (!stream || !item) + { + return -1; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + out += stream_string(stream, "null"); + break; + case cJSON_False: + out += stream_string(stream, "false"); + break; + case cJSON_True: + out += stream_string(stream, "true"); + break; + case cJSON_Number: + out += stream_number(stream, item); + break; + case cJSON_String: + out += stream_string(stream, item->valuestring); + break; + case cJSON_Array: + out += stream_array(stream, item, depth); + break; + case cJSON_Object: + out += stream_object(stream, item, depth); + break; + } + + return out; +} + +static int stream_string(cJSON_Stream *stream, const char *str) +{ + size_t length = strlen(str); + + /* TODO Need to handle if length of string is larger than the buffer */ + + /* If the data being appended is larger than our buffer flush and reset */ + if (stream->data->offset + length + 1 > sizeof(stream->data->buffer)) + { + stream->cb(stream->data->buffer, stream->data->offset, stream->cb_data); + + StreamData_Init(stream->data); + } + + strcpy(stream->data->buffer + stream->data->offset, str); + stream->data->offset += length; + + return length; +} + +static int stream_number(cJSON_Stream *stream, const cJSON *item) +{ + double d; + char buffer[64]; + + d = item->valuedouble; + + if (d == 0) + { + sprintf(buffer, "0"); + } + else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) + { + sprintf(buffer, "%d", item->valueint); + } + else + { + if ((d * 0) != 0) + { + sprintf(buffer, "null"); + } + else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) + { + sprintf(buffer, "%.0f", d); + } + else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) + { + sprintf(buffer, "%e", d); + } + else + { + sprintf(buffer, "%f", d); + } + } + + return stream_string(stream, buffer); +} + +static int stream_array(cJSON_Stream *stream, const cJSON *item, int depth) +{ + int out = 0; + + /* Place the opening brace */ + out += stream_string(stream, "["); + + /* Iterate through all children */ + for (item = item->child; item != NULL; item = item->next) + { + /* Print the item value */ + out += stream_value(stream, item, depth + 1); + + if (item->next) + { + /* Add coma for next item */ + out += stream_string(stream, ","); + } + + if (stream->fmt) + { + /* Format spacing */ + out += stream_string(stream, " "); + } + } + + /* Place the closing brace */ + out += stream_string(stream, "]"); + + return out; +} + +static int stream_object(cJSON_Stream *stream, const cJSON *item, int depth) +{ + int i; + int out = 0; + + out += stream_string(stream, "{"); + + if (stream->fmt) + { + /* Format newline */ + out += stream_string(stream, "\n"); + + /* Format indent */ + for (i = 0; i < depth; i++) + { + out += stream_string(stream, "\t"); + } + } + + /* Iterate through all children */ + for (item = item->child; item != NULL; item = item->next) + { + /* Print the item key */ + out += stream_string(stream, item->string); + + /* Separate key and value with colon */ + out += stream_string(stream, ":"); + + if (stream->fmt) + { + out += stream_string(stream, "\t"); + } + + /* Print the item value */ + out += stream_value(stream, item, depth); + + if (item->next) + { + /* Add coma for next item */ + out += stream_string(stream, ","); + } + + if (stream->fmt) + { + /* Format newline */ + out += stream_string(stream, "\n"); + + /* Format indent */ + for (i = 0; i < depth; i++) + { + out += stream_string(stream, "\t"); + } + } + } + + /* Place the closing brace */ + out += stream_string(stream, "}"); + + return out; +} diff --git a/cJSON.h b/cJSON.h index c4956fa4..3b85f9e2 100644 --- a/cJSON.h +++ b/cJSON.h @@ -67,8 +67,12 @@ typedef struct cJSON typedef int (*cjson_callback)(const char *ptr, size_t length, void *cb_data); +typedef struct StreamData StreamData; + typedef struct cJSON_Stream { + StreamData *data; + cjson_callback cb; void *cb_data; @@ -76,7 +80,7 @@ typedef struct cJSON_Stream } cJSON_Stream; void cJSON_StreamInit(cJSON_Stream *stream); -int cJSON_PrintStream(const cJSON_Stream *stream, const cJSON *item); +int cJSON_PrintStream(cJSON_Stream *stream, const cJSON *item); void cJSON_StreamDeInit(cJSON_Stream *stream); typedef struct cJSON_Hooks From d4b3b3009aa68074a2e7f9814fc3daa8d1274b2c Mon Sep 17 00:00:00 2001 From: Ian Mobley Date: Thu, 5 Jan 2017 14:41:04 -0800 Subject: [PATCH 3/5] Add stream_putc for simple character copy Fleshed out the stream_string to deal with escaped values Fixed tabs/spaces from vim default --- cJSON.c | 461 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 289 insertions(+), 172 deletions(-) diff --git a/cJSON.c b/cJSON.c index 8abef21f..2255335d 100644 --- a/cJSON.c +++ b/cJSON.c @@ -2267,231 +2267,348 @@ void cJSON_Minify(char *json) struct StreamData { - char buffer[STREAM_SIZE]; - size_t offset; + char buffer[STREAM_SIZE]; + size_t offset; }; static void StreamData_Init(StreamData *data); static int stream_value(cJSON_Stream *stream, const cJSON *item, int depth); -static int stream_string(cJSON_Stream *stream, const char *str); static int stream_number(cJSON_Stream *stream, const cJSON *item); +static int stream_string(cJSON_Stream *stream, const char *str); static int stream_array(cJSON_Stream *stream, const cJSON *item, int depth); static int stream_object(cJSON_Stream *stream, const cJSON *item, int depth); +static int stream_puts(cJSON_Stream *stream, const char *str); +static int stream_putc(cJSON_Stream *stream, char c); +static int stream_flush(cJSON_Stream *stream); void cJSON_StreamInit(cJSON_Stream *stream) { - memset(stream, 0, sizeof(cJSON_Stream)); + memset(stream, 0, sizeof(cJSON_Stream)); } void cJSON_StreamDeInit(cJSON_Stream *stream) { - /* Do nothing, reserved for later */ - (void)stream; + /* Do nothing, reserved for later */ + (void)stream; } int cJSON_PrintStream(cJSON_Stream *stream, const cJSON *item) { - int out; - StreamData data; + int out; + StreamData data; - StreamData_Init(&data); + StreamData_Init(&data); - stream->data = &data; + stream->data = &data; - out = stream_value(stream, item, 0); + out = stream_value(stream, item, 0); - return out; + out += stream_flush(stream); + + return out; } static void StreamData_Init(StreamData *data) { - memset(data, 0, sizeof(StreamData)); + memset(data, 0, sizeof(StreamData)); } static int stream_value(cJSON_Stream *stream, const cJSON *item, int depth) { - int out = 0; - - if (!stream || !item) - { - return -1; - } - - switch ((item->type) & 0xFF) - { - case cJSON_NULL: - out += stream_string(stream, "null"); - break; - case cJSON_False: - out += stream_string(stream, "false"); - break; - case cJSON_True: - out += stream_string(stream, "true"); - break; - case cJSON_Number: - out += stream_number(stream, item); - break; - case cJSON_String: - out += stream_string(stream, item->valuestring); - break; - case cJSON_Array: - out += stream_array(stream, item, depth); - break; - case cJSON_Object: - out += stream_object(stream, item, depth); - break; - } - - return out; + int out = 0; + + if (!stream || !item) + { + return -1; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + out += stream_puts(stream, "null"); + break; + case cJSON_False: + out += stream_puts(stream, "false"); + break; + case cJSON_True: + out += stream_puts(stream, "true"); + break; + case cJSON_Number: + out += stream_number(stream, item); + break; + case cJSON_String: + out += stream_string(stream, item->valuestring); + break; + case cJSON_Array: + out += stream_array(stream, item, depth); + break; + case cJSON_Object: + out += stream_object(stream, item, depth); + break; + } + + return out; } -static int stream_string(cJSON_Stream *stream, const char *str) +static int stream_number(cJSON_Stream *stream, const cJSON *item) { - size_t length = strlen(str); + double d; + char buffer[64]; + + d = item->valuedouble; + + if (d == 0) + { + sprintf(buffer, "0"); + } + else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) + { + sprintf(buffer, "%d", item->valueint); + } + else + { + if ((d * 0) != 0) + { + sprintf(buffer, "null"); + } + else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) + { + sprintf(buffer, "%.0f", d); + } + else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) + { + sprintf(buffer, "%e", d); + } + else + { + sprintf(buffer, "%f", d); + } + } - /* TODO Need to handle if length of string is larger than the buffer */ + return stream_puts(stream, buffer); +} - /* If the data being appended is larger than our buffer flush and reset */ - if (stream->data->offset + length + 1 > sizeof(stream->data->buffer)) - { - stream->cb(stream->data->buffer, stream->data->offset, stream->cb_data); +static cjbool escaped_str(const char *str) +{ + for (; *str; str++) + { + if (0 < *str && *str < 32) + return true; - StreamData_Init(stream->data); - } + if (*str == '\"') + return true; - strcpy(stream->data->buffer + stream->data->offset, str); - stream->data->offset += length; + if (*str == '\\') + return true; + } - return length; + return false; } -static int stream_number(cJSON_Stream *stream, const cJSON *item) +static int stream_string(cJSON_Stream *stream, const char *str) { - double d; - char buffer[64]; - - d = item->valuedouble; - - if (d == 0) - { - sprintf(buffer, "0"); - } - else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) - { - sprintf(buffer, "%d", item->valueint); - } - else - { - if ((d * 0) != 0) - { - sprintf(buffer, "null"); - } - else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) - { - sprintf(buffer, "%.0f", d); - } - else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9)) - { - sprintf(buffer, "%e", d); - } - else - { - sprintf(buffer, "%f", d); - } - } - - return stream_string(stream, buffer); + int out = 0; + char buffer[8]; + + out += stream_putc(stream, '"'); + + if (escaped_str(str)) + { + for (; *str; str++) + { + if (*str < 32 || *str == '\"' || *str == '\\') + { + stream_putc(stream, '\\'); + + switch (*str) + { + case '\\': + case '\"': + stream_putc(stream, *str); + break; + case '\b': + stream_putc(stream, 'b'); + break; + case '\f': + stream_putc(stream, 'f'); + break; + case '\n': + stream_putc(stream, 'n'); + break; + case '\r': + stream_putc(stream, 'r'); + break; + case '\t': + stream_putc(stream, 't'); + break; + default: + /* Escape and print as unicode codepoint */ + sprintf(buffer, "u%04x", *str); + stream_puts(stream, buffer); + + str += 4; + break; + } + } + else + { + stream_putc(stream, *str); + } + } + } + else + { + out += stream_puts(stream, str); + } + + out += stream_putc(stream, '"'); + + return out; } static int stream_array(cJSON_Stream *stream, const cJSON *item, int depth) { - int out = 0; + int out = 0; - /* Place the opening brace */ - out += stream_string(stream, "["); + /* Place the opening brace */ + out += stream_putc(stream, '['); - /* Iterate through all children */ - for (item = item->child; item != NULL; item = item->next) - { - /* Print the item value */ - out += stream_value(stream, item, depth + 1); + /* Iterate through all children */ + for (item = item->child; item != NULL; item = item->next) + { + /* Print the item value */ + out += stream_value(stream, item, depth + 1); - if (item->next) - { - /* Add coma for next item */ - out += stream_string(stream, ","); - } + if (item->next) + { + /* Add coma for next item */ + out += stream_putc(stream, ','); + } - if (stream->fmt) - { - /* Format spacing */ - out += stream_string(stream, " "); - } - } + if (stream->fmt) + { + /* Format spacing */ + out += stream_putc(stream, ' '); + } + } - /* Place the closing brace */ - out += stream_string(stream, "]"); + /* Place the closing brace */ + out += stream_putc(stream, ']'); - return out; + return out; } static int stream_object(cJSON_Stream *stream, const cJSON *item, int depth) { - int i; - int out = 0; - - out += stream_string(stream, "{"); - - if (stream->fmt) - { - /* Format newline */ - out += stream_string(stream, "\n"); - - /* Format indent */ - for (i = 0; i < depth; i++) - { - out += stream_string(stream, "\t"); - } - } - - /* Iterate through all children */ - for (item = item->child; item != NULL; item = item->next) - { - /* Print the item key */ - out += stream_string(stream, item->string); - - /* Separate key and value with colon */ - out += stream_string(stream, ":"); - - if (stream->fmt) - { - out += stream_string(stream, "\t"); - } - - /* Print the item value */ - out += stream_value(stream, item, depth); - - if (item->next) - { - /* Add coma for next item */ - out += stream_string(stream, ","); - } - - if (stream->fmt) - { - /* Format newline */ - out += stream_string(stream, "\n"); - - /* Format indent */ - for (i = 0; i < depth; i++) - { - out += stream_string(stream, "\t"); - } - } - } - - /* Place the closing brace */ - out += stream_string(stream, "}"); - - return out; + int i; + int out = 0; + + out += stream_putc(stream, '{'); + + /* Iterate through all children */ + for (item = item->child; item != NULL; item = item->next) + { + if (stream->fmt) + { + /* Format newline */ + out += stream_putc(stream, '\n'); + + /* Format indent */ + for (i = 0; i < depth + 1; i++) + { + out += stream_putc(stream, '\t'); + } + } + + /* Print the item key */ + out += stream_string(stream, item->string); + + /* Separate key and value with colon */ + out += stream_putc(stream, ':'); + + if (stream->fmt) + { + out += stream_putc(stream, '\t'); + } + + /* Print the item value */ + out += stream_value(stream, item, depth + 1); + + if (item->next) + { + /* Add comma for next item */ + out += stream_putc(stream, ','); + } + } + + if (stream->fmt) + { + /* Format newline */ + out += stream_putc(stream, '\n'); + + /* Format indent */ + for (i = 0; i < depth; i++) + { + out += stream_putc(stream, '\t'); + } + } + + /* Place the closing brace */ + out += stream_putc(stream, '}'); + + return out; +} + +static int stream_puts(cJSON_Stream *stream, const char *str) +{ + int out = 0; + size_t d; + size_t str_length = strlen(str); + + while (sizeof(stream->data->buffer) - stream->data->offset <= str_length) + { + /* Fill the remaining stream buffer */ + d = sizeof(stream->data->buffer) - stream->data->offset - 1; + + strncpy(stream->data->buffer + stream->data->offset, str, d); + stream->data->offset += d; + + out += stream_flush(stream); + + str += d; + str_length -= d; + } + + strcpy(stream->data->buffer + stream->data->offset, str); + stream->data->offset += str_length; + + return out; +} + +static int stream_putc(cJSON_Stream *stream, char c) +{ + int out = 0; + + if (stream->data->offset == sizeof(stream->data->buffer) - 1) + { + out = stream_flush(stream); + } + + stream->data->buffer[stream->data->offset++] = c; + + return out; +} + +static int stream_flush(cJSON_Stream *stream) +{ + int out = stream->data->offset; + + if (stream->cb(stream->data->buffer, stream->data->offset, stream->cb_data)) + { + /* TODO Need to handle an error here */ + } + + StreamData_Init(stream->data); + + return out; } From 5605da167d4ee217f4560bb6bbdfc792fe0407bf Mon Sep 17 00:00:00 2001 From: Ian Mobley Date: Sat, 7 Jan 2017 11:56:18 -0800 Subject: [PATCH 4/5] Add in more comments and a way to bail on error If the stream->error value is non zero, most functions will try to return immediately. Currently the error is only set from the user provided callback, but can easily be modified elsewhere in future use. --- cJSON.c | 129 +++++++++++++++++++++++++++++++++++++++----------------- cJSON.h | 21 +++++++-- 2 files changed, 109 insertions(+), 41 deletions(-) diff --git a/cJSON.c b/cJSON.c index 2255335d..0d11fd77 100644 --- a/cJSON.c +++ b/cJSON.c @@ -2271,7 +2271,6 @@ struct StreamData size_t offset; }; -static void StreamData_Init(StreamData *data); static int stream_value(cJSON_Stream *stream, const cJSON *item, int depth); static int stream_number(cJSON_Stream *stream, const cJSON *item); static int stream_string(cJSON_Stream *stream, const char *str); @@ -2297,57 +2296,55 @@ int cJSON_PrintStream(cJSON_Stream *stream, const cJSON *item) int out; StreamData data; - StreamData_Init(&data); + if (!stream || !item || !stream->cb) + { + return -1; + } + /* Initialize and assign the stream data */ + memset(&data, 0, sizeof(StreamData)); stream->data = &data; + stream->error = 0; out = stream_value(stream, item, 0); - out += stream_flush(stream); + if (!stream->error) + { + /* Flush the stream buffer */ + out += stream_flush(stream); + } return out; } -static void StreamData_Init(StreamData *data) -{ - memset(data, 0, sizeof(StreamData)); -} - static int stream_value(cJSON_Stream *stream, const cJSON *item, int depth) { - int out = 0; - - if (!stream || !item) + /* Bail ASAP if error */ + if (stream->error) { - return -1; + return 0; } switch ((item->type) & 0xFF) { case cJSON_NULL: - out += stream_puts(stream, "null"); - break; + return stream_puts(stream, "null"); case cJSON_False: - out += stream_puts(stream, "false"); - break; + return stream_puts(stream, "false"); case cJSON_True: - out += stream_puts(stream, "true"); - break; + return stream_puts(stream, "true"); case cJSON_Number: - out += stream_number(stream, item); - break; + return stream_number(stream, item); case cJSON_String: - out += stream_string(stream, item->valuestring); - break; + return stream_string(stream, item->valuestring); case cJSON_Array: - out += stream_array(stream, item, depth); - break; + return stream_array(stream, item, depth); case cJSON_Object: - out += stream_object(stream, item, depth); - break; + return stream_object(stream, item, depth); + default: + stream->error = 1; + return 0; } - - return out; } static int stream_number(cJSON_Stream *stream, const cJSON *item) @@ -2355,11 +2352,17 @@ static int stream_number(cJSON_Stream *stream, const cJSON *item) double d; char buffer[64]; + /* Bail ASAP if error */ + if (stream->error) + { + return 0; + } + d = item->valuedouble; if (d == 0) { - sprintf(buffer, "0"); + strcpy(buffer, "0"); } else if ((fabs(((double)item->valueint) - d) <= DBL_EPSILON) && (d <= INT_MAX) && (d >= INT_MIN)) { @@ -2369,7 +2372,7 @@ static int stream_number(cJSON_Stream *stream, const cJSON *item) { if ((d * 0) != 0) { - sprintf(buffer, "null"); + strcpy(buffer, "null"); } else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60)) { @@ -2410,6 +2413,12 @@ static int stream_string(cJSON_Stream *stream, const char *str) int out = 0; char buffer[8]; + /* Bail ASAP if error */ + if (stream->error) + { + return 0; + } + out += stream_putc(stream, '"'); if (escaped_str(str)) @@ -2470,6 +2479,12 @@ static int stream_array(cJSON_Stream *stream, const cJSON *item, int depth) { int out = 0; + /* Bail ASAP if error */ + if (stream->error) + { + return 0; + } + /* Place the opening brace */ out += stream_putc(stream, '['); @@ -2503,6 +2518,13 @@ static int stream_object(cJSON_Stream *stream, const cJSON *item, int depth) int i; int out = 0; + /* Bail ASAP if error */ + if (stream->error) + { + return 0; + } + + /* Place the opening brace */ out += stream_putc(stream, '{'); /* Iterate through all children */ @@ -2565,14 +2587,22 @@ static int stream_puts(cJSON_Stream *stream, const char *str) size_t d; size_t str_length = strlen(str); - while (sizeof(stream->data->buffer) - stream->data->offset <= str_length) + /* Bail ASAP if error */ + if (stream->error) + { + return 0; + } + + /* If there is not enough room in the buffer, flush it out */ + while ((sizeof(stream->data->buffer) - stream->data->offset) <= str_length) { - /* Fill the remaining stream buffer */ + /* Fill the remaining stream buffer if possible */ d = sizeof(stream->data->buffer) - stream->data->offset - 1; strncpy(stream->data->buffer + stream->data->offset, str, d); stream->data->offset += d; + /* Flush stream and increment str */ out += stream_flush(stream); str += d; @@ -2589,11 +2619,19 @@ static int stream_putc(cJSON_Stream *stream, char c) { int out = 0; - if (stream->data->offset == sizeof(stream->data->buffer) - 1) + /* Bail ASAP if error */ + if (stream->error) + { + return 0; + } + + /* If the buffer is full, flush it out */ + if (stream->data->offset == (sizeof(stream->data->buffer) - 1)) { out = stream_flush(stream); } + /* Append the character and increment the stream offset */ stream->data->buffer[stream->data->offset++] = c; return out; @@ -2601,14 +2639,29 @@ static int stream_putc(cJSON_Stream *stream, char c) static int stream_flush(cJSON_Stream *stream) { - int out = stream->data->offset; + int ret = 0; - if (stream->cb(stream->data->buffer, stream->data->offset, stream->cb_data)) + /* Bail ASAP if error */ + if (stream->error) { - /* TODO Need to handle an error here */ + return 0; } - StreamData_Init(stream->data); + /* Only call the callback if there is data */ + if (stream->data->offset) + { + ret = stream->cb(stream->data->buffer, + stream->data->offset, + stream->cb_data); + + if (ret) + { + stream->error = ret; + } + + ret = stream->data->offset; + memset(stream->data, 0, sizeof(StreamData)); + } - return out; + return ret; } diff --git a/cJSON.h b/cJSON.h index 3b85f9e2..1bad5fc5 100644 --- a/cJSON.h +++ b/cJSON.h @@ -65,24 +65,39 @@ typedef struct cJSON char *string; } cJSON; -typedef int (*cjson_callback)(const char *ptr, size_t length, void *cb_data); +/* + * Callback should return non zero if there is an error. + */ +typedef int (*cJSON_Callback)(const char *ptr, size_t length, void *cb_data); +/* Forward declare StreamData */ typedef struct StreamData StreamData; typedef struct cJSON_Stream { + /* Stream private data */ StreamData *data; - cjson_callback cb; + /* User defined callback function */ + cJSON_Callback cb; + /* User defined data for callback function */ void *cb_data; + /* Whether or not to format the JSON output */ int fmt; + + /* Non zero if error */ + int error; } cJSON_Stream; +/* Initializaes a cJSON_Stream */ void cJSON_StreamInit(cJSON_Stream *stream); -int cJSON_PrintStream(cJSON_Stream *stream, const cJSON *item); + +/* Deinitializes a cJSON_Stream */ void cJSON_StreamDeInit(cJSON_Stream *stream); +int cJSON_PrintStream(cJSON_Stream *stream, const cJSON *item); + typedef struct cJSON_Hooks { void *(*malloc_fn)(size_t sz); From 3b475d678e01e1f2175a107667a6a37717497cc4 Mon Sep 17 00:00:00 2001 From: Ian Mobley Date: Sun, 8 Jan 2017 14:58:55 -0800 Subject: [PATCH 5/5] Forgot to add character out in stream_string --- cJSON.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cJSON.c b/cJSON.c index 0d11fd77..22280fbc 100644 --- a/cJSON.c +++ b/cJSON.c @@ -2427,33 +2427,33 @@ static int stream_string(cJSON_Stream *stream, const char *str) { if (*str < 32 || *str == '\"' || *str == '\\') { - stream_putc(stream, '\\'); + out += stream_putc(stream, '\\'); switch (*str) { case '\\': case '\"': - stream_putc(stream, *str); + out += stream_putc(stream, *str); break; case '\b': - stream_putc(stream, 'b'); + out += stream_putc(stream, 'b'); break; case '\f': - stream_putc(stream, 'f'); + out += stream_putc(stream, 'f'); break; case '\n': - stream_putc(stream, 'n'); + out += stream_putc(stream, 'n'); break; case '\r': - stream_putc(stream, 'r'); + out += stream_putc(stream, 'r'); break; case '\t': - stream_putc(stream, 't'); + out += stream_putc(stream, 't'); break; default: /* Escape and print as unicode codepoint */ sprintf(buffer, "u%04x", *str); - stream_puts(stream, buffer); + out += stream_puts(stream, buffer); str += 4; break; @@ -2461,7 +2461,7 @@ static int stream_string(cJSON_Stream *stream, const char *str) } else { - stream_putc(stream, *str); + out += stream_putc(stream, *str); } } }