From 3c0eac3cba8bde03040dab6f6b0022020cec4192 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Wed, 3 May 2017 15:57:18 -0400 Subject: [PATCH] lib: json: escape strings in-place Currently, json_escape() allocates a temporary buffer on the stack that's the size of the string being escaped. Stack space is precious and longer JSON strings can run into the hundreds of bytes, so re-implement this routine to escape in place. Signed-off-by: Marti Bolivar --- lib/json/json.c | 96 ++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/lib/json/json.c b/lib/json/json.c index 8b8eef06c1..41faaf7b3e 100644 --- a/lib/json/json.c +++ b/lib/json/json.c @@ -640,49 +640,6 @@ static int json_escape_internal(const char *str, return ret; } -struct appender { - char *buffer; - size_t used; - size_t size; -}; - -static int append_bytes_to_buf(const u8_t *bytes, size_t len, void *data) -{ - struct appender *appender = data; - - if (len > appender->size - appender->used) { - return -ENOMEM; - } - - memcpy(appender->buffer + appender->used, bytes, len); - appender->used += len; - appender->buffer[appender->used] = '\0'; - - return 0; -} - -static int json_escape_buf(char *str, size_t *len, size_t buf_size) -{ - char tmp_buf[buf_size + 1]; - struct appender appender = { .buffer = tmp_buf, .size = buf_size }; - int ret; - - ret = json_escape_internal(str, append_bytes_to_buf, &appender); - if (ret < 0) { - return ret; - } - - ret = append_bytes_to_buf("", 1, &appender); - if (!ret) { - memcpy(str, tmp_buf, appender.size); - if (len) { - *len = appender.size; - } - } - - return ret; -} - size_t json_calc_escaped_len(const char *str, size_t len) { size_t escaped_len = len; @@ -699,13 +656,13 @@ size_t json_calc_escaped_len(const char *str, size_t len) ssize_t json_escape(char *str, size_t *len, size_t buf_size) { - size_t escaped_len; - - escaped_len = json_calc_escaped_len(str, *len); + char *next; /* Points after next character to escape. */ + char *dest; /* Points after next place to write escaped character. */ + size_t escaped_len = json_calc_escaped_len(str, *len); if (escaped_len == *len) { - /* If no escape is necessary, don't bother using up temporary - * stack space to copy the string. + /* + * If no escape is necessary, there is nothing to do. */ return 0; } @@ -714,7 +671,27 @@ ssize_t json_escape(char *str, size_t *len, size_t buf_size) return -ENOMEM; } - return json_escape_buf(str, len, escaped_len); + /* + * By walking backwards in the buffer from the end positions + * of both the original and escaped strings, we avoid using + * extra space. Characters in the original string are + * overwritten only after they have already been escaped. + */ + str[escaped_len] = '\0'; + for (next = &str[*len], dest = &str[escaped_len]; next != str;) { + char next_c = *(--next); + char escape = escape_as(next_c); + + if (escape) { + *(--dest) = escape; + *(--dest) = '\\'; + } else { + *(--dest) = next_c; + } + } + *len = escaped_len; + + return 0; } static int encode(const struct json_obj_descr *descr, const void *val, @@ -881,6 +858,27 @@ int json_obj_encode(const struct json_obj_descr *descr, size_t descr_len, return append_bytes("", 1, data); } +struct appender { + char *buffer; + size_t used; + size_t size; +}; + +static int append_bytes_to_buf(const u8_t *bytes, size_t len, void *data) +{ + struct appender *appender = data; + + if (len > appender->size - appender->used) { + return -ENOMEM; + } + + memcpy(appender->buffer + appender->used, bytes, len); + appender->used += len; + appender->buffer[appender->used] = '\0'; + + return 0; +} + int json_obj_encode_buf(const struct json_obj_descr *descr, size_t descr_len, const void *val, char *buffer, size_t buf_size) {