diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/lib/json | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/lib/json')
-rw-r--r-- | src/spdk/lib/json/Makefile | 45 | ||||
-rw-r--r-- | src/spdk/lib/json/json_parse.c | 668 | ||||
-rw-r--r-- | src/spdk/lib/json/json_util.c | 653 | ||||
-rw-r--r-- | src/spdk/lib/json/json_write.c | 687 | ||||
-rw-r--r-- | src/spdk/lib/json/spdk_json.map | 67 |
5 files changed, 2120 insertions, 0 deletions
diff --git a/src/spdk/lib/json/Makefile b/src/spdk/lib/json/Makefile new file mode 100644 index 000000000..91cb8868f --- /dev/null +++ b/src/spdk/lib/json/Makefile @@ -0,0 +1,45 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +SO_VER := 2 +SO_MINOR := 0 + +C_SRCS = json_parse.c json_util.c json_write.c +LIBNAME = json + +SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_json.map) + +include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk diff --git a/src/spdk/lib/json/json_parse.c b/src/spdk/lib/json/json_parse.c new file mode 100644 index 000000000..8639d5ff8 --- /dev/null +++ b/src/spdk/lib/json/json_parse.c @@ -0,0 +1,668 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/json.h" + +#include "spdk_internal/utf.h" + +#define SPDK_JSON_MAX_NESTING_DEPTH 64 + +static int +hex_value(uint8_t c) +{ +#define V(x, y) [x] = y + 1 + static const int8_t val[256] = { + V('0', 0), V('1', 1), V('2', 2), V('3', 3), V('4', 4), + V('5', 5), V('6', 6), V('7', 7), V('8', 8), V('9', 9), + V('A', 0xA), V('B', 0xB), V('C', 0xC), V('D', 0xD), V('E', 0xE), V('F', 0xF), + V('a', 0xA), V('b', 0xB), V('c', 0xC), V('d', 0xD), V('e', 0xE), V('f', 0xF), + }; +#undef V + + return val[c] - 1; +} + +static int +json_decode_string_escape_unicode(uint8_t **strp, uint8_t *buf_end, uint8_t *out) +{ + uint8_t *str = *strp; + int v0, v1, v2, v3; + uint32_t val; + uint32_t surrogate_high = 0; + int rc; +decode: + /* \uXXXX */ + assert(buf_end > str); + + if (*str++ != '\\') { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if (*str++ != 'u') { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v3 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v2 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v1 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + if ((v0 = hex_value(*str++)) < 0) { return SPDK_JSON_PARSE_INVALID; } + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + + val = v0 | (v1 << 4) | (v2 << 8) | (v3 << 12); + + if (surrogate_high) { + /* We already parsed the high surrogate, so this should be the low part. */ + if (!utf16_valid_surrogate_low(val)) { + return SPDK_JSON_PARSE_INVALID; + } + + /* Convert UTF-16 surrogate pair into codepoint and fall through to utf8_encode. */ + val = utf16_decode_surrogate_pair(surrogate_high, val); + } else if (utf16_valid_surrogate_high(val)) { + surrogate_high = val; + + /* + * We parsed a \uXXXX sequence that decoded to the first half of a + * UTF-16 surrogate pair, so it must be immediately followed by another + * \uXXXX escape. + * + * Loop around to get the low half of the surrogate pair. + */ + if (buf_end == str) { return SPDK_JSON_PARSE_INCOMPLETE; } + goto decode; + } else if (utf16_valid_surrogate_low(val)) { + /* + * We found the second half of surrogate pair without the first half; + * this is an invalid encoding. + */ + return SPDK_JSON_PARSE_INVALID; + } + + /* + * Convert Unicode escape (or surrogate pair) to UTF-8 in place. + * + * This is safe (will not write beyond the buffer) because the \uXXXX sequence is 6 bytes + * (or 12 bytes for surrogate pairs), and the longest possible UTF-8 encoding of a + * single codepoint is 4 bytes. + */ + if (out) { + rc = utf8_encode_unsafe(out, val); + } else { + rc = utf8_codepoint_len(val); + } + if (rc < 0) { + return SPDK_JSON_PARSE_INVALID; + } + + *strp = str; /* update input pointer */ + return rc; /* return number of bytes decoded */ +} + +static int +json_decode_string_escape_twochar(uint8_t **strp, uint8_t *buf_end, uint8_t *out) +{ + static const uint8_t escapes[256] = { + ['b'] = '\b', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['t'] = '\t', + ['/'] = '/', + ['"'] = '"', + ['\\'] = '\\', + }; + uint8_t *str = *strp; + uint8_t c; + + assert(buf_end > str); + if (buf_end - str < 2) { + return SPDK_JSON_PARSE_INCOMPLETE; + } + + assert(str[0] == '\\'); + + c = escapes[str[1]]; + if (c) { + if (out) { + *out = c; + } + *strp += 2; /* consumed two bytes */ + return 1; /* produced one byte */ + } + + return SPDK_JSON_PARSE_INVALID; +} + +/* + * Decode JSON string backslash escape. + * \param strp pointer to pointer to first character of escape (the backslash). + * *strp is also advanced to indicate how much input was consumed. + * + * \return Number of bytes appended to out + */ +static int +json_decode_string_escape(uint8_t **strp, uint8_t *buf_end, uint8_t *out) +{ + int rc; + + rc = json_decode_string_escape_twochar(strp, buf_end, out); + if (rc > 0) { + return rc; + } + + return json_decode_string_escape_unicode(strp, buf_end, out); +} + +/* + * Decode JSON string in place. + * + * \param str_start Pointer to the beginning of the string (the opening " character). + * + * \return Number of bytes in decoded string (beginning from start). + */ +static int +json_decode_string(uint8_t *str_start, uint8_t *buf_end, uint8_t **str_end, uint32_t flags) +{ + uint8_t *str = str_start; + uint8_t *out = str_start + 1; /* Decode string in place (skip the initial quote) */ + int rc; + + if (buf_end - str_start < 2) { + /* + * Shortest valid string (the empty string) is two bytes (""), + * so this can't possibly be valid + */ + *str_end = str; + return SPDK_JSON_PARSE_INCOMPLETE; + } + + if (*str++ != '"') { + *str_end = str; + return SPDK_JSON_PARSE_INVALID; + } + + while (str < buf_end) { + if (str[0] == '"') { + /* + * End of string. + * Update str_end to point at next input byte and return output length. + */ + *str_end = str + 1; + return out - str_start - 1; + } else if (str[0] == '\\') { + rc = json_decode_string_escape(&str, buf_end, + flags & SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE ? out : NULL); + assert(rc != 0); + if (rc < 0) { + *str_end = str; + return rc; + } + out += rc; + } else if (str[0] <= 0x1f) { + /* control characters must be escaped */ + *str_end = str; + return SPDK_JSON_PARSE_INVALID; + } else { + rc = utf8_valid(str, buf_end); + if (rc == 0) { + *str_end = str; + return SPDK_JSON_PARSE_INCOMPLETE; + } else if (rc < 0) { + *str_end = str; + return SPDK_JSON_PARSE_INVALID; + } + + if (out && out != str && (flags & SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE)) { + memmove(out, str, rc); + } + out += rc; + str += rc; + } + } + + /* If execution gets here, we ran out of buffer. */ + *str_end = str; + return SPDK_JSON_PARSE_INCOMPLETE; +} + +static int +json_valid_number(uint8_t *start, uint8_t *buf_end) +{ + uint8_t *p = start; + uint8_t c; + + if (p >= buf_end) { return -1; } + + c = *p++; + if (c >= '1' && c <= '9') { goto num_int_digits; } + if (c == '0') { goto num_frac_or_exp; } + if (c == '-') { goto num_int_first_digit; } + p--; + goto done_invalid; + +num_int_first_digit: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c == '0') { goto num_frac_or_exp; } + if (c >= '1' && c <= '9') { goto num_int_digits; } + p--; + } + goto done_invalid; + +num_int_digits: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_int_digits; } + if (c == '.') { goto num_frac_first_digit; } + if (c == 'e' || c == 'E') { goto num_exp_sign; } + p--; + } + goto done_valid; + +num_frac_or_exp: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c == '.') { goto num_frac_first_digit; } + if (c == 'e' || c == 'E') { goto num_exp_sign; } + p--; + } + goto done_valid; + +num_frac_first_digit: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_frac_digits; } + p--; + } + goto done_invalid; + +num_frac_digits: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_frac_digits; } + if (c == 'e' || c == 'E') { goto num_exp_sign; } + p--; + } + goto done_valid; + +num_exp_sign: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_exp_digits; } + if (c == '-' || c == '+') { goto num_exp_first_digit; } + p--; + } + goto done_invalid; + +num_exp_first_digit: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_exp_digits; } + p--; + } + goto done_invalid; + +num_exp_digits: + if (spdk_likely(p != buf_end)) { + c = *p++; + if (c >= '0' && c <= '9') { goto num_exp_digits; } + p--; + } + goto done_valid; + +done_valid: + /* Valid end state */ + return p - start; + +done_invalid: + /* Invalid end state */ + if (p == buf_end) { + /* Hit the end of the buffer - the stream is incomplete. */ + return SPDK_JSON_PARSE_INCOMPLETE; + } + + /* Found an invalid character in an invalid end state */ + return SPDK_JSON_PARSE_INVALID; +} + +static int +json_valid_comment(const uint8_t *start, const uint8_t *buf_end) +{ + const uint8_t *p = start; + bool multiline; + + assert(buf_end > p); + if (buf_end - p < 2) { + return SPDK_JSON_PARSE_INCOMPLETE; + } + + if (p[0] != '/') { + return SPDK_JSON_PARSE_INVALID; + } + if (p[1] == '*') { + multiline = true; + } else if (p[1] == '/') { + multiline = false; + } else { + return SPDK_JSON_PARSE_INVALID; + } + p += 2; + + if (multiline) { + while (p != buf_end - 1) { + if (p[0] == '*' && p[1] == '/') { + /* Include the terminating star and slash in the comment */ + return p - start + 2; + } + p++; + } + } else { + while (p != buf_end) { + if (*p == '\r' || *p == '\n') { + /* Do not include the line terminator in the comment */ + return p - start; + } + p++; + } + } + + return SPDK_JSON_PARSE_INCOMPLETE; +} + +struct json_literal { + enum spdk_json_val_type type; + uint32_t len; + uint8_t str[8]; +}; + +/* + * JSON only defines 3 possible literals; they can be uniquely identified by bits + * 3 and 4 of the first character: + * 'f' = 0b11[00]110 + * 'n' = 0b11[01]110 + * 't' = 0b11[10]100 + * These two bits can be used as an index into the g_json_literals array. + */ +static const struct json_literal g_json_literals[] = { + {SPDK_JSON_VAL_FALSE, 5, "false"}, + {SPDK_JSON_VAL_NULL, 4, "null"}, + {SPDK_JSON_VAL_TRUE, 4, "true"}, + {} +}; + +static int +match_literal(const uint8_t *start, const uint8_t *end, const uint8_t *literal, size_t len) +{ + assert(end >= start); + if ((size_t)(end - start) < len) { + return SPDK_JSON_PARSE_INCOMPLETE; + } + + if (memcmp(start, literal, len) != 0) { + return SPDK_JSON_PARSE_INVALID; + } + + return len; +} + +ssize_t +spdk_json_parse(void *json, size_t size, struct spdk_json_val *values, size_t num_values, + void **end, uint32_t flags) +{ + uint8_t *json_end = json + size; + enum spdk_json_val_type containers[SPDK_JSON_MAX_NESTING_DEPTH]; + size_t con_value[SPDK_JSON_MAX_NESTING_DEPTH]; + enum spdk_json_val_type con_type = SPDK_JSON_VAL_INVALID; + bool trailing_comma = false; + size_t depth = 0; /* index into containers */ + size_t cur_value = 0; /* index into values */ + size_t con_start_value; + uint8_t *data = json; + uint8_t *new_data; + int rc = 0; + const struct json_literal *lit; + enum { + STATE_VALUE, /* initial state */ + STATE_VALUE_SEPARATOR, /* value separator (comma) */ + STATE_NAME, /* "name": value */ + STATE_NAME_SEPARATOR, /* colon */ + STATE_END, /* parsed the complete value, so only whitespace is valid */ + } state = STATE_VALUE; + +#define ADD_VALUE(t, val_start_ptr, val_end_ptr) \ + if (values && cur_value < num_values) { \ + values[cur_value].type = t; \ + values[cur_value].start = val_start_ptr; \ + values[cur_value].len = val_end_ptr - val_start_ptr; \ + } \ + cur_value++ + + while (data < json_end) { + uint8_t c = *data; + + switch (c) { + case ' ': + case '\t': + case '\r': + case '\n': + /* Whitespace is allowed between any tokens. */ + data++; + break; + + case 't': + case 'f': + case 'n': + /* true, false, or null */ + if (state != STATE_VALUE) { goto done_invalid; } + lit = &g_json_literals[(c >> 3) & 3]; /* See comment above g_json_literals[] */ + assert(lit->str[0] == c); + rc = match_literal(data, json_end, lit->str, lit->len); + if (rc < 0) { goto done_rc; } + ADD_VALUE(lit->type, data, data + rc); + data += rc; + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + trailing_comma = false; + break; + + case '"': + if (state != STATE_VALUE && state != STATE_NAME) { goto done_invalid; } + rc = json_decode_string(data, json_end, &new_data, flags); + if (rc < 0) { + data = new_data; + goto done_rc; + } + /* + * Start is data + 1 to skip initial quote. + * Length is data + rc - 1 to skip both quotes. + */ + ADD_VALUE(state == STATE_VALUE ? SPDK_JSON_VAL_STRING : SPDK_JSON_VAL_NAME, + data + 1, data + rc - 1); + data = new_data; + if (state == STATE_NAME) { + state = STATE_NAME_SEPARATOR; + } else { + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + } + trailing_comma = false; + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (state != STATE_VALUE) { goto done_invalid; } + rc = json_valid_number(data, json_end); + if (rc < 0) { goto done_rc; } + ADD_VALUE(SPDK_JSON_VAL_NUMBER, data, data + rc); + data += rc; + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + trailing_comma = false; + break; + + case '{': + case '[': + if (state != STATE_VALUE) { goto done_invalid; } + if (depth == SPDK_JSON_MAX_NESTING_DEPTH) { + rc = SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED; + goto done_rc; + } + if (c == '{') { + con_type = SPDK_JSON_VAL_OBJECT_BEGIN; + state = STATE_NAME; + } else { + con_type = SPDK_JSON_VAL_ARRAY_BEGIN; + state = STATE_VALUE; + } + con_value[depth] = cur_value; + containers[depth++] = con_type; + ADD_VALUE(con_type, data, data + 1); + data++; + trailing_comma = false; + break; + + case '}': + case ']': + if (trailing_comma) { goto done_invalid; } + if (depth == 0) { goto done_invalid; } + con_type = containers[--depth]; + con_start_value = con_value[depth]; + if (values && con_start_value < num_values) { + values[con_start_value].len = cur_value - con_start_value - 1; + } + if (c == '}') { + if (state != STATE_NAME && state != STATE_VALUE_SEPARATOR) { + goto done_invalid; + } + if (con_type != SPDK_JSON_VAL_OBJECT_BEGIN) { + goto done_invalid; + } + ADD_VALUE(SPDK_JSON_VAL_OBJECT_END, data, data + 1); + } else { + if (state != STATE_VALUE && state != STATE_VALUE_SEPARATOR) { + goto done_invalid; + } + if (con_type != SPDK_JSON_VAL_ARRAY_BEGIN) { + goto done_invalid; + } + ADD_VALUE(SPDK_JSON_VAL_ARRAY_END, data, data + 1); + } + con_type = depth == 0 ? SPDK_JSON_VAL_INVALID : containers[depth - 1]; + data++; + state = depth ? STATE_VALUE_SEPARATOR : STATE_END; + trailing_comma = false; + break; + + case ',': + if (state != STATE_VALUE_SEPARATOR) { goto done_invalid; } + data++; + assert(con_type == SPDK_JSON_VAL_ARRAY_BEGIN || + con_type == SPDK_JSON_VAL_OBJECT_BEGIN); + state = con_type == SPDK_JSON_VAL_ARRAY_BEGIN ? STATE_VALUE : STATE_NAME; + trailing_comma = true; + break; + + case ':': + if (state != STATE_NAME_SEPARATOR) { goto done_invalid; } + data++; + state = STATE_VALUE; + break; + + case '/': + if (!(flags & SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS)) { + goto done_invalid; + } + rc = json_valid_comment(data, json_end); + if (rc < 0) { goto done_rc; } + /* Skip over comment */ + data += rc; + break; + + default: + goto done_invalid; + } + + if (state == STATE_END) { + break; + } + } + + if (state == STATE_END) { + /* Skip trailing whitespace */ + while (data < json_end) { + uint8_t c = *data; + + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + data++; + } else { + break; + } + } + + /* + * These asserts are just for sanity checking - they are guaranteed by the allowed + * state transitions. + */ + assert(depth == 0); + assert(trailing_comma == false); + assert(data <= json_end); + if (end) { + *end = data; + } + return cur_value; + } + + /* Invalid end state - ran out of data */ + rc = SPDK_JSON_PARSE_INCOMPLETE; + +done_rc: + assert(rc < 0); + if (end) { + *end = data; + } + return rc; + +done_invalid: + rc = SPDK_JSON_PARSE_INVALID; + goto done_rc; +} diff --git a/src/spdk/lib/json/json_util.c b/src/spdk/lib/json/json_util.c new file mode 100644 index 000000000..18d751047 --- /dev/null +++ b/src/spdk/lib/json/json_util.c @@ -0,0 +1,653 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/json.h" + +#include "spdk_internal/utf.h" +#include "spdk_internal/log.h" + +#define SPDK_JSON_DEBUG(...) SPDK_DEBUGLOG(SPDK_LOG_JSON, __VA_ARGS__) + +size_t +spdk_json_val_len(const struct spdk_json_val *val) +{ + if (val == NULL) { + return 0; + } + + if (val->type == SPDK_JSON_VAL_ARRAY_BEGIN || val->type == SPDK_JSON_VAL_OBJECT_BEGIN) { + return val->len + 2; + } + + return 1; +} + +bool +spdk_json_strequal(const struct spdk_json_val *val, const char *str) +{ + size_t len; + + if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) { + return false; + } + + len = strlen(str); + if (val->len != len) { + return false; + } + + return memcmp(val->start, str, len) == 0; +} + +char * +spdk_json_strdup(const struct spdk_json_val *val) +{ + size_t len; + char *s; + + if (val->type != SPDK_JSON_VAL_STRING && val->type != SPDK_JSON_VAL_NAME) { + return NULL; + } + + len = val->len; + + if (memchr(val->start, '\0', len)) { + /* String contains embedded NUL, so it is not a valid C string. */ + return NULL; + } + + s = malloc(len + 1); + if (s == NULL) { + return s; + } + + memcpy(s, val->start, len); + s[len] = '\0'; + + return s; +} + +struct spdk_json_num { + bool negative; + uint64_t significand; + int64_t exponent; +}; + +static int +json_number_split(const struct spdk_json_val *val, struct spdk_json_num *num) +{ + const char *iter; + size_t remaining; + uint64_t *pval; + uint64_t frac_digits = 0; + uint64_t exponent_u64 = 0; + bool exponent_negative = false; + enum { + NUM_STATE_INT, + NUM_STATE_FRAC, + NUM_STATE_EXP, + } state; + + memset(num, 0, sizeof(*num)); + + if (val->type != SPDK_JSON_VAL_NUMBER) { + return -EINVAL; + } + + remaining = val->len; + if (remaining == 0) { + return -EINVAL; + } + + iter = val->start; + if (*iter == '-') { + num->negative = true; + iter++; + remaining--; + } + + state = NUM_STATE_INT; + pval = &num->significand; + while (remaining--) { + char c = *iter++; + + if (c == '.') { + state = NUM_STATE_FRAC; + } else if (c == 'e' || c == 'E') { + state = NUM_STATE_EXP; + pval = &exponent_u64; + } else if (c == '-') { + assert(state == NUM_STATE_EXP); + exponent_negative = true; + } else if (c == '+') { + assert(state == NUM_STATE_EXP); + /* exp_negative = false; */ /* already false by default */ + } else { + uint64_t new_val; + + assert(c >= '0' && c <= '9'); + new_val = *pval * 10 + c - '0'; + if (new_val < *pval) { + return -ERANGE; + } + + if (state == NUM_STATE_FRAC) { + frac_digits++; + } + + *pval = new_val; + } + } + + if (exponent_negative) { + if (exponent_u64 > 9223372036854775808ULL) { /* abs(INT64_MIN) */ + return -ERANGE; + } + num->exponent = (int64_t) - exponent_u64; + } else { + if (exponent_u64 > INT64_MAX) { + return -ERANGE; + } + num->exponent = exponent_u64; + } + num->exponent -= frac_digits; + + /* Apply as much of the exponent as possible without overflow or truncation */ + if (num->exponent < 0) { + while (num->exponent && num->significand >= 10 && num->significand % 10 == 0) { + num->significand /= 10; + num->exponent++; + } + } else { /* positive exponent */ + while (num->exponent) { + uint64_t new_val = num->significand * 10; + + if (new_val < num->significand) { + break; + } + + num->significand = new_val; + num->exponent--; + } + } + + return 0; +} + +int +spdk_json_number_to_uint16(const struct spdk_json_val *val, uint16_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent || split_num.negative) { + return -ERANGE; + } + + if (split_num.significand > UINT16_MAX) { + return -ERANGE; + } + *num = (uint16_t)split_num.significand; + return 0; +} + +int +spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent) { + return -ERANGE; + } + + if (split_num.negative) { + if (split_num.significand > 2147483648) { /* abs(INT32_MIN) */ + return -ERANGE; + } + *num = (int32_t) - (int64_t)split_num.significand; + return 0; + } + + /* positive */ + if (split_num.significand > INT32_MAX) { + return -ERANGE; + } + *num = (int32_t)split_num.significand; + return 0; +} + +int +spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent || split_num.negative) { + return -ERANGE; + } + + if (split_num.significand > UINT32_MAX) { + return -ERANGE; + } + *num = (uint32_t)split_num.significand; + return 0; +} + +int +spdk_json_number_to_uint64(const struct spdk_json_val *val, uint64_t *num) +{ + struct spdk_json_num split_num; + int rc; + + rc = json_number_split(val, &split_num); + if (rc) { + return rc; + } + + if (split_num.exponent || split_num.negative) { + return -ERANGE; + } + + *num = split_num.significand; + return 0; +} + +int +spdk_json_decode_object(const struct spdk_json_val *values, + const struct spdk_json_object_decoder *decoders, size_t num_decoders, void *out) +{ + uint32_t i; + bool invalid = false; + size_t decidx; + bool *seen; + + if (values == NULL || values->type != SPDK_JSON_VAL_OBJECT_BEGIN) { + return -1; + } + + seen = calloc(sizeof(bool), num_decoders); + if (seen == NULL) { + return -1; + } + + for (i = 0; i < values->len;) { + const struct spdk_json_val *name = &values[i + 1]; + const struct spdk_json_val *v = &values[i + 2]; + bool found = false; + + for (decidx = 0; decidx < num_decoders; decidx++) { + const struct spdk_json_object_decoder *dec = &decoders[decidx]; + if (spdk_json_strequal(name, dec->name)) { + void *field = (void *)((uintptr_t)out + dec->offset); + + found = true; + + if (seen[decidx]) { + /* duplicate field name */ + invalid = true; + SPDK_JSON_DEBUG("Duplicate key '%s'\n", dec->name); + } else { + seen[decidx] = true; + if (dec->decode_func(v, field)) { + invalid = true; + SPDK_JSON_DEBUG("Decoder failed to decode key '%s'\n", dec->name); + /* keep going to fill out any other valid keys */ + } + } + break; + } + } + + if (!found) { + invalid = true; + SPDK_JSON_DEBUG("Decoder not found for key '%.*s'\n", name->len, (char *)name->start); + } + + i += 1 + spdk_json_val_len(v); + } + + for (decidx = 0; decidx < num_decoders; decidx++) { + if (!decoders[decidx].optional && !seen[decidx]) { + /* required field is missing */ + invalid = true; + break; + } + } + + free(seen); + return invalid ? -1 : 0; +} + +int +spdk_json_decode_array(const struct spdk_json_val *values, spdk_json_decode_fn decode_func, + void *out, size_t max_size, size_t *out_size, size_t stride) +{ + uint32_t i; + char *field; + char *out_end; + + if (values == NULL || values->type != SPDK_JSON_VAL_ARRAY_BEGIN) { + return -1; + } + + *out_size = 0; + field = out; + out_end = field + max_size * stride; + for (i = 0; i < values->len;) { + const struct spdk_json_val *v = &values[i + 1]; + + if (field == out_end) { + return -1; + } + + if (decode_func(v, field)) { + return -1; + } + + i += spdk_json_val_len(v); + field += stride; + (*out_size)++; + } + + return 0; +} + +int +spdk_json_decode_bool(const struct spdk_json_val *val, void *out) +{ + bool *f = out; + + if (val->type != SPDK_JSON_VAL_TRUE && val->type != SPDK_JSON_VAL_FALSE) { + return -1; + } + + *f = val->type == SPDK_JSON_VAL_TRUE; + return 0; +} + +int +spdk_json_decode_uint16(const struct spdk_json_val *val, void *out) +{ + uint16_t *i = out; + + return spdk_json_number_to_uint16(val, i); +} + +int +spdk_json_decode_int32(const struct spdk_json_val *val, void *out) +{ + int32_t *i = out; + + return spdk_json_number_to_int32(val, i); +} + +int +spdk_json_decode_uint32(const struct spdk_json_val *val, void *out) +{ + uint32_t *i = out; + + return spdk_json_number_to_uint32(val, i); +} + +int +spdk_json_decode_uint64(const struct spdk_json_val *val, void *out) +{ + uint64_t *i = out; + + return spdk_json_number_to_uint64(val, i); +} + +int +spdk_json_decode_string(const struct spdk_json_val *val, void *out) +{ + char **s = out; + + free(*s); + + *s = spdk_json_strdup(val); + + if (*s) { + return 0; + } else { + return -1; + } +} + +static struct spdk_json_val * +json_first(struct spdk_json_val *object, enum spdk_json_val_type type) +{ + /* 'object' must be JSON object or array. 'type' might be combination of these two. */ + assert((type & (SPDK_JSON_VAL_ARRAY_BEGIN | SPDK_JSON_VAL_OBJECT_BEGIN)) != 0); + + assert(object != NULL); + + if ((object->type & type) == 0) { + return NULL; + } + + object++; + if (object->len == 0) { + return NULL; + } + + return object; +} + +static struct spdk_json_val * +json_value(struct spdk_json_val *key) +{ + return key->type == SPDK_JSON_VAL_NAME ? key + 1 : NULL; +} + +int +spdk_json_find(struct spdk_json_val *object, const char *key_name, struct spdk_json_val **key, + struct spdk_json_val **val, enum spdk_json_val_type type) +{ + struct spdk_json_val *_key = NULL; + struct spdk_json_val *_val = NULL; + struct spdk_json_val *it; + + assert(object != NULL); + + for (it = json_first(object, SPDK_JSON_VAL_ARRAY_BEGIN | SPDK_JSON_VAL_OBJECT_BEGIN); + it != NULL; + it = spdk_json_next(it)) { + if (it->type != SPDK_JSON_VAL_NAME) { + continue; + } + + if (spdk_json_strequal(it, key_name) != true) { + continue; + } + + if (_key) { + SPDK_JSON_DEBUG("Duplicate key '%s'", key_name); + return -EINVAL; + } + + _key = it; + _val = json_value(_key); + + if (type != SPDK_JSON_VAL_INVALID && (_val->type & type) == 0) { + SPDK_JSON_DEBUG("key '%s' type is %#x but expected one of %#x\n", key_name, _val->type, type); + return -EDOM; + } + } + + if (key) { + *key = _key; + } + + if (val) { + *val = _val; + } + + return _val ? 0 : -ENOENT; +} + +int +spdk_json_find_string(struct spdk_json_val *object, const char *key_name, + struct spdk_json_val **key, struct spdk_json_val **val) +{ + return spdk_json_find(object, key_name, key, val, SPDK_JSON_VAL_STRING); +} + +int +spdk_json_find_array(struct spdk_json_val *object, const char *key_name, + struct spdk_json_val **key, struct spdk_json_val **val) +{ + return spdk_json_find(object, key_name, key, val, SPDK_JSON_VAL_ARRAY_BEGIN); +} + +struct spdk_json_val * +spdk_json_object_first(struct spdk_json_val *object) +{ + struct spdk_json_val *first = json_first(object, SPDK_JSON_VAL_OBJECT_BEGIN); + + /* Empty object? */ + return first && first->type != SPDK_JSON_VAL_OBJECT_END ? first : NULL; +} + +struct spdk_json_val * +spdk_json_array_first(struct spdk_json_val *array_begin) +{ + struct spdk_json_val *first = json_first(array_begin, SPDK_JSON_VAL_ARRAY_BEGIN); + + /* Empty array? */ + return first && first->type != SPDK_JSON_VAL_ARRAY_END ? first : NULL; +} + +static struct spdk_json_val * +json_skip_object_or_array(struct spdk_json_val *val) +{ + unsigned lvl; + enum spdk_json_val_type end_type; + struct spdk_json_val *it; + + if (val->type == SPDK_JSON_VAL_OBJECT_BEGIN) { + end_type = SPDK_JSON_VAL_OBJECT_END; + } else if (val->type == SPDK_JSON_VAL_ARRAY_BEGIN) { + end_type = SPDK_JSON_VAL_ARRAY_END; + } else { + SPDK_JSON_DEBUG("Expected JSON object (%#x) or array (%#x) but got %#x\n", + SPDK_JSON_VAL_OBJECT_BEGIN, SPDK_JSON_VAL_ARRAY_BEGIN, val->type); + return NULL; + } + + lvl = 1; + for (it = val + 1; it->type != SPDK_JSON_VAL_INVALID && lvl != 0; it++) { + if (it->type == val->type) { + lvl++; + } else if (it->type == end_type) { + lvl--; + } + } + + /* if lvl != 0 we have invalid JSON object */ + if (lvl != 0) { + SPDK_JSON_DEBUG("Can't find end of object (type: %#x): lvl (%u) != 0)\n", val->type, lvl); + it = NULL; + } + + return it; +} + +struct spdk_json_val * +spdk_json_next(struct spdk_json_val *it) +{ + struct spdk_json_val *val, *next; + + switch (it->type) { + case SPDK_JSON_VAL_NAME: + val = json_value(it); + next = spdk_json_next(val); + break; + + /* We are in the middle of an array - get to next entry */ + case SPDK_JSON_VAL_NULL: + case SPDK_JSON_VAL_TRUE: + case SPDK_JSON_VAL_FALSE: + case SPDK_JSON_VAL_NUMBER: + case SPDK_JSON_VAL_STRING: + val = it + 1; + return val; + + case SPDK_JSON_VAL_ARRAY_BEGIN: + case SPDK_JSON_VAL_OBJECT_BEGIN: + next = json_skip_object_or_array(it); + break; + + /* Can't go to the next object if started from the end of array or object */ + case SPDK_JSON_VAL_ARRAY_END: + case SPDK_JSON_VAL_OBJECT_END: + case SPDK_JSON_VAL_INVALID: + return NULL; + default: + assert(false); + return NULL; + + } + + /* EOF ? */ + if (next == NULL) { + return NULL; + } + + switch (next->type) { + case SPDK_JSON_VAL_ARRAY_END: + case SPDK_JSON_VAL_OBJECT_END: + case SPDK_JSON_VAL_INVALID: + return NULL; + default: + /* Next value */ + return next; + } +} + +SPDK_LOG_REGISTER_COMPONENT("json_util", SPDK_LOG_JSON) diff --git a/src/spdk/lib/json/json_write.c b/src/spdk/lib/json/json_write.c new file mode 100644 index 000000000..7e9fbb5c3 --- /dev/null +++ b/src/spdk/lib/json/json_write.c @@ -0,0 +1,687 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/json.h" + +#include "spdk_internal/utf.h" + +struct spdk_json_write_ctx { + spdk_json_write_cb write_cb; + void *cb_ctx; + uint32_t flags; + uint32_t indent; + bool new_indent; + bool first_value; + bool failed; + size_t buf_filled; + uint8_t buf[4096]; +}; + +static int emit_buf_full(struct spdk_json_write_ctx *w, const void *data, size_t size); + +static int +fail(struct spdk_json_write_ctx *w) +{ + w->failed = true; + return -1; +} + +static int +flush_buf(struct spdk_json_write_ctx *w) +{ + int rc; + + rc = w->write_cb(w->cb_ctx, w->buf, w->buf_filled); + if (rc != 0) { + return fail(w); + } + + w->buf_filled = 0; + + return 0; +} + +struct spdk_json_write_ctx * +spdk_json_write_begin(spdk_json_write_cb write_cb, void *cb_ctx, uint32_t flags) +{ + struct spdk_json_write_ctx *w; + + w = calloc(1, sizeof(*w)); + if (w == NULL) { + return w; + } + + w->write_cb = write_cb; + w->cb_ctx = cb_ctx; + w->flags = flags; + w->indent = 0; + w->new_indent = false; + w->first_value = true; + w->failed = false; + w->buf_filled = 0; + + return w; +} + +int +spdk_json_write_end(struct spdk_json_write_ctx *w) +{ + bool failed; + int rc; + + if (w == NULL) { + return 0; + } + + failed = w->failed; + + rc = flush_buf(w); + if (rc != 0) { + failed = true; + } + + free(w); + + return failed ? -1 : 0; +} + +static inline int +emit(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + size_t buf_remain = sizeof(w->buf) - w->buf_filled; + + if (spdk_unlikely(size > buf_remain)) { + /* Not enough space in buffer for the new data. */ + return emit_buf_full(w, data, size); + } + + /* Copy the new data into buf. */ + memcpy(w->buf + w->buf_filled, data, size); + w->buf_filled += size; + return 0; +} + +static int +emit_buf_full(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + size_t buf_remain = sizeof(w->buf) - w->buf_filled; + int rc; + + assert(size > buf_remain); + + /* Copy as much of the new data as possible into the buffer and flush it. */ + memcpy(w->buf + w->buf_filled, data, buf_remain); + w->buf_filled += buf_remain; + + rc = flush_buf(w); + if (rc != 0) { + return fail(w); + } + + /* Recurse to emit the rest of the data. */ + return emit(w, data + buf_remain, size - buf_remain); +} + +static int +emit_fmt(struct spdk_json_write_ctx *w, const void *data, size_t size) +{ + if (w->flags & SPDK_JSON_WRITE_FLAG_FORMATTED) { + return emit(w, data, size); + } + return 0; +} + +static int +emit_indent(struct spdk_json_write_ctx *w) +{ + uint32_t i; + + if (w->flags & SPDK_JSON_WRITE_FLAG_FORMATTED) { + for (i = 0; i < w->indent; i++) { + if (emit(w, " ", 2)) { return fail(w); } + } + } + return 0; +} + +static int +begin_value(struct spdk_json_write_ctx *w) +{ + /* TODO: check for value state */ + if (w->new_indent) { + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + if (!w->first_value) { + if (emit(w, ",", 1)) { return fail(w); } + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + w->first_value = false; + w->new_indent = false; + return 0; +} + +int +spdk_json_write_val_raw(struct spdk_json_write_ctx *w, const void *data, size_t len) +{ + if (begin_value(w)) { return fail(w); } + return emit(w, data, len); +} + +int +spdk_json_write_null(struct spdk_json_write_ctx *w) +{ + if (begin_value(w)) { return fail(w); } + return emit(w, "null", 4); +} + +int +spdk_json_write_bool(struct spdk_json_write_ctx *w, bool val) +{ + if (begin_value(w)) { return fail(w); } + if (val) { + return emit(w, "true", 4); + } else { + return emit(w, "false", 5); + } +} + +int +spdk_json_write_int32(struct spdk_json_write_ctx *w, int32_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRId32, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +int +spdk_json_write_uint32(struct spdk_json_write_ctx *w, uint32_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRIu32, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +int +spdk_json_write_int64(struct spdk_json_write_ctx *w, int64_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRId64, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +int +spdk_json_write_uint64(struct spdk_json_write_ctx *w, uint64_t val) +{ + char buf[32]; + int count; + + if (begin_value(w)) { return fail(w); } + count = snprintf(buf, sizeof(buf), "%" PRIu64, val); + if (count <= 0 || (size_t)count >= sizeof(buf)) { return fail(w); } + return emit(w, buf, count); +} + +static void +write_hex_4(void *dest, uint16_t val) +{ + uint8_t *p = dest; + char hex[] = "0123456789ABCDEF"; + + p[0] = hex[(val >> 12)]; + p[1] = hex[(val >> 8) & 0xF]; + p[2] = hex[(val >> 4) & 0xF]; + p[3] = hex[val & 0xF]; +} + +static inline int +write_codepoint(struct spdk_json_write_ctx *w, uint32_t codepoint) +{ + static const uint8_t escapes[] = { + ['\b'] = 'b', + ['\f'] = 'f', + ['\n'] = 'n', + ['\r'] = 'r', + ['\t'] = 't', + ['"'] = '"', + ['\\'] = '\\', + /* + * Forward slash (/) is intentionally not converted to an escape + * (it is valid unescaped). + */ + }; + uint16_t high, low; + char out[13]; + size_t out_len; + + if (codepoint < sizeof(escapes) && escapes[codepoint]) { + out[0] = '\\'; + out[1] = escapes[codepoint]; + out_len = 2; + } else if (codepoint >= 0x20 && codepoint < 0x7F) { + /* + * Encode plain ASCII directly (except 0x7F, since it is really + * a control character, despite the JSON spec not considering it one). + */ + out[0] = (uint8_t)codepoint; + out_len = 1; + } else if (codepoint < 0x10000) { + out[0] = '\\'; + out[1] = 'u'; + write_hex_4(&out[2], (uint16_t)codepoint); + out_len = 6; + } else { + utf16_encode_surrogate_pair(codepoint, &high, &low); + out[0] = '\\'; + out[1] = 'u'; + write_hex_4(&out[2], high); + out[6] = '\\'; + out[7] = 'u'; + write_hex_4(&out[8], low); + out_len = 12; + } + + return emit(w, out, out_len); +} + +static int +write_string_or_name(struct spdk_json_write_ctx *w, const char *val, size_t len) +{ + const uint8_t *p = val; + const uint8_t *end = val + len; + + if (emit(w, "\"", 1)) { return fail(w); } + + while (p != end) { + int codepoint_len; + uint32_t codepoint; + + codepoint_len = utf8_valid(p, end); + switch (codepoint_len) { + case 1: + codepoint = utf8_decode_unsafe_1(p); + break; + case 2: + codepoint = utf8_decode_unsafe_2(p); + break; + case 3: + codepoint = utf8_decode_unsafe_3(p); + break; + case 4: + codepoint = utf8_decode_unsafe_4(p); + break; + default: + return fail(w); + } + + if (write_codepoint(w, codepoint)) { return fail(w); } + p += codepoint_len; + } + + return emit(w, "\"", 1); +} + +static int +write_string_or_name_utf16le(struct spdk_json_write_ctx *w, const uint16_t *val, size_t len) +{ + const uint16_t *p = val; + const uint16_t *end = val + len; + + if (emit(w, "\"", 1)) { return fail(w); } + + while (p != end) { + int codepoint_len; + uint32_t codepoint; + + codepoint_len = utf16le_valid(p, end); + switch (codepoint_len) { + case 1: + codepoint = from_le16(&p[0]); + break; + case 2: + codepoint = utf16_decode_surrogate_pair(from_le16(&p[0]), from_le16(&p[1])); + break; + default: + return fail(w); + } + + if (write_codepoint(w, codepoint)) { return fail(w); } + p += codepoint_len; + } + + return emit(w, "\"", 1); +} + +int +spdk_json_write_string_raw(struct spdk_json_write_ctx *w, const char *val, size_t len) +{ + if (begin_value(w)) { return fail(w); } + return write_string_or_name(w, val, len); +} + +int +spdk_json_write_string(struct spdk_json_write_ctx *w, const char *val) +{ + return spdk_json_write_string_raw(w, val, strlen(val)); +} + +int +spdk_json_write_string_utf16le_raw(struct spdk_json_write_ctx *w, const uint16_t *val, size_t len) +{ + if (begin_value(w)) { return fail(w); } + return write_string_or_name_utf16le(w, val, len); +} + +int +spdk_json_write_string_utf16le(struct spdk_json_write_ctx *w, const uint16_t *val) +{ + const uint16_t *p; + size_t len; + + for (len = 0, p = val; *p; p++) { + len++; + } + + return spdk_json_write_string_utf16le_raw(w, val, len); +} + +int +spdk_json_write_string_fmt(struct spdk_json_write_ctx *w, const char *fmt, ...) +{ + va_list args; + int rc; + + va_start(args, fmt); + rc = spdk_json_write_string_fmt_v(w, fmt, args); + va_end(args); + + return rc; +} + +int +spdk_json_write_string_fmt_v(struct spdk_json_write_ctx *w, const char *fmt, va_list args) +{ + char *s; + int rc; + + s = spdk_vsprintf_alloc(fmt, args); + if (s == NULL) { + return -1; + } + + rc = spdk_json_write_string(w, s); + free(s); + return rc; +} + +int +spdk_json_write_array_begin(struct spdk_json_write_ctx *w) +{ + if (begin_value(w)) { return fail(w); } + w->first_value = true; + w->new_indent = true; + w->indent++; + if (emit(w, "[", 1)) { return fail(w); } + return 0; +} + +int +spdk_json_write_array_end(struct spdk_json_write_ctx *w) +{ + w->first_value = false; + if (w->indent == 0) { return fail(w); } + w->indent--; + if (!w->new_indent) { + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + w->new_indent = false; + return emit(w, "]", 1); +} + +int +spdk_json_write_object_begin(struct spdk_json_write_ctx *w) +{ + if (begin_value(w)) { return fail(w); } + w->first_value = true; + w->new_indent = true; + w->indent++; + if (emit(w, "{", 1)) { return fail(w); } + return 0; +} + +int +spdk_json_write_object_end(struct spdk_json_write_ctx *w) +{ + w->first_value = false; + w->indent--; + if (!w->new_indent) { + if (emit_fmt(w, "\n", 1)) { return fail(w); } + if (emit_indent(w)) { return fail(w); } + } + w->new_indent = false; + return emit(w, "}", 1); +} + +int +spdk_json_write_name_raw(struct spdk_json_write_ctx *w, const char *name, size_t len) +{ + /* TODO: check that container is an object */ + if (begin_value(w)) { return fail(w); } + if (write_string_or_name(w, name, len)) { return fail(w); } + w->first_value = true; + if (emit(w, ":", 1)) { return fail(w); } + return emit_fmt(w, " ", 1); +} + +int +spdk_json_write_name(struct spdk_json_write_ctx *w, const char *name) +{ + return spdk_json_write_name_raw(w, name, strlen(name)); +} + +int +spdk_json_write_val(struct spdk_json_write_ctx *w, const struct spdk_json_val *val) +{ + size_t num_values, i; + + switch (val->type) { + case SPDK_JSON_VAL_NUMBER: + return spdk_json_write_val_raw(w, val->start, val->len); + + case SPDK_JSON_VAL_STRING: + return spdk_json_write_string_raw(w, val->start, val->len); + + case SPDK_JSON_VAL_NAME: + return spdk_json_write_name_raw(w, val->start, val->len); + + case SPDK_JSON_VAL_TRUE: + return spdk_json_write_bool(w, true); + + case SPDK_JSON_VAL_FALSE: + return spdk_json_write_bool(w, false); + + case SPDK_JSON_VAL_NULL: + return spdk_json_write_null(w); + + case SPDK_JSON_VAL_ARRAY_BEGIN: + case SPDK_JSON_VAL_OBJECT_BEGIN: + num_values = val[0].len; + + if (val[0].type == SPDK_JSON_VAL_OBJECT_BEGIN) { + if (spdk_json_write_object_begin(w)) { + return fail(w); + } + } else { + if (spdk_json_write_array_begin(w)) { + return fail(w); + } + } + + /* Loop up to and including the _END value */ + for (i = 0; i < num_values + 1;) { + if (spdk_json_write_val(w, &val[i + 1])) { + return fail(w); + } + if (val[i + 1].type == SPDK_JSON_VAL_ARRAY_BEGIN || + val[i + 1].type == SPDK_JSON_VAL_OBJECT_BEGIN) { + i += val[i + 1].len + 2; + } else { + i++; + } + } + return 0; + + case SPDK_JSON_VAL_ARRAY_END: + return spdk_json_write_array_end(w); + + case SPDK_JSON_VAL_OBJECT_END: + return spdk_json_write_object_end(w); + + case SPDK_JSON_VAL_INVALID: + /* Handle INVALID to make the compiler happy (and catch other unhandled types) */ + return fail(w); + } + + return fail(w); +} + +int spdk_json_write_named_null(struct spdk_json_write_ctx *w, const char *name) +{ + int rc = spdk_json_write_name(w, name); + return rc ? rc : spdk_json_write_null(w); +} + +int spdk_json_write_named_bool(struct spdk_json_write_ctx *w, const char *name, bool val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_bool(w, val); +} + +int spdk_json_write_named_int32(struct spdk_json_write_ctx *w, const char *name, int32_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_int32(w, val); +} + +int spdk_json_write_named_uint32(struct spdk_json_write_ctx *w, const char *name, uint32_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_uint32(w, val); +} + +int spdk_json_write_named_uint64(struct spdk_json_write_ctx *w, const char *name, uint64_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_uint64(w, val); +} + +int spdk_json_write_named_int64(struct spdk_json_write_ctx *w, const char *name, int64_t val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_int64(w, val); +} + +int spdk_json_write_named_string(struct spdk_json_write_ctx *w, const char *name, const char *val) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_string(w, val); +} + +int spdk_json_write_named_string_fmt(struct spdk_json_write_ctx *w, const char *name, + const char *fmt, ...) +{ + va_list args; + int rc; + + va_start(args, fmt); + rc = spdk_json_write_named_string_fmt_v(w, name, fmt, args); + va_end(args); + + return rc; +} + +int spdk_json_write_named_string_fmt_v(struct spdk_json_write_ctx *w, const char *name, + const char *fmt, va_list args) +{ + char *s; + int rc; + + rc = spdk_json_write_name(w, name); + if (rc) { + return rc; + } + + s = spdk_vsprintf_alloc(fmt, args); + + if (s == NULL) { + return -1; + } + + rc = spdk_json_write_string(w, s); + free(s); + return rc; +} + +int spdk_json_write_named_array_begin(struct spdk_json_write_ctx *w, const char *name) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_array_begin(w); +} + +int spdk_json_write_named_object_begin(struct spdk_json_write_ctx *w, const char *name) +{ + int rc = spdk_json_write_name(w, name); + + return rc ? rc : spdk_json_write_object_begin(w); +} diff --git a/src/spdk/lib/json/spdk_json.map b/src/spdk/lib/json/spdk_json.map new file mode 100644 index 000000000..0699feaad --- /dev/null +++ b/src/spdk/lib/json/spdk_json.map @@ -0,0 +1,67 @@ +{ + global: + + # public functions + spdk_json_parse; + spdk_json_decode_object; + spdk_json_decode_array; + spdk_json_decode_bool; + spdk_json_decode_uint16; + spdk_json_decode_int32; + spdk_json_decode_uint32; + spdk_json_decode_uint64; + spdk_json_decode_string; + + spdk_json_val_len; + spdk_json_strequal; + spdk_json_strdup; + + spdk_json_number_to_uint16; + spdk_json_number_to_int32; + spdk_json_number_to_uint32; + spdk_json_number_to_uint64; + + spdk_json_write_begin; + spdk_json_write_end; + spdk_json_write_null; + spdk_json_write_bool; + spdk_json_write_int32; + spdk_json_write_uint32; + spdk_json_write_int64; + spdk_json_write_uint64; + spdk_json_write_string; + spdk_json_write_string_raw; + spdk_json_write_string_utf16le; + spdk_json_write_string_utf16le_raw; + spdk_json_write_string_fmt; + spdk_json_write_string_fmt_v; + spdk_json_write_array_begin; + spdk_json_write_array_end; + spdk_json_write_object_begin; + spdk_json_write_object_end; + spdk_json_write_name; + spdk_json_write_name_raw; + spdk_json_write_val; + spdk_json_write_val_raw; + + spdk_json_write_named_null; + spdk_json_write_named_bool; + spdk_json_write_named_int32; + spdk_json_write_named_uint32; + spdk_json_write_named_uint64; + spdk_json_write_named_int64; + spdk_json_write_named_string; + spdk_json_write_named_string_fmt; + spdk_json_write_named_string_fmt_v; + spdk_json_write_named_array_begin; + spdk_json_write_named_object_begin; + + spdk_json_find; + spdk_json_find_string; + spdk_json_find_array; + spdk_json_object_first; + spdk_json_array_first; + spdk_json_next; + + local: *; +}; |