diff options
Diffstat (limited to '')
-rw-r--r-- | src/spdk/lib/json/json_util.c | 650 |
1 files changed, 650 insertions, 0 deletions
diff --git a/src/spdk/lib/json/json_util.c b/src/spdk/lib/json/json_util.c new file mode 100644 index 00000000..1146e6fa --- /dev/null +++ b/src/spdk/lib/json/json_util.c @@ -0,0 +1,650 @@ +/*- + * 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_UTIL, __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 +spdk_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 = spdk_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 = spdk_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 = spdk_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 = spdk_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; + } else { + seen[decidx] = true; + if (dec->decode_func(v, field)) { + invalid = true; + /* keep going to fill out any other valid keys */ + } + } + break; + } + } + + if (!found) { + invalid = true; + } + + 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 * +spdk_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 * +spdk_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 = spdk_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 = spdk_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 = spdk_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 = spdk_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 * +spdk_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_END, 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 = spdk_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 = spdk_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_UTIL) |