json: add JSON parser and encoder libraries
Change-Id: Id73fb7e300d66d31a7c3986c0334b6f56e284905 Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
parent
3a94688d8e
commit
f9193f4ce7
@ -47,6 +47,7 @@ timing_enter lib
|
||||
time test/lib/nvme/nvme.sh
|
||||
time test/lib/memory/memory.sh
|
||||
time test/lib/ioat/ioat.sh
|
||||
time test/lib/json/json.sh
|
||||
|
||||
timing_exit lib
|
||||
|
||||
|
208
include/spdk/json.h
Normal file
208
include/spdk/json.h
Normal file
@ -0,0 +1,208 @@
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* JSON parsing and encoding
|
||||
*/
|
||||
|
||||
#ifndef SPDK_JSON_H_
|
||||
#define SPDK_JSON_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum spdk_json_val_type {
|
||||
SPDK_JSON_VAL_INVALID,
|
||||
SPDK_JSON_VAL_NULL,
|
||||
SPDK_JSON_VAL_TRUE,
|
||||
SPDK_JSON_VAL_FALSE,
|
||||
SPDK_JSON_VAL_NUMBER,
|
||||
SPDK_JSON_VAL_STRING,
|
||||
SPDK_JSON_VAL_ARRAY_BEGIN,
|
||||
SPDK_JSON_VAL_ARRAY_END,
|
||||
SPDK_JSON_VAL_OBJECT_BEGIN,
|
||||
SPDK_JSON_VAL_OBJECT_END,
|
||||
SPDK_JSON_VAL_NAME,
|
||||
};
|
||||
|
||||
struct spdk_json_val {
|
||||
/**
|
||||
* Pointer to the location of the value within the parsed JSON input.
|
||||
*
|
||||
* For SPDK_JSON_VAL_STRING and SPDK_JSON_VAL_NAME,
|
||||
* this points to the beginning of the decoded UTF-8 string without quotes.
|
||||
*
|
||||
* For SPDK_JSON_VAL_NUMBER, this points to the beginning of the number as represented in
|
||||
* the original JSON (text representation, not converted to a numeric value).
|
||||
*/
|
||||
void *start;
|
||||
|
||||
/**
|
||||
* Length of value.
|
||||
*
|
||||
* For SPDK_JSON_VAL_STRING, SPDK_JSON_VAL_NUMBER, and SPDK_JSON_VAL_NAME,
|
||||
* this is the length in bytes of the value starting at \ref start.
|
||||
*
|
||||
* For SPDK_JSON_VAL_ARRAY_BEGIN and SPDK_JSON_VAL_OBJECT_BEGIN,
|
||||
* this is the number of values contained within the array or object (including
|
||||
* nested objects and arrays, but not including the _END value). The array or object _END
|
||||
* value can be found by advancing len values from the _BEGIN value.
|
||||
*/
|
||||
uint32_t len;
|
||||
|
||||
/**
|
||||
* Type of value.
|
||||
*/
|
||||
enum spdk_json_val_type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invalid JSON syntax.
|
||||
*/
|
||||
#define SPDK_JSON_PARSE_INVALID -1
|
||||
|
||||
/**
|
||||
* JSON was valid up to the end of the current buffer, but did not represent a complete JSON value.
|
||||
*/
|
||||
#define SPDK_JSON_PARSE_INCOMPLETE -2
|
||||
|
||||
#define SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED -3
|
||||
|
||||
/**
|
||||
* Decode JSON strings and names in place (modify the input buffer).
|
||||
*/
|
||||
#define SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE 0x000000001
|
||||
|
||||
/*
|
||||
* Parse JSON data.
|
||||
*
|
||||
* \param data Raw JSON data; must be encoded in UTF-8.
|
||||
* Note that the data may be modified to perform in-place string decoding.
|
||||
*
|
||||
* \param size Size of data in bytes.
|
||||
*
|
||||
* \param end If non-NULL, this will be filled a pointer to the byte just beyond the end
|
||||
* of the valid JSON.
|
||||
*
|
||||
* \return Number of values parsed, or negative on failure:
|
||||
* SPDK_JSON_PARSE_INVALID if the provided data was not valid JSON, or
|
||||
* SPDK_JSON_PARSE_INCOMPLETE if the provided data was not a complete JSON value.
|
||||
*/
|
||||
ssize_t spdk_json_parse(void *json, size_t size, struct spdk_json_val *values, size_t num_values,
|
||||
void **end, uint32_t flags);
|
||||
|
||||
typedef int (*spdk_json_decode_fn)(const struct spdk_json_val *val, void *out);
|
||||
|
||||
struct spdk_json_object_decoder {
|
||||
const char *name;
|
||||
size_t offset;
|
||||
spdk_json_decode_fn decode_func;
|
||||
bool optional;
|
||||
};
|
||||
|
||||
int spdk_json_decode_object(const struct spdk_json_val *values,
|
||||
const struct spdk_json_object_decoder *decoders, size_t num_decoders, void *out);
|
||||
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);
|
||||
|
||||
int spdk_json_decode_int32(const struct spdk_json_val *val, void *out);
|
||||
int spdk_json_decode_uint32(const struct spdk_json_val *val, void *out);
|
||||
int spdk_json_decode_string(const struct spdk_json_val *val, void *out);
|
||||
|
||||
/**
|
||||
* Get length of a value in number of values.
|
||||
*
|
||||
* This can be used to skip over a value while interpreting parse results.
|
||||
*
|
||||
* For SPDK_JSON_VAL_ARRAY_BEGIN and SPDK_JSON_VAL_OBJECT_BEGIN,
|
||||
* this returns the number of values contained within this value, plus the _BEGIN and _END values.
|
||||
*
|
||||
* For all other values, this returns 1.
|
||||
*/
|
||||
size_t spdk_json_val_len(const struct spdk_json_val *val);
|
||||
|
||||
/**
|
||||
* Compare JSON string with null terminated C string.
|
||||
*
|
||||
* \return true if strings are equal or false if not
|
||||
*/
|
||||
bool spdk_json_strequal(const struct spdk_json_val *val, const char *str);
|
||||
|
||||
/**
|
||||
* Equivalent of strdup() for JSON string values.
|
||||
*
|
||||
* If val is not representable as a C string (contains embedded '\0' characters),
|
||||
* returns NULL.
|
||||
*
|
||||
* Caller is responsible for passing the result to free() when it is no longer needed.
|
||||
*/
|
||||
char *spdk_json_strdup(const struct spdk_json_val *val);
|
||||
|
||||
int spdk_json_number_to_double(const struct spdk_json_val *val, double *num);
|
||||
int spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num);
|
||||
int spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num);
|
||||
|
||||
struct spdk_json_write_ctx;
|
||||
|
||||
typedef int (*spdk_json_write_cb)(void *cb_ctx, const void *data, size_t size);
|
||||
|
||||
struct spdk_json_write_ctx *spdk_json_write_begin(spdk_json_write_cb write_cb, void *cb_ctx,
|
||||
uint32_t flags);
|
||||
int spdk_json_write_end(struct spdk_json_write_ctx *w);
|
||||
int spdk_json_write_null(struct spdk_json_write_ctx *w);
|
||||
int spdk_json_write_bool(struct spdk_json_write_ctx *w, bool val);
|
||||
int spdk_json_write_int32(struct spdk_json_write_ctx *w, int32_t val);
|
||||
int spdk_json_write_uint32(struct spdk_json_write_ctx *w, uint32_t val);
|
||||
int spdk_json_write_string(struct spdk_json_write_ctx *w, const char *val);
|
||||
int spdk_json_write_string_raw(struct spdk_json_write_ctx *w, const char *val, size_t len);
|
||||
int spdk_json_write_array_begin(struct spdk_json_write_ctx *w);
|
||||
int spdk_json_write_array_end(struct spdk_json_write_ctx *w);
|
||||
int spdk_json_write_object_begin(struct spdk_json_write_ctx *w);
|
||||
int spdk_json_write_object_end(struct spdk_json_write_ctx *w);
|
||||
int spdk_json_write_name(struct spdk_json_write_ctx *w, const char *name);
|
||||
int spdk_json_write_name_raw(struct spdk_json_write_ctx *w, const char *name, size_t len);
|
||||
|
||||
int spdk_json_write_val(struct spdk_json_write_ctx *w, const struct spdk_json_val *val);
|
||||
|
||||
/*
|
||||
* Append bytes directly to the output stream without validation.
|
||||
*
|
||||
* Can be used to write values with specific encodings that differ from the JSON writer output.
|
||||
*/
|
||||
int spdk_json_write_val_raw(struct spdk_json_write_ctx *w, const void *data, size_t len);
|
||||
|
||||
#endif
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(CURDIR)/..
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
DIRS-y += conf memory util nvme ioat
|
||||
DIRS-y += conf json memory util nvme ioat
|
||||
|
||||
.PHONY: all clean $(DIRS-y)
|
||||
|
||||
|
39
lib/json/Makefile
Normal file
39
lib/json/Makefile
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# 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 := $(CURDIR)/../..
|
||||
|
||||
C_SRCS = json_parse.c json_util.c json_write.c
|
||||
LIBNAME = json
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
|
290
lib/json/json_internal.h
Normal file
290
lib/json/json_internal.h
Normal file
@ -0,0 +1,290 @@
|
||||
/*-
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef SPDK_JSON_INTERNAL_H_
|
||||
#define SPDK_JSON_INTERNAL_H_
|
||||
|
||||
#include "spdk/json.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define SPDK_JSON_MAX_NESTING_DEPTH 64
|
||||
|
||||
static inline bool
|
||||
utf8_tail(uint8_t c)
|
||||
{
|
||||
/* c >= 0x80 && c <= 0xBF, or binary 01xxxxxx */
|
||||
return (c & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for a valid UTF-8 encoding of a single codepoint.
|
||||
*
|
||||
* \return Length of valid UTF-8 byte sequence, or negative if invalid.
|
||||
*/
|
||||
static inline int
|
||||
utf8_valid(const uint8_t *start, const uint8_t *end)
|
||||
{
|
||||
const uint8_t *p = start;
|
||||
uint8_t b0, b1, b2, b3;
|
||||
|
||||
if (p == end) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
b0 = *p;
|
||||
|
||||
if (b0 <= 0x7F) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (b0 <= 0xC1) {
|
||||
/* Invalid start byte */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (++p == end) {
|
||||
/* Not enough bytes left */
|
||||
return -1;
|
||||
}
|
||||
b1 = *p;
|
||||
|
||||
if (b0 <= 0xDF) {
|
||||
/* C2..DF 80..BF */
|
||||
if (!utf8_tail(b1)) {
|
||||
return -1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (++p == end) {
|
||||
/* Not enough bytes left */
|
||||
return -1;
|
||||
}
|
||||
b2 = *p;
|
||||
|
||||
if (b0 == 0xE0) {
|
||||
/* E0 A0..BF 80..BF */
|
||||
if (b1 < 0xA0 || b1 > 0xBF || !utf8_tail(b2)) {
|
||||
return -1;
|
||||
}
|
||||
return 3;
|
||||
} else if (b0 == 0xED && b1 >= 0xA0) {
|
||||
/*
|
||||
* UTF-16 surrogate pairs use U+D800..U+DFFF, which would be encoded as
|
||||
* ED A0..BF 80..BF in UTF-8; however, surrogate pairs are not allowed in UTF-8.
|
||||
*/
|
||||
return -1;
|
||||
} else if (b0 <= 0xEF) {
|
||||
/* E1..EF 80..BF 80..BF */
|
||||
if (!utf8_tail(b1) || !utf8_tail(b2)) {
|
||||
return -1;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (++p == end) {
|
||||
/* Not enough bytes left */
|
||||
return -1;
|
||||
}
|
||||
b3 = *p;
|
||||
|
||||
if (b0 == 0xF0) {
|
||||
/* F0 90..BF 80..BF 80..BF */
|
||||
if (b1 < 0x90 || b1 > 0xBF || !utf8_tail(b2) || !utf8_tail(b3)) {
|
||||
return -1;
|
||||
}
|
||||
return 4;
|
||||
} else if (b0 <= 0xF3) {
|
||||
/* F1..F3 80..BF 80..BF 80..BF */
|
||||
if (!utf8_tail(b1) || !utf8_tail(b2) || !utf8_tail(b3)) {
|
||||
return -1;
|
||||
}
|
||||
return 4;
|
||||
} else if (b0 == 0xF4) {
|
||||
/* F4 80..8F 80..BF 80..BF */
|
||||
if (b1 < 0x80 || b1 > 0x8F || !utf8_tail(b2) || !utf8_tail(b3)) {
|
||||
return -1;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
utf8_decode_unsafe_1(const uint8_t *data)
|
||||
{
|
||||
return data[0];
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
utf8_decode_unsafe_2(const uint8_t *data)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
|
||||
codepoint = ((data[0] & 0x1F) << 6);
|
||||
codepoint |= (data[1] & 0x3F);
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
utf8_decode_unsafe_3(const uint8_t *data)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
|
||||
codepoint = ((data[0] & 0x0F) << 12);
|
||||
codepoint |= (data[1] & 0x3F) << 6;
|
||||
codepoint |= (data[2] & 0x3F);
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
utf8_decode_unsafe_4(const uint8_t *data)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
|
||||
codepoint = ((data[0] & 0x07) << 18);
|
||||
codepoint |= (data[1] & 0x3F) << 12;
|
||||
codepoint |= (data[2] & 0x3F) << 6;
|
||||
codepoint |= (data[3] & 0x3F);
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a single Unicode codepoint as UTF-8.
|
||||
*
|
||||
* buf must have at least 4 bytes of space available (hence unsafe).
|
||||
*
|
||||
* \return Number of bytes appended to buf, or negative if encoding failed.
|
||||
*/
|
||||
static inline int
|
||||
utf8_encode_unsafe(uint8_t *buf, uint32_t c)
|
||||
{
|
||||
if (c <= 0x7F) {
|
||||
buf[0] = c;
|
||||
return 1;
|
||||
} else if (c <= 0x7FF) {
|
||||
buf[0] = 0xC0 | (c >> 6);
|
||||
buf[1] = 0x80 | (c & 0x3F);
|
||||
return 2;
|
||||
} else if (c >= 0xD800 && c <= 0xDFFF) {
|
||||
/* UTF-16 surrogate pairs - invalid in UTF-8 */
|
||||
return -1;
|
||||
} else if (c <= 0xFFFF) {
|
||||
buf[0] = 0xE0 | (c >> 12);
|
||||
buf[1] = 0x80 | ((c >> 6) & 0x3F);
|
||||
buf[2] = 0x80 | (c & 0x3F);
|
||||
return 3;
|
||||
} else if (c <= 0x10FFFF) {
|
||||
buf[0] = 0xF0 | (c >> 18);
|
||||
buf[1] = 0x80 | ((c >> 12) & 0x3F);
|
||||
buf[2] = 0x80 | ((c >> 6) & 0x3F);
|
||||
buf[3] = 0x80 | (c & 0x3F);
|
||||
return 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
utf8_codepoint_len(uint32_t c)
|
||||
{
|
||||
if (c <= 0x7F) {
|
||||
return 1;
|
||||
} else if (c <= 0x7FF) {
|
||||
return 2;
|
||||
} else if (c >= 0xD800 && c <= 0xDFFF) {
|
||||
/* UTF-16 surrogate pairs - invalid in UTF-8 */
|
||||
return -1;
|
||||
} else if (c <= 0xFFFF) {
|
||||
return 3;
|
||||
} else if (c <= 0x10FFFF) {
|
||||
return 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
utf16_valid_surrogate_high(uint32_t val)
|
||||
{
|
||||
return val >= 0xD800 && val <= 0xDBFF;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
utf16_valid_surrogate_low(uint32_t val)
|
||||
{
|
||||
return val >= 0xDC00 && val <= 0xDFFF;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
utf16_decode_surrogate_pair(uint32_t high, uint32_t low)
|
||||
{
|
||||
uint32_t codepoint;
|
||||
|
||||
assert(utf16_valid_surrogate_high(high));
|
||||
assert(utf16_valid_surrogate_low(low));
|
||||
|
||||
codepoint = low;
|
||||
codepoint &= 0x3FF;
|
||||
codepoint |= ((high & 0x3FF) << 10);
|
||||
codepoint += 0x10000;
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
static inline void
|
||||
utf16_encode_surrogate_pair(uint32_t codepoint, uint16_t *high, uint16_t *low)
|
||||
{
|
||||
assert(codepoint >= 0x10000);
|
||||
assert(codepoint <= 0x10FFFF);
|
||||
|
||||
codepoint -= 0x10000;
|
||||
*high = 0xD800 | (codepoint >> 10);
|
||||
*low = 0xDC00 | (codepoint & 0x3FF);
|
||||
|
||||
assert(utf16_valid_surrogate_high(*high));
|
||||
assert(utf16_valid_surrogate_low(*low));
|
||||
}
|
||||
|
||||
#endif
|
616
lib/json/json_parse.c
Normal file
616
lib/json/json_parse.c
Normal file
@ -0,0 +1,616 @@
|
||||
/*-
|
||||
* 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 "json_internal.h"
|
||||
|
||||
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
|
||||
*/
|
||||
return SPDK_JSON_PARSE_INCOMPLETE;
|
||||
}
|
||||
|
||||
if (*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) {
|
||||
return rc;
|
||||
}
|
||||
out += rc;
|
||||
} else if (str[0] <= 0x1f) {
|
||||
/* control characters must be escaped */
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
} else {
|
||||
rc = utf8_valid(str, buf_end);
|
||||
if (rc == 0) {
|
||||
return SPDK_JSON_PARSE_INCOMPLETE;
|
||||
} else if (rc < 0) {
|
||||
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. */
|
||||
return SPDK_JSON_PARSE_INCOMPLETE;
|
||||
}
|
||||
|
||||
static int
|
||||
json_valid_number(uint8_t *start, uint8_t *buf_end)
|
||||
{
|
||||
uint8_t *p = start;
|
||||
enum {
|
||||
NUM_STATE_START,
|
||||
NUM_STATE_INT_FIRST_DIGIT,
|
||||
NUM_STATE_INT_DIGITS,
|
||||
NUM_STATE_FRAC_OR_EXP,
|
||||
NUM_STATE_FRAC_FIRST_DIGIT,
|
||||
NUM_STATE_FRAC_DIGITS,
|
||||
NUM_STATE_EXP_SIGN,
|
||||
NUM_STATE_EXP_FIRST_DIGIT,
|
||||
NUM_STATE_EXP_DIGITS,
|
||||
} state = NUM_STATE_START;
|
||||
|
||||
if (p >= buf_end) return -1;
|
||||
|
||||
while (p != buf_end) {
|
||||
uint8_t c = *p++;
|
||||
|
||||
switch (c) {
|
||||
case '0':
|
||||
if (state == NUM_STATE_START || state == NUM_STATE_INT_FIRST_DIGIT) {
|
||||
/*
|
||||
* If the very first digit is 0,
|
||||
* it must be the last digit of the integer part
|
||||
* (no leading zeroes allowed).
|
||||
*/
|
||||
state = NUM_STATE_FRAC_OR_EXP;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
switch (state) {
|
||||
case NUM_STATE_START:
|
||||
case NUM_STATE_INT_FIRST_DIGIT:
|
||||
state = NUM_STATE_INT_DIGITS;
|
||||
break;
|
||||
|
||||
case NUM_STATE_FRAC_FIRST_DIGIT:
|
||||
state = NUM_STATE_FRAC_DIGITS;
|
||||
break;
|
||||
|
||||
case NUM_STATE_EXP_SIGN:
|
||||
case NUM_STATE_EXP_FIRST_DIGIT:
|
||||
state = NUM_STATE_EXP_DIGITS;
|
||||
break;
|
||||
|
||||
case NUM_STATE_INT_DIGITS:
|
||||
case NUM_STATE_FRAC_DIGITS:
|
||||
case NUM_STATE_EXP_DIGITS:
|
||||
/* stay in same state */
|
||||
break;
|
||||
|
||||
default:
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
break;
|
||||
|
||||
case '.':
|
||||
if (state != NUM_STATE_INT_DIGITS && state != NUM_STATE_FRAC_OR_EXP) {
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
state = NUM_STATE_FRAC_FIRST_DIGIT;
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
case 'E':
|
||||
switch (state) {
|
||||
case NUM_STATE_INT_DIGITS:
|
||||
case NUM_STATE_FRAC_OR_EXP:
|
||||
case NUM_STATE_FRAC_DIGITS:
|
||||
state = NUM_STATE_EXP_SIGN;
|
||||
break;
|
||||
default:
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
break;
|
||||
|
||||
case '-':
|
||||
if (state == NUM_STATE_START) {
|
||||
state = NUM_STATE_INT_FIRST_DIGIT;
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case '+':
|
||||
if (state == NUM_STATE_EXP_SIGN) {
|
||||
state = NUM_STATE_EXP_FIRST_DIGIT;
|
||||
} else {
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* Got an unexpected character - back up and stop parsing number.
|
||||
* The top-level parsing code will handle invalid trailing characters.
|
||||
*/
|
||||
p--;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
switch (state) {
|
||||
case NUM_STATE_INT_DIGITS:
|
||||
case NUM_STATE_FRAC_OR_EXP:
|
||||
case NUM_STATE_FRAC_DIGITS:
|
||||
case NUM_STATE_EXP_DIGITS:
|
||||
/* Valid end state */
|
||||
return p - start;
|
||||
|
||||
default:
|
||||
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;
|
||||
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) return SPDK_JSON_PARSE_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) return 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) return SPDK_JSON_PARSE_INVALID;
|
||||
rc = json_decode_string(data, json_end, &new_data, flags);
|
||||
if (rc < 0) return 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) return SPDK_JSON_PARSE_INVALID;
|
||||
rc = json_valid_number(data, json_end);
|
||||
if (rc < 0) return 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) return SPDK_JSON_PARSE_INVALID;
|
||||
if (depth == SPDK_JSON_MAX_NESTING_DEPTH) {
|
||||
return SPDK_JSON_PARSE_MAX_DEPTH_EXCEEDED;
|
||||
}
|
||||
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) return SPDK_JSON_PARSE_INVALID;
|
||||
if (depth == 0) return SPDK_JSON_PARSE_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) {
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
if (con_type != SPDK_JSON_VAL_OBJECT_BEGIN) {
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
ADD_VALUE(SPDK_JSON_VAL_OBJECT_END, data, data + 1);
|
||||
} else {
|
||||
if (state != STATE_VALUE && state != STATE_VALUE_SEPARATOR) {
|
||||
return SPDK_JSON_PARSE_INVALID;
|
||||
}
|
||||
if (con_type != SPDK_JSON_VAL_ARRAY_BEGIN) {
|
||||
return SPDK_JSON_PARSE_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) return SPDK_JSON_PARSE_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) return SPDK_JSON_PARSE_INVALID;
|
||||
data++;
|
||||
state = STATE_VALUE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return SPDK_JSON_PARSE_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 */
|
||||
if (end) {
|
||||
*end = data;
|
||||
}
|
||||
return SPDK_JSON_PARSE_INCOMPLETE;
|
||||
}
|
284
lib/json/json_util.c
Normal file
284
lib/json/json_util.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*-
|
||||
* 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 "json_internal.h"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_json_number_to_double(const struct spdk_json_val *val, double *num)
|
||||
{
|
||||
char buf[32];
|
||||
char *end;
|
||||
|
||||
if (val->type != SPDK_JSON_VAL_NUMBER || val->len >= sizeof(buf)) {
|
||||
*num = 0.0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(buf, val->start, val->len);
|
||||
buf[val->len] = '\0';
|
||||
|
||||
errno = 0;
|
||||
/* TODO: strtod() uses locale for decimal point (. is not guaranteed) */
|
||||
*num = strtod(buf, &end);
|
||||
if (*end != '\0' || errno != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num)
|
||||
{
|
||||
double dbl;
|
||||
|
||||
if (spdk_json_number_to_double(val, &dbl)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*num = (int32_t)dbl;
|
||||
if (dbl != (double)*num) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num)
|
||||
{
|
||||
double dbl;
|
||||
|
||||
if (spdk_json_number_to_double(val, &dbl)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dbl < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*num = (uint32_t)dbl;
|
||||
if (dbl != (double)*num) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (values == NULL || values->type != SPDK_JSON_VAL_ARRAY_BEGIN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (values->len >= max_size) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out_size = 0;
|
||||
field = out;
|
||||
for (i = 0; i < values->len;) {
|
||||
const struct spdk_json_val *v = &values[i + 1];
|
||||
|
||||
if (decode_func(v, field)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i += spdk_json_val_len(v);
|
||||
field += stride;
|
||||
(*out_size)++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_string(const struct spdk_json_val *val, void *out)
|
||||
{
|
||||
char **s = out;
|
||||
|
||||
if (*s) {
|
||||
free(*s);
|
||||
}
|
||||
|
||||
*s = spdk_json_strdup(val);
|
||||
|
||||
if (*s) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
365
lib/json/json_write.c
Normal file
365
lib/json/json_write.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*-
|
||||
* 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 "json_internal.h"
|
||||
|
||||
struct spdk_json_write_ctx {
|
||||
spdk_json_write_cb write_cb;
|
||||
void *cb_ctx;
|
||||
bool first_value;
|
||||
bool failed;
|
||||
};
|
||||
|
||||
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->first_value = true;
|
||||
w->failed = false;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_json_write_end(struct spdk_json_write_ctx *w)
|
||||
{
|
||||
bool failed;
|
||||
|
||||
if (w == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
failed = w->failed;
|
||||
|
||||
free(w);
|
||||
|
||||
return failed ? -1 : 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fail(struct spdk_json_write_ctx *w)
|
||||
{
|
||||
w->failed = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
emit(struct spdk_json_write_ctx *w, const void *data, size_t size)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = w->write_cb(w->cb_ctx, data, size);
|
||||
if (rc != 0) {
|
||||
return fail(w);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
begin_value(struct spdk_json_write_ctx *w)
|
||||
{
|
||||
// TODO: check for value state
|
||||
if (!w->first_value) {
|
||||
if (emit(w, ",", 1)) return fail(w);
|
||||
}
|
||||
w->first_value = 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);
|
||||
}
|
||||
|
||||
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 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;
|
||||
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).
|
||||
*/
|
||||
};
|
||||
|
||||
if (emit(w, "\"", 1)) return fail(w);
|
||||
|
||||
while (p != end) {
|
||||
int codepoint_len;
|
||||
uint32_t codepoint;
|
||||
uint16_t high, low;
|
||||
char out[13];
|
||||
size_t out_len;
|
||||
|
||||
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 (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;
|
||||
}
|
||||
|
||||
if (emit(w, out, out_len)) 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_array_begin(struct spdk_json_write_ctx *w)
|
||||
{
|
||||
if (begin_value(w)) return fail(w);
|
||||
w->first_value = true;
|
||||
return emit(w, "[", 1);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_json_write_array_end(struct spdk_json_write_ctx *w)
|
||||
{
|
||||
w->first_value = 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;
|
||||
return emit(w, "{", 1);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_json_write_object_end(struct spdk_json_write_ctx *w)
|
||||
{
|
||||
w->first_value = 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;
|
||||
return emit(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; i++) {
|
||||
if (spdk_json_write_val(w, &val[i + 1])) {
|
||||
return fail(w);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
60
mk/json.unittest.mk
Normal file
60
mk/json.unittest.mk
Normal file
@ -0,0 +1,60 @@
|
||||
#
|
||||
# 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 := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/..
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
JSON_DIR := $(SPDK_ROOT_DIR)/lib/json
|
||||
|
||||
C_SRCS = $(TEST_FILE) $(OTHER_FILES)
|
||||
|
||||
CFLAGS += -I$(JSON_DIR)
|
||||
CFLAGS += -I$(SPDK_ROOT_DIR)/lib
|
||||
CFLAGS += -I$(SPDK_ROOT_DIR)/test
|
||||
|
||||
LIBS += -lcunit -lpthread
|
||||
|
||||
APP = $(TEST_FILE:.c=)
|
||||
|
||||
all: $(APP)
|
||||
|
||||
$(APP) : $(OBJS)
|
||||
$(LINK_C)
|
||||
|
||||
clean:
|
||||
$(CLEAN_C) $(APP)
|
||||
|
||||
%.o: $(JSON_DIR)/%.c %.d $(MAKEFILE_LIST)
|
||||
$(COMPILE_C)
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(CURDIR)/../..
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
DIRS-y = nvme memory ioat
|
||||
DIRS-y = json nvme memory ioat
|
||||
|
||||
.PHONY: all clean $(DIRS-y)
|
||||
|
||||
|
44
test/lib/json/Makefile
Normal file
44
test/lib/json/Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
#
|
||||
# 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 := $(CURDIR)/../../..
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
DIRS-y = parse util write
|
||||
|
||||
.PHONY: all clean $(DIRS-y)
|
||||
|
||||
all: $(DIRS-y)
|
||||
clean: $(DIRS-y)
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk
|
15
test/lib/json/json.sh
Executable file
15
test/lib/json/json.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -xe
|
||||
|
||||
testdir=$(readlink -f $(dirname $0))
|
||||
rootdir=$testdir/../../..
|
||||
source $rootdir/scripts/autotest_common.sh
|
||||
|
||||
timing_enter json
|
||||
|
||||
$testdir/parse/json_parse_ut
|
||||
$testdir/util/json_util_ut
|
||||
$testdir/write/json_write_ut
|
||||
|
||||
timing_exit json
|
1
test/lib/json/parse/.gitignore
vendored
Normal file
1
test/lib/json/parse/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
json_parse_ut
|
38
test/lib/json/parse/Makefile
Normal file
38
test/lib/json/parse/Makefile
Normal file
@ -0,0 +1,38 @@
|
||||
#
|
||||
# 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 := $(CURDIR)/../../../..
|
||||
|
||||
TEST_FILE = json_parse_ut.c
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/json.unittest.mk
|
838
test/lib/json/parse/json_parse_ut.c
Normal file
838
test/lib/json/parse/json_parse_ut.c
Normal file
@ -0,0 +1,838 @@
|
||||
/*-
|
||||
* 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_cunit.h"
|
||||
|
||||
#include "json_parse.c"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint8_t g_buf[1000];
|
||||
static void *g_end;
|
||||
static struct spdk_json_val g_vals[100];
|
||||
static int g_cur_val;
|
||||
|
||||
/* Fill buf with raw data */
|
||||
#define BUF_SETUP(in) \
|
||||
memset(g_buf, 0, sizeof(g_buf)); \
|
||||
if (sizeof(in) > 1) { \
|
||||
memcpy(g_buf, in, sizeof(in) - 1); \
|
||||
} \
|
||||
g_end = NULL
|
||||
|
||||
/*
|
||||
* Do two checks - first pass NULL for values to ensure the count is correct,
|
||||
* then pass g_vals to get the actual values.
|
||||
*/
|
||||
#define PARSE_PASS(in, num_vals, trailing) \
|
||||
BUF_SETUP(in); \
|
||||
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, 0) == num_vals); \
|
||||
memset(g_vals, 0, sizeof(g_vals)); \
|
||||
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, g_vals, sizeof(g_vals), &g_end, SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE) == num_vals); \
|
||||
CU_ASSERT(g_end == g_buf + sizeof(in) - sizeof(trailing)); \
|
||||
CU_ASSERT(memcmp(g_end, trailing, sizeof(trailing) - 1) == 0); \
|
||||
g_cur_val = 0
|
||||
|
||||
#define PARSE_FAIL(in, retval) \
|
||||
BUF_SETUP(in); \
|
||||
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, 0) == retval)
|
||||
|
||||
#define VAL_STRING_MATCH(str, var_type) \
|
||||
CU_ASSERT(g_vals[g_cur_val].type == var_type); \
|
||||
CU_ASSERT(g_vals[g_cur_val].len == sizeof(str) - 1); \
|
||||
if (g_vals[g_cur_val].len == sizeof(str) - 1 && sizeof(str) > 1) { \
|
||||
CU_ASSERT(memcmp(g_vals[g_cur_val].start, str, g_vals[g_cur_val].len) == 0); \
|
||||
} \
|
||||
g_cur_val++
|
||||
|
||||
#define VAL_STRING(str) VAL_STRING_MATCH(str, SPDK_JSON_VAL_STRING)
|
||||
#define VAL_NAME(str) VAL_STRING_MATCH(str, SPDK_JSON_VAL_NAME)
|
||||
#define VAL_NUMBER(num) VAL_STRING_MATCH(num, SPDK_JSON_VAL_NUMBER)
|
||||
|
||||
#define VAL_LITERAL(str, val_type) \
|
||||
CU_ASSERT(g_vals[g_cur_val].type == val_type); \
|
||||
CU_ASSERT(g_vals[g_cur_val].len == strlen(str)); \
|
||||
if (g_vals[g_cur_val].len == strlen(str)) { \
|
||||
CU_ASSERT(memcmp(g_vals[g_cur_val].start, str, g_vals[g_cur_val].len) == 0); \
|
||||
} \
|
||||
g_cur_val++
|
||||
|
||||
#define VAL_TRUE() VAL_LITERAL("true", SPDK_JSON_VAL_TRUE)
|
||||
#define VAL_FALSE() VAL_LITERAL("false", SPDK_JSON_VAL_FALSE)
|
||||
#define VAL_NULL() VAL_LITERAL("null", SPDK_JSON_VAL_NULL)
|
||||
|
||||
#define VAL_ARRAY_BEGIN(count) \
|
||||
CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_ARRAY_BEGIN); \
|
||||
CU_ASSERT(g_vals[g_cur_val].len == count); \
|
||||
g_cur_val++
|
||||
|
||||
#define VAL_ARRAY_END() \
|
||||
CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_ARRAY_END); \
|
||||
g_cur_val++
|
||||
|
||||
#define VAL_OBJECT_BEGIN(count) \
|
||||
CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_OBJECT_BEGIN); \
|
||||
CU_ASSERT(g_vals[g_cur_val].len == count); \
|
||||
g_cur_val++
|
||||
|
||||
#define VAL_OBJECT_END() \
|
||||
CU_ASSERT(g_vals[g_cur_val].type == SPDK_JSON_VAL_OBJECT_END); \
|
||||
g_cur_val++
|
||||
|
||||
/* Simplified macros for string-only testing */
|
||||
#define STR_PASS(in, out) \
|
||||
PARSE_PASS("\"" in "\"", 1, ""); \
|
||||
VAL_STRING(out)
|
||||
|
||||
#define STR_FAIL(in, retval) \
|
||||
PARSE_FAIL("\"" in "\"", retval)
|
||||
|
||||
/* Simplified macros for number-only testing (no whitespace allowed) */
|
||||
#define NUM_PASS(in) \
|
||||
PARSE_PASS(in, 1, ""); \
|
||||
VAL_NUMBER(in)
|
||||
|
||||
#define NUM_FAIL(in, retval) \
|
||||
PARSE_FAIL(in, retval)
|
||||
|
||||
static void
|
||||
test_parse_literal(void)
|
||||
{
|
||||
PARSE_PASS("true", 1, "");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS(" true ", 1, "");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS("false", 1, "");
|
||||
VAL_FALSE();
|
||||
|
||||
PARSE_PASS("null", 1, "");
|
||||
VAL_NULL();
|
||||
|
||||
PARSE_PASS("trueaaa", 1, "aaa");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS("truefalse", 1, "false");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS("true false", 1, "false");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS("true,false", 1, ",false");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS("true,", 1, ",");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_FAIL("True", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("abcdef", SPDK_JSON_PARSE_INVALID);
|
||||
|
||||
PARSE_FAIL("t", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("tru", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("f", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("fals", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("n", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("nul", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_string_simple(void)
|
||||
{
|
||||
PARSE_PASS("\"\"", 1, "");
|
||||
VAL_STRING("");
|
||||
|
||||
PARSE_PASS("\"hello world\"", 1, "");
|
||||
VAL_STRING("hello world");
|
||||
|
||||
PARSE_PASS(" \"hello world\" ", 1, "");
|
||||
VAL_STRING("hello world");
|
||||
|
||||
/* Unterminated string */
|
||||
PARSE_FAIL("\"hello world", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
|
||||
/* Trailing comma */
|
||||
PARSE_PASS("\"hello world\",", 1, ",");
|
||||
VAL_STRING("hello world");
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_string_control_chars(void)
|
||||
{
|
||||
/* U+0000 through U+001F must be escaped */
|
||||
STR_FAIL("\x00", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x01", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x02", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x03", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x04", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x05", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x06", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x07", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x08", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x09", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x0A", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x0B", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x0C", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x0D", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x0E", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x0F", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x10", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x11", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x12", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x13", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x14", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x15", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x16", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x17", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x18", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x19", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x1A", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x1B", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x1C", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x1D", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x1E", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\x1F", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS(" ", " "); /* \x20 (first valid unescaped char) */
|
||||
|
||||
/* Test control chars in the middle of a string */
|
||||
STR_FAIL("abc\ndef", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("abc\tdef", SPDK_JSON_PARSE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_string_utf8(void)
|
||||
{
|
||||
/* Valid one-, two-, three-, and four-byte sequences */
|
||||
STR_PASS("\x41", "A");
|
||||
STR_PASS("\xC3\xB6", "\xC3\xB6");
|
||||
STR_PASS("\xE2\x88\x9A", "\xE2\x88\x9A");
|
||||
STR_PASS("\xF0\xA0\x9C\x8E", "\xF0\xA0\x9C\x8E");
|
||||
|
||||
/* Examples from RFC 3629 */
|
||||
STR_PASS("\x41\xE2\x89\xA2\xCE\x91\x2E", "\x41\xE2\x89\xA2\xCE\x91\x2E");
|
||||
STR_PASS("\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4", "\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4");
|
||||
STR_PASS("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E", "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E");
|
||||
STR_PASS("\xEF\xBB\xBF\xF0\xA3\x8E\xB4", "\xEF\xBB\xBF\xF0\xA3\x8E\xB4");
|
||||
|
||||
/* Edge cases */
|
||||
STR_PASS("\x7F", "\x7F");
|
||||
STR_FAIL("\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xC1", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xC2", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xC2\x80", "\xC2\x80");
|
||||
STR_PASS("\xC2\xBF", "\xC2\xBF");
|
||||
STR_PASS("\xDF\x80", "\xDF\x80");
|
||||
STR_PASS("\xDF\xBF", "\xDF\xBF");
|
||||
STR_FAIL("\xDF", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE0\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE0\x1F", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE0\x1F\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE0\xA0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xE0\xA0\x80", "\xE0\xA0\x80");
|
||||
STR_PASS("\xE0\xA0\xBF", "\xE0\xA0\xBF");
|
||||
STR_FAIL("\xE0\xA0\xC0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xE0\xBF\x80", "\xE0\xBF\x80");
|
||||
STR_PASS("\xE0\xBF\xBF", "\xE0\xBF\xBF");
|
||||
STR_FAIL("\xE0\xC0\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE1", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE1\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE1\x7F\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE1\x80\x7F", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xE1\x80\x80", "\xE1\x80\x80");
|
||||
STR_PASS("\xE1\x80\xBF", "\xE1\x80\xBF");
|
||||
STR_PASS("\xE1\xBF\x80", "\xE1\xBF\x80");
|
||||
STR_PASS("\xE1\xBF\xBF", "\xE1\xBF\xBF");
|
||||
STR_FAIL("\xE1\xC0\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xE1\x80\xC0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xEF\x80\x80", "\xEF\x80\x80");
|
||||
STR_PASS("\xEF\xBF\xBF", "\xEF\xBF\xBF");
|
||||
STR_FAIL("\xF0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF0\x90", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF0\x90\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF0\x80\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF0\x8F\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xF0\x90\x80\x80", "\xF0\x90\x80\x80");
|
||||
STR_PASS("\xF0\x90\x80\xBF", "\xF0\x90\x80\xBF");
|
||||
STR_PASS("\xF0\x90\xBF\x80", "\xF0\x90\xBF\x80");
|
||||
STR_PASS("\xF0\xBF\x80\x80", "\xF0\xBF\x80\x80");
|
||||
STR_FAIL("\xF0\xC0\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF1", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF1\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF1\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF1\x80\x80\x7F", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xF1\x80\x80\x80", "\xF1\x80\x80\x80");
|
||||
STR_PASS("\xF1\x80\x80\xBF", "\xF1\x80\x80\xBF");
|
||||
STR_PASS("\xF1\x80\xBF\x80", "\xF1\x80\xBF\x80");
|
||||
STR_PASS("\xF1\xBF\x80\x80", "\xF1\xBF\x80\x80");
|
||||
STR_PASS("\xF3\x80\x80\x80", "\xF3\x80\x80\x80");
|
||||
STR_FAIL("\xF3\xC0\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF3\x80\xC0\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF3\x80\x80\xC0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF4", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF4\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF4\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_PASS("\xF4\x80\x80\x80", "\xF4\x80\x80\x80");
|
||||
STR_PASS("\xF4\x8F\x80\x80", "\xF4\x8F\x80\x80");
|
||||
STR_PASS("\xF4\x8F\xBF\xBF", "\xF4\x8F\xBF\xBF");
|
||||
STR_FAIL("\xF4\x90\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF5", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF5\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF5\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF5\x80\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\xF5\x80\x80\x80\x80", SPDK_JSON_PARSE_INVALID);
|
||||
|
||||
/* Overlong encodings */
|
||||
STR_FAIL("\xC0\x80", SPDK_JSON_PARSE_INVALID);
|
||||
|
||||
/* Surrogate pairs */
|
||||
STR_FAIL("\xED\xA0\x80", SPDK_JSON_PARSE_INVALID); /* U+D800 First high surrogate */
|
||||
STR_FAIL("\xED\xAF\xBF", SPDK_JSON_PARSE_INVALID); /* U+DBFF Last high surrogate */
|
||||
STR_FAIL("\xED\xB0\x80", SPDK_JSON_PARSE_INVALID); /* U+DC00 First low surrogate */
|
||||
STR_FAIL("\xED\xBF\xBF", SPDK_JSON_PARSE_INVALID); /* U+DFFF Last low surrogate */
|
||||
STR_FAIL("\xED\xA1\x8C\xED\xBE\xB4",
|
||||
SPDK_JSON_PARSE_INVALID); /* U+233B4 (invalid surrogate pair encoding) */
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_string_escapes_twochar(void)
|
||||
{
|
||||
STR_PASS("\\\"", "\"");
|
||||
STR_PASS("\\\\", "\\");
|
||||
STR_PASS("\\/", "/");
|
||||
STR_PASS("\\b", "\b");
|
||||
STR_PASS("\\f", "\f");
|
||||
STR_PASS("\\n", "\n");
|
||||
STR_PASS("\\r", "\r");
|
||||
STR_PASS("\\t", "\t");
|
||||
|
||||
STR_PASS("abc\\tdef", "abc\tdef");
|
||||
STR_PASS("abc\\\"def", "abc\"def");
|
||||
|
||||
/* Backslash at end of string (will be treated as escaped quote) */
|
||||
STR_FAIL("\\", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
STR_FAIL("abc\\", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
|
||||
/* Invalid C-like escapes */
|
||||
STR_FAIL("\\a", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\v", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\'", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\?", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\x00", SPDK_JSON_PARSE_INVALID);
|
||||
|
||||
/* Other invalid escapes */
|
||||
STR_FAIL("\\B", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\z", SPDK_JSON_PARSE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_string_escapes_unicode(void)
|
||||
{
|
||||
STR_PASS("\\u0000", "\0");
|
||||
STR_PASS("\\u0001", "\1");
|
||||
STR_PASS("\\u0041", "A");
|
||||
STR_PASS("\\uAAAA", "\xEA\xAA\xAA");
|
||||
STR_PASS("\\uaaaa", "\xEA\xAA\xAA");
|
||||
STR_PASS("\\uAaAa", "\xEA\xAA\xAA");
|
||||
|
||||
STR_FAIL("\\u", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\u0", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\u00", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\u000", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\u000g", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\U", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\U0000", SPDK_JSON_PARSE_INVALID);
|
||||
|
||||
PARSE_FAIL("\"\\u", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\u0", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\u00", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\u000", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
|
||||
/* Surrogate pair */
|
||||
STR_PASS("\\uD834\\uDD1E", "\xF0\x9D\x84\x9E");
|
||||
|
||||
/* Low surrogate without high */
|
||||
STR_FAIL("\\uDC00", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\uDC00\\uDC00", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\uDC00abcdef", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\uDEAD", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("\"\\uD834", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\uD834\\", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\uD834\\u", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\uD834\\uD", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("\"\\uD834\\uDD1", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
|
||||
/* High surrogate without low */
|
||||
STR_FAIL("\\uD800", SPDK_JSON_PARSE_INVALID);
|
||||
STR_FAIL("\\uD800abcdef", SPDK_JSON_PARSE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_number(void)
|
||||
{
|
||||
NUM_PASS("0");
|
||||
NUM_PASS("1");
|
||||
NUM_PASS("100");
|
||||
NUM_PASS("-1");
|
||||
NUM_PASS("-0");
|
||||
NUM_PASS("3.0");
|
||||
NUM_PASS("3.00");
|
||||
NUM_PASS("3.001");
|
||||
NUM_PASS("3.14159");
|
||||
NUM_PASS("3.141592653589793238462643383279");
|
||||
NUM_PASS("1e400");
|
||||
NUM_PASS("1E400");
|
||||
NUM_PASS("0e10");
|
||||
NUM_PASS("0e0");
|
||||
NUM_PASS("-0e0");
|
||||
NUM_PASS("-0e+0");
|
||||
NUM_PASS("-0e-0");
|
||||
NUM_PASS("1e+400");
|
||||
NUM_PASS("1e-400");
|
||||
NUM_PASS("6.022e23");
|
||||
NUM_PASS("-1.234e+56");
|
||||
NUM_PASS("1.23e+56");
|
||||
NUM_PASS("-1.23e-56");
|
||||
NUM_PASS("1.23e-56");
|
||||
NUM_PASS("1e04");
|
||||
|
||||
/* Trailing garbage */
|
||||
PARSE_PASS("0A", 1, "A");
|
||||
VAL_NUMBER("0");
|
||||
|
||||
PARSE_PASS("0,", 1, ",");
|
||||
VAL_NUMBER("0");
|
||||
|
||||
PARSE_PASS("0true", 1, "true");
|
||||
VAL_NUMBER("0");
|
||||
|
||||
NUM_FAIL("00", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("007", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("345.", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
NUM_FAIL("345.678.1", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("+1", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("--1", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("3.", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
NUM_FAIL("3.+4", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("3.2e+-4", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("3.2e-+4", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("3.2e-4+5", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("3.4.5", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("3e+", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
NUM_FAIL("3e-", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
NUM_FAIL("3.e4", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL("-", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
NUM_FAIL("NaN", SPDK_JSON_PARSE_INVALID);
|
||||
NUM_FAIL(".123", SPDK_JSON_PARSE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_array(void)
|
||||
{
|
||||
PARSE_PASS("[]", 2, "");
|
||||
VAL_ARRAY_BEGIN(0);
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("[true]", 3, "");
|
||||
VAL_ARRAY_BEGIN(1);
|
||||
VAL_TRUE();
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("[true, false]", 4, "");
|
||||
VAL_ARRAY_BEGIN(2);
|
||||
VAL_TRUE();
|
||||
VAL_FALSE();
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("[\"hello\"]", 3, "");
|
||||
VAL_ARRAY_BEGIN(1);
|
||||
VAL_STRING("hello");
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("[[]]", 4, "");
|
||||
VAL_ARRAY_BEGIN(2);
|
||||
VAL_ARRAY_BEGIN(0);
|
||||
VAL_ARRAY_END();
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("[\"hello\", \"world\"]", 4, "");
|
||||
VAL_ARRAY_BEGIN(2);
|
||||
VAL_STRING("hello");
|
||||
VAL_STRING("world");
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("[],", 2, ",");
|
||||
VAL_ARRAY_BEGIN(0);
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_FAIL("]", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("[", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("[true", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("[\"hello", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("[\"hello\"", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("[true,]", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("[,]", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("[,true]", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("[true}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("[true,,true]", SPDK_JSON_PARSE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_object(void)
|
||||
{
|
||||
PARSE_PASS("{}", 2, "");
|
||||
VAL_OBJECT_BEGIN(0);
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"a\": true}", 4, "");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("a");
|
||||
VAL_TRUE();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"abc\": \"def\"}", 4, "");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("abc");
|
||||
VAL_STRING("def");
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"a\": true, \"b\": false}", 6, "");
|
||||
VAL_OBJECT_BEGIN(4);
|
||||
VAL_NAME("a");
|
||||
VAL_TRUE();
|
||||
VAL_NAME("b");
|
||||
VAL_FALSE();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"a\": { \"b\": true } }", 7, "");
|
||||
VAL_OBJECT_BEGIN(5);
|
||||
VAL_NAME("a");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("b");
|
||||
VAL_TRUE();
|
||||
VAL_OBJECT_END();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"{test\": 0}", 4, "");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("{test");
|
||||
VAL_NUMBER("0");
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"test}\": 1}", 4, "");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("test}");
|
||||
VAL_NUMBER("1");
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"\\\"\": 2}", 4, "");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("\"");
|
||||
VAL_NUMBER("2");
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"a\":true},", 4, ",");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("a");
|
||||
VAL_TRUE();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
/* Object end without object begin (trailing garbage) */
|
||||
PARSE_PASS("true}", 1, "}");
|
||||
VAL_TRUE();
|
||||
|
||||
PARSE_PASS("0}", 1, "}");
|
||||
VAL_NUMBER("0");
|
||||
|
||||
PARSE_PASS("\"a\"}", 1, "}");
|
||||
VAL_STRING("a");
|
||||
|
||||
PARSE_FAIL("}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\"", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\":", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\":true", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\":true,", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\":true,}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{\"a\":true,\"}", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\":true,\"b}", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
PARSE_FAIL("{\"a\":true,\"b\"}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{\"a\":true,\"b\":}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{\"a\":true,\"b\",}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{\"a\",}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{,\"a\": true}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{a:true}", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{'a':true}", SPDK_JSON_PARSE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
test_parse_nesting(void)
|
||||
{
|
||||
PARSE_PASS("[[[[[[[[]]]]]]]]", 16, "");
|
||||
|
||||
PARSE_PASS("{\"a\": [0, 1, 2]}", 8, "");
|
||||
VAL_OBJECT_BEGIN(6);
|
||||
VAL_NAME("a");
|
||||
VAL_ARRAY_BEGIN(3);
|
||||
VAL_NUMBER("0");
|
||||
VAL_NUMBER("1");
|
||||
VAL_NUMBER("2");
|
||||
VAL_ARRAY_END();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"a\": [0, 1, 2], \"b\": 3 }", 10, "");
|
||||
VAL_OBJECT_BEGIN(8);
|
||||
VAL_NAME("a");
|
||||
VAL_ARRAY_BEGIN(3);
|
||||
VAL_NUMBER("0");
|
||||
VAL_NUMBER("1");
|
||||
VAL_NUMBER("2");
|
||||
VAL_ARRAY_END();
|
||||
VAL_NAME("b");
|
||||
VAL_NUMBER("3");
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("[0, 1, {\"a\": 3}, 4, 5]", 10, "");
|
||||
VAL_ARRAY_BEGIN(8);
|
||||
VAL_NUMBER("0");
|
||||
VAL_NUMBER("1");
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("a");
|
||||
VAL_NUMBER("3");
|
||||
VAL_OBJECT_END();
|
||||
VAL_NUMBER("4");
|
||||
VAL_NUMBER("5");
|
||||
VAL_ARRAY_END();
|
||||
|
||||
PARSE_PASS("\t[ { \"a\": {\"b\": [ {\"c\": 1}, 2 ],\n\"d\": 3}, \"e\" : 4}, 5 ] ", 20, "");
|
||||
VAL_ARRAY_BEGIN(18);
|
||||
VAL_OBJECT_BEGIN(15);
|
||||
VAL_NAME("a");
|
||||
VAL_OBJECT_BEGIN(10);
|
||||
VAL_NAME("b");
|
||||
VAL_ARRAY_BEGIN(5);
|
||||
VAL_OBJECT_BEGIN(2);
|
||||
VAL_NAME("c");
|
||||
VAL_NUMBER("1");
|
||||
VAL_OBJECT_END();
|
||||
VAL_NUMBER("2");
|
||||
VAL_ARRAY_END();
|
||||
VAL_NAME("d");
|
||||
VAL_NUMBER("3");
|
||||
VAL_OBJECT_END();
|
||||
VAL_NAME("e");
|
||||
VAL_NUMBER("4");
|
||||
VAL_OBJECT_END();
|
||||
VAL_NUMBER("5");
|
||||
VAL_ARRAY_END();
|
||||
|
||||
/* Examples from RFC 7159 */
|
||||
PARSE_PASS(
|
||||
"{\n"
|
||||
" \"Image\": {\n"
|
||||
" \"Width\": 800,\n"
|
||||
" \"Height\": 600,\n"
|
||||
" \"Title\": \"View from 15th Floor\",\n"
|
||||
" \"Thumbnail\": {\n"
|
||||
" \"Url\": \"http://www.example.com/image/481989943\",\n"
|
||||
" \"Height\": 125,\n"
|
||||
" \"Width\": 100\n"
|
||||
" },\n"
|
||||
" \"Animated\" : false,\n"
|
||||
" \"IDs\": [116, 943, 234, 38793]\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
29, "");
|
||||
|
||||
VAL_OBJECT_BEGIN(27);
|
||||
VAL_NAME("Image");
|
||||
VAL_OBJECT_BEGIN(24);
|
||||
VAL_NAME("Width");
|
||||
VAL_NUMBER("800");
|
||||
VAL_NAME("Height");
|
||||
VAL_NUMBER("600");
|
||||
VAL_NAME("Title");
|
||||
VAL_STRING("View from 15th Floor");
|
||||
VAL_NAME("Thumbnail");
|
||||
VAL_OBJECT_BEGIN(6);
|
||||
VAL_NAME("Url");
|
||||
VAL_STRING("http://www.example.com/image/481989943");
|
||||
VAL_NAME("Height");
|
||||
VAL_NUMBER("125");
|
||||
VAL_NAME("Width");
|
||||
VAL_NUMBER("100");
|
||||
VAL_OBJECT_END();
|
||||
VAL_NAME("Animated");
|
||||
VAL_FALSE();
|
||||
VAL_NAME("IDs");
|
||||
VAL_ARRAY_BEGIN(4);
|
||||
VAL_NUMBER("116");
|
||||
VAL_NUMBER("943");
|
||||
VAL_NUMBER("234");
|
||||
VAL_NUMBER("38793");
|
||||
VAL_ARRAY_END();
|
||||
VAL_OBJECT_END();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS(
|
||||
"[\n"
|
||||
" {\n"
|
||||
" \"precision\": \"zip\",\n"
|
||||
" \"Latitude\": 37.7668,\n"
|
||||
" \"Longitude\": -122.3959,\n"
|
||||
" \"Address\": \"\",\n"
|
||||
" \"City\": \"SAN FRANCISCO\",\n"
|
||||
" \"State\": \"CA\",\n"
|
||||
" \"Zip\": \"94107\",\n"
|
||||
" \"Country\": \"US\"\n"
|
||||
" },\n"
|
||||
" {\n"
|
||||
" \"precision\": \"zip\",\n"
|
||||
" \"Latitude\": 37.371991,\n"
|
||||
" \"Longitude\": -122.026020,\n"
|
||||
" \"Address\": \"\",\n"
|
||||
" \"City\": \"SUNNYVALE\",\n"
|
||||
" \"State\": \"CA\",\n"
|
||||
" \"Zip\": \"94085\",\n"
|
||||
" \"Country\": \"US\"\n"
|
||||
" }\n"
|
||||
"]",
|
||||
38, "");
|
||||
|
||||
VAL_ARRAY_BEGIN(36);
|
||||
VAL_OBJECT_BEGIN(16);
|
||||
VAL_NAME("precision");
|
||||
VAL_STRING("zip");
|
||||
VAL_NAME("Latitude");
|
||||
VAL_NUMBER("37.7668");
|
||||
VAL_NAME("Longitude");
|
||||
VAL_NUMBER("-122.3959");
|
||||
VAL_NAME("Address");
|
||||
VAL_STRING("");
|
||||
VAL_NAME("City");
|
||||
VAL_STRING("SAN FRANCISCO");
|
||||
VAL_NAME("State");
|
||||
VAL_STRING("CA");
|
||||
VAL_NAME("Zip");
|
||||
VAL_STRING("94107");
|
||||
VAL_NAME("Country");
|
||||
VAL_STRING("US");
|
||||
VAL_OBJECT_END();
|
||||
VAL_OBJECT_BEGIN(16);
|
||||
VAL_NAME("precision");
|
||||
VAL_STRING("zip");
|
||||
VAL_NAME("Latitude");
|
||||
VAL_NUMBER("37.371991");
|
||||
VAL_NAME("Longitude");
|
||||
VAL_NUMBER("-122.026020");
|
||||
VAL_NAME("Address");
|
||||
VAL_STRING("");
|
||||
VAL_NAME("City");
|
||||
VAL_STRING("SUNNYVALE");
|
||||
VAL_NAME("State");
|
||||
VAL_STRING("CA");
|
||||
VAL_NAME("Zip");
|
||||
VAL_STRING("94085");
|
||||
VAL_NAME("Country");
|
||||
VAL_STRING("US");
|
||||
VAL_OBJECT_END();
|
||||
VAL_ARRAY_END();
|
||||
|
||||
/* Trailing garbage */
|
||||
PARSE_PASS("{\"a\": [0, 1, 2]}]", 8, "]");
|
||||
VAL_OBJECT_BEGIN(6);
|
||||
VAL_NAME("a");
|
||||
VAL_ARRAY_BEGIN(3);
|
||||
VAL_NUMBER("0");
|
||||
VAL_NUMBER("1");
|
||||
VAL_NUMBER("2");
|
||||
VAL_ARRAY_END();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_PASS("{\"a\": [0, 1, 2]}}", 8, "}");
|
||||
PARSE_PASS("{\"a\": [0, 1, 2]}]", 8, "]");
|
||||
VAL_OBJECT_BEGIN(6);
|
||||
VAL_NAME("a");
|
||||
VAL_ARRAY_BEGIN(3);
|
||||
VAL_NUMBER("0");
|
||||
VAL_NUMBER("1");
|
||||
VAL_NUMBER("2");
|
||||
VAL_ARRAY_END();
|
||||
VAL_OBJECT_END();
|
||||
|
||||
PARSE_FAIL("{\"a\": [0, 1, 2}]", SPDK_JSON_PARSE_INVALID);
|
||||
PARSE_FAIL("{\"a\": [0, 1, 2]", SPDK_JSON_PARSE_INCOMPLETE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CU_pSuite suite = NULL;
|
||||
unsigned int num_failures;
|
||||
|
||||
if (CU_initialize_registry() != CUE_SUCCESS) {
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
suite = CU_add_suite("json", NULL, NULL);
|
||||
if (suite == NULL) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
if (
|
||||
CU_add_test(suite, "parse_literal", test_parse_literal) == NULL ||
|
||||
CU_add_test(suite, "parse_string_simple", test_parse_string_simple) == NULL ||
|
||||
CU_add_test(suite, "parse_string_control_chars", test_parse_string_control_chars) == NULL ||
|
||||
CU_add_test(suite, "parse_string_utf8", test_parse_string_utf8) == NULL ||
|
||||
CU_add_test(suite, "parse_string_escapes_twochar", test_parse_string_escapes_twochar) == NULL ||
|
||||
CU_add_test(suite, "parse_string_escapes_unicode", test_parse_string_escapes_unicode) == NULL ||
|
||||
CU_add_test(suite, "parse_number", test_parse_number) == NULL ||
|
||||
CU_add_test(suite, "parse_array", test_parse_array) == NULL ||
|
||||
CU_add_test(suite, "parse_object", test_parse_object) == NULL ||
|
||||
CU_add_test(suite, "parse_nesting", test_parse_nesting) == NULL) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||
|
||||
CU_basic_run_tests();
|
||||
|
||||
num_failures = CU_get_number_of_failures();
|
||||
CU_cleanup_registry();
|
||||
|
||||
return num_failures;
|
||||
}
|
1
test/lib/json/util/.gitignore
vendored
Normal file
1
test/lib/json/util/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
json_util_ut
|
38
test/lib/json/util/Makefile
Normal file
38
test/lib/json/util/Makefile
Normal file
@ -0,0 +1,38 @@
|
||||
#
|
||||
# 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 := $(CURDIR)/../../../..
|
||||
|
||||
TEST_FILE = json_util_ut.c
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/json.unittest.mk
|
137
test/lib/json/util/json_util_ut.c
Normal file
137
test/lib/json/util/json_util_ut.c
Normal file
@ -0,0 +1,137 @@
|
||||
/*-
|
||||
* 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_cunit.h"
|
||||
|
||||
#include "json_util.c"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NUM_SETUP(x) \
|
||||
snprintf(buf, sizeof(buf), "%s", x); \
|
||||
v.type = SPDK_JSON_VAL_NUMBER; \
|
||||
v.start = buf; \
|
||||
v.len = sizeof(x) - 1
|
||||
|
||||
#define NUM_INT32_PASS(s, i) \
|
||||
NUM_SETUP(s); \
|
||||
CU_ASSERT(spdk_json_number_to_int32(&v, &i32) == 0); \
|
||||
CU_ASSERT(i32 == i)
|
||||
|
||||
#define NUM_INT32_FAIL(s) \
|
||||
NUM_SETUP(s); \
|
||||
CU_ASSERT(spdk_json_number_to_int32(&v, &i32) != 0)
|
||||
|
||||
static void
|
||||
test_strequal(void)
|
||||
{
|
||||
struct spdk_json_val v;
|
||||
|
||||
v.type = SPDK_JSON_VAL_STRING;
|
||||
v.start = "test";
|
||||
v.len = sizeof("test") - 1;
|
||||
CU_ASSERT(spdk_json_strequal(&v, "test") == true);
|
||||
CU_ASSERT(spdk_json_strequal(&v, "TEST") == false);
|
||||
CU_ASSERT(spdk_json_strequal(&v, "hello") == false);
|
||||
CU_ASSERT(spdk_json_strequal(&v, "t") == false);
|
||||
|
||||
v.type = SPDK_JSON_VAL_NAME;
|
||||
CU_ASSERT(spdk_json_strequal(&v, "test") == true);
|
||||
|
||||
v.type = SPDK_JSON_VAL_NUMBER;
|
||||
CU_ASSERT(spdk_json_strequal(&v, "test") == false);
|
||||
|
||||
v.type = SPDK_JSON_VAL_STRING;
|
||||
v.start = "test\0hello";
|
||||
v.len = sizeof("test\0hello") - 1;
|
||||
CU_ASSERT(spdk_json_strequal(&v, "test") == false);
|
||||
}
|
||||
|
||||
static void
|
||||
test_num_to_int32(void)
|
||||
{
|
||||
struct spdk_json_val v;
|
||||
char buf[100];
|
||||
int32_t i32;
|
||||
|
||||
NUM_SETUP("1234");
|
||||
CU_ASSERT(spdk_json_number_to_int32(&v, &i32) == 0);
|
||||
CU_ASSERT(i32 == 1234);
|
||||
|
||||
|
||||
NUM_INT32_PASS("0", 0);
|
||||
NUM_INT32_PASS("1234", 1234);
|
||||
NUM_INT32_PASS("-1234", -1234);
|
||||
NUM_INT32_PASS("1234.00000", 1234);
|
||||
NUM_INT32_PASS("1.2e1", 12);
|
||||
NUM_INT32_PASS("12340e-1", 1234);
|
||||
NUM_INT32_PASS("-0", 0);
|
||||
|
||||
NUM_INT32_FAIL("1.2");
|
||||
NUM_INT32_FAIL("1.2E0");
|
||||
NUM_INT32_FAIL("1.234e1");
|
||||
NUM_INT32_FAIL("12341e-1");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CU_pSuite suite = NULL;
|
||||
unsigned int num_failures;
|
||||
|
||||
if (CU_initialize_registry() != CUE_SUCCESS) {
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
suite = CU_add_suite("json", NULL, NULL);
|
||||
if (suite == NULL) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
if (
|
||||
CU_add_test(suite, "strequal", test_strequal) == NULL ||
|
||||
CU_add_test(suite, "num_to_int32", test_num_to_int32) == NULL) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||
|
||||
CU_basic_run_tests();
|
||||
|
||||
num_failures = CU_get_number_of_failures();
|
||||
CU_cleanup_registry();
|
||||
|
||||
return num_failures;
|
||||
}
|
1
test/lib/json/write/.gitignore
vendored
Normal file
1
test/lib/json/write/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
json_write_ut
|
38
test/lib/json/write/Makefile
Normal file
38
test/lib/json/write/Makefile
Normal file
@ -0,0 +1,38 @@
|
||||
#
|
||||
# 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 := $(CURDIR)/../../../..
|
||||
|
||||
TEST_FILE = json_write_ut.c
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/json.unittest.mk
|
619
test/lib/json/write/json_write_ut.c
Normal file
619
test/lib/json/write/json_write_ut.c
Normal file
@ -0,0 +1,619 @@
|
||||
/*-
|
||||
* 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_cunit.h"
|
||||
|
||||
#include "json_write.c"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static uint8_t g_buf[1000];
|
||||
static uint8_t *g_write_pos;
|
||||
|
||||
static int
|
||||
write_cb(void *cb_ctx, const void *data, size_t size)
|
||||
{
|
||||
size_t buf_free = g_buf + sizeof(g_buf) - g_write_pos;
|
||||
|
||||
if (size > buf_free) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(g_write_pos, data, size);
|
||||
g_write_pos += size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BEGIN() \
|
||||
memset(g_buf, 0, sizeof(g_buf)); \
|
||||
g_write_pos = g_buf; \
|
||||
w = spdk_json_write_begin(write_cb, NULL, 0); \
|
||||
CU_ASSERT_FATAL(w != NULL)
|
||||
|
||||
#define END(json) \
|
||||
CU_ASSERT(spdk_json_write_end(w) == 0); \
|
||||
CU_ASSERT(g_write_pos - g_buf == sizeof(json) - 1); \
|
||||
CU_ASSERT(memcmp(json, g_buf, sizeof(json) - 1) == 0)
|
||||
|
||||
#define END_NOCMP() \
|
||||
CU_ASSERT(spdk_json_write_end(w) == 0)
|
||||
|
||||
#define END_FAIL() \
|
||||
CU_ASSERT(spdk_json_write_end(w) < 0)
|
||||
|
||||
#define VAL_STRING(str) \
|
||||
CU_ASSERT(spdk_json_write_string_raw(w, str, sizeof(str) - 1) == 0)
|
||||
|
||||
#define VAL_STRING_FAIL(str) \
|
||||
CU_ASSERT(spdk_json_write_string_raw(w, str, sizeof(str) - 1) < 0)
|
||||
|
||||
#define STR_PASS(in, out) \
|
||||
BEGIN(); VAL_STRING(in); END("\"" out "\"")
|
||||
|
||||
#define STR_FAIL(in) \
|
||||
BEGIN(); VAL_STRING_FAIL(in); END_FAIL()
|
||||
|
||||
#define VAL_NAME(name) \
|
||||
CU_ASSERT(spdk_json_write_name_raw(w, name, sizeof(name) - 1) == 0)
|
||||
|
||||
#define VAL_NULL() CU_ASSERT(spdk_json_write_null(w) == 0)
|
||||
#define VAL_TRUE() CU_ASSERT(spdk_json_write_bool(w, true) == 0)
|
||||
#define VAL_FALSE() CU_ASSERT(spdk_json_write_bool(w, false) == 0)
|
||||
|
||||
#define VAL_INT32(i) CU_ASSERT(spdk_json_write_int32(w, i) == 0);
|
||||
#define VAL_UINT32(u) CU_ASSERT(spdk_json_write_uint32(w, u) == 0);
|
||||
|
||||
#define VAL_ARRAY_BEGIN() CU_ASSERT(spdk_json_write_array_begin(w) == 0)
|
||||
#define VAL_ARRAY_END() CU_ASSERT(spdk_json_write_array_end(w) == 0)
|
||||
|
||||
#define VAL_OBJECT_BEGIN() CU_ASSERT(spdk_json_write_object_begin(w) == 0)
|
||||
#define VAL_OBJECT_END() CU_ASSERT(spdk_json_write_object_end(w) == 0)
|
||||
|
||||
static void
|
||||
test_write_literal(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
BEGIN();
|
||||
VAL_NULL();
|
||||
END("null");
|
||||
|
||||
BEGIN();
|
||||
VAL_TRUE();
|
||||
END("true");
|
||||
|
||||
BEGIN();
|
||||
VAL_FALSE();
|
||||
END("false");
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_string_simple(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
STR_PASS("hello world", "hello world");
|
||||
STR_PASS(" ", " ");
|
||||
STR_PASS("~", "~");
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_string_escapes(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
/* Two-character escapes */
|
||||
STR_PASS("\b", "\\b");
|
||||
STR_PASS("\f", "\\f");
|
||||
STR_PASS("\n", "\\n");
|
||||
STR_PASS("\r", "\\r");
|
||||
STR_PASS("\t", "\\t");
|
||||
STR_PASS("\"", "\\\"");
|
||||
STR_PASS("\\", "\\\\");
|
||||
|
||||
/* JSON defines an escape for forward slash, but it is optional */
|
||||
STR_PASS("/", "/");
|
||||
|
||||
STR_PASS("hello\nworld", "hello\\nworld");
|
||||
|
||||
STR_PASS("\x00", "\\u0000");
|
||||
STR_PASS("\x01", "\\u0001");
|
||||
STR_PASS("\x02", "\\u0002");
|
||||
|
||||
STR_PASS("\xC3\xB6", "\\u00F6");
|
||||
STR_PASS("\xE2\x88\x9A", "\\u221A");
|
||||
STR_PASS("\xEA\xAA\xAA", "\\uAAAA");
|
||||
|
||||
/* Surrogate pairs */
|
||||
STR_PASS("\xF0\x9D\x84\x9E", "\\uD834\\uDD1E");
|
||||
STR_PASS("\xF0\xA0\x9C\x8E", "\\uD841\\uDF0E");
|
||||
|
||||
/* Examples from RFC 3629 */
|
||||
STR_PASS("\x41\xE2\x89\xA2\xCE\x91\x2E", "A\\u2262\\u0391.");
|
||||
STR_PASS("\xED\x95\x9C\xEA\xB5\xAD\xEC\x96\xB4", "\\uD55C\\uAD6D\\uC5B4");
|
||||
STR_PASS("\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E", "\\u65E5\\u672C\\u8A9E");
|
||||
STR_PASS("\xEF\xBB\xBF\xF0\xA3\x8E\xB4", "\\uFEFF\\uD84C\\uDFB4");
|
||||
|
||||
/* UTF-8 edge cases */
|
||||
STR_PASS("\x7F", "\\u007F");
|
||||
STR_FAIL("\x80");
|
||||
STR_FAIL("\xC1");
|
||||
STR_FAIL("\xC2");
|
||||
STR_PASS("\xC2\x80", "\\u0080");
|
||||
STR_PASS("\xC2\xBF", "\\u00BF");
|
||||
STR_PASS("\xDF\x80", "\\u07C0");
|
||||
STR_PASS("\xDF\xBF", "\\u07FF");
|
||||
STR_FAIL("\xDF");
|
||||
STR_FAIL("\xE0\x80");
|
||||
STR_FAIL("\xE0\x1F");
|
||||
STR_FAIL("\xE0\x1F\x80");
|
||||
STR_FAIL("\xE0");
|
||||
STR_FAIL("\xE0\xA0");
|
||||
STR_PASS("\xE0\xA0\x80", "\\u0800");
|
||||
STR_PASS("\xE0\xA0\xBF", "\\u083F");
|
||||
STR_FAIL("\xE0\xA0\xC0");
|
||||
STR_PASS("\xE0\xBF\x80", "\\u0FC0");
|
||||
STR_PASS("\xE0\xBF\xBF", "\\u0FFF");
|
||||
STR_FAIL("\xE0\xC0\x80");
|
||||
STR_FAIL("\xE1");
|
||||
STR_FAIL("\xE1\x80");
|
||||
STR_FAIL("\xE1\x7F\x80");
|
||||
STR_FAIL("\xE1\x80\x7F");
|
||||
STR_PASS("\xE1\x80\x80", "\\u1000");
|
||||
STR_PASS("\xE1\x80\xBF", "\\u103F");
|
||||
STR_PASS("\xE1\xBF\x80", "\\u1FC0");
|
||||
STR_PASS("\xE1\xBF\xBF", "\\u1FFF");
|
||||
STR_FAIL("\xE1\xC0\x80");
|
||||
STR_FAIL("\xE1\x80\xC0");
|
||||
STR_PASS("\xEF\x80\x80", "\\uF000");
|
||||
STR_PASS("\xEF\xBF\xBF", "\\uFFFF");
|
||||
STR_FAIL("\xF0");
|
||||
STR_FAIL("\xF0\x90");
|
||||
STR_FAIL("\xF0\x90\x80");
|
||||
STR_FAIL("\xF0\x80\x80\x80");
|
||||
STR_FAIL("\xF0\x8F\x80\x80");
|
||||
STR_PASS("\xF0\x90\x80\x80", "\\uD800\\uDC00");
|
||||
STR_PASS("\xF0\x90\x80\xBF", "\\uD800\\uDC3F");
|
||||
STR_PASS("\xF0\x90\xBF\x80", "\\uD803\\uDFC0");
|
||||
STR_PASS("\xF0\xBF\x80\x80", "\\uD8BC\\uDC00");
|
||||
STR_FAIL("\xF0\xC0\x80\x80");
|
||||
STR_FAIL("\xF1");
|
||||
STR_FAIL("\xF1\x80");
|
||||
STR_FAIL("\xF1\x80\x80");
|
||||
STR_FAIL("\xF1\x80\x80\x7F");
|
||||
STR_PASS("\xF1\x80\x80\x80", "\\uD8C0\\uDC00");
|
||||
STR_PASS("\xF1\x80\x80\xBF", "\\uD8C0\\uDC3F");
|
||||
STR_PASS("\xF1\x80\xBF\x80", "\\uD8C3\\uDFC0");
|
||||
STR_PASS("\xF1\xBF\x80\x80", "\\uD9BC\\uDC00");
|
||||
STR_PASS("\xF3\x80\x80\x80", "\\uDAC0\\uDC00");
|
||||
STR_FAIL("\xF3\xC0\x80\x80");
|
||||
STR_FAIL("\xF3\x80\xC0\x80");
|
||||
STR_FAIL("\xF3\x80\x80\xC0");
|
||||
STR_FAIL("\xF4");
|
||||
STR_FAIL("\xF4\x80");
|
||||
STR_FAIL("\xF4\x80\x80");
|
||||
STR_PASS("\xF4\x80\x80\x80", "\\uDBC0\\uDC00");
|
||||
STR_PASS("\xF4\x8F\x80\x80", "\\uDBFC\\uDC00");
|
||||
STR_PASS("\xF4\x8F\xBF\xBF", "\\uDBFF\\uDFFF");
|
||||
STR_FAIL("\xF4\x90\x80\x80");
|
||||
STR_FAIL("\xF5");
|
||||
STR_FAIL("\xF5\x80");
|
||||
STR_FAIL("\xF5\x80\x80");
|
||||
STR_FAIL("\xF5\x80\x80\x80");
|
||||
STR_FAIL("\xF5\x80\x80\x80\x80");
|
||||
|
||||
/* Overlong encodings */
|
||||
STR_FAIL("\xC0\x80");
|
||||
|
||||
/* Surrogate pairs */
|
||||
STR_FAIL("\xED\xA0\x80"); /* U+D800 First high surrogate */
|
||||
STR_FAIL("\xED\xAF\xBF"); /* U+DBFF Last high surrogate */
|
||||
STR_FAIL("\xED\xB0\x80"); /* U+DC00 First low surrogate */
|
||||
STR_FAIL("\xED\xBF\xBF"); /* U+DFFF Last low surrogate */
|
||||
STR_FAIL("\xED\xA1\x8C\xED\xBE\xB4"); /* U+233B4 (invalid surrogate pair encoding) */
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_number_int32(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
BEGIN();
|
||||
VAL_INT32(0);
|
||||
END("0");
|
||||
|
||||
BEGIN();
|
||||
VAL_INT32(1);
|
||||
END("1");
|
||||
|
||||
BEGIN();
|
||||
VAL_INT32(123);
|
||||
END("123");
|
||||
|
||||
BEGIN();
|
||||
VAL_INT32(-123);
|
||||
END("-123");
|
||||
|
||||
BEGIN();
|
||||
VAL_INT32(2147483647);
|
||||
END("2147483647");
|
||||
|
||||
BEGIN();
|
||||
VAL_INT32(-2147483648);
|
||||
END("-2147483648");
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_number_uint32(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
BEGIN();
|
||||
VAL_UINT32(0);
|
||||
END("0");
|
||||
|
||||
BEGIN();
|
||||
VAL_UINT32(1);
|
||||
END("1");
|
||||
|
||||
BEGIN();
|
||||
VAL_UINT32(123);
|
||||
END("123");
|
||||
|
||||
BEGIN();
|
||||
VAL_UINT32(2147483647);
|
||||
END("2147483647");
|
||||
|
||||
BEGIN();
|
||||
VAL_UINT32(4294967295);
|
||||
END("4294967295");
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_array(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_END();
|
||||
END("[]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_ARRAY_END();
|
||||
END("[0]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_INT32(1);
|
||||
VAL_ARRAY_END();
|
||||
END("[0,1]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_INT32(1);
|
||||
VAL_INT32(2);
|
||||
VAL_ARRAY_END();
|
||||
END("[0,1,2]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_STRING("a");
|
||||
VAL_ARRAY_END();
|
||||
END("[\"a\"]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_STRING("a");
|
||||
VAL_STRING("b");
|
||||
VAL_ARRAY_END();
|
||||
END("[\"a\",\"b\"]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_STRING("a");
|
||||
VAL_STRING("b");
|
||||
VAL_STRING("c");
|
||||
VAL_ARRAY_END();
|
||||
END("[\"a\",\"b\",\"c\"]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_TRUE();
|
||||
VAL_ARRAY_END();
|
||||
END("[true]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_TRUE();
|
||||
VAL_FALSE();
|
||||
VAL_ARRAY_END();
|
||||
END("[true,false]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_TRUE();
|
||||
VAL_FALSE();
|
||||
VAL_TRUE();
|
||||
VAL_ARRAY_END();
|
||||
END("[true,false,true]");
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_object(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_OBJECT_END();
|
||||
END("{}");
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_INT32(0);
|
||||
VAL_OBJECT_END();
|
||||
END("{\"a\":0}");
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_INT32(0);
|
||||
VAL_NAME("b");
|
||||
VAL_INT32(1);
|
||||
VAL_OBJECT_END();
|
||||
END("{\"a\":0,\"b\":1}");
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_INT32(0);
|
||||
VAL_NAME("b");
|
||||
VAL_INT32(1);
|
||||
VAL_NAME("c");
|
||||
VAL_INT32(2);
|
||||
VAL_OBJECT_END();
|
||||
END("{\"a\":0,\"b\":1,\"c\":2}");
|
||||
}
|
||||
|
||||
static void
|
||||
test_write_nesting(void)
|
||||
{
|
||||
struct spdk_json_write_ctx *w;
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_END();
|
||||
VAL_ARRAY_END();
|
||||
END("[[]]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_END();
|
||||
VAL_ARRAY_END();
|
||||
VAL_ARRAY_END();
|
||||
END("[[[]]]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_END();
|
||||
VAL_ARRAY_END();
|
||||
END("[0,[]]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_ARRAY_END();
|
||||
VAL_INT32(0);
|
||||
VAL_ARRAY_END();
|
||||
END("[[],0]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(1);
|
||||
VAL_ARRAY_END();
|
||||
VAL_INT32(2);
|
||||
VAL_ARRAY_END();
|
||||
END("[0,[1],2]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_INT32(1);
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(2);
|
||||
VAL_INT32(3);
|
||||
VAL_ARRAY_END();
|
||||
VAL_INT32(4);
|
||||
VAL_INT32(5);
|
||||
VAL_ARRAY_END();
|
||||
END("[0,1,[2,3],4,5]");
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_OBJECT_END();
|
||||
VAL_OBJECT_END();
|
||||
END("{\"a\":{}}");
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("b");
|
||||
VAL_INT32(0);
|
||||
VAL_OBJECT_END();
|
||||
VAL_OBJECT_END();
|
||||
END("{\"a\":{\"b\":0}}");
|
||||
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(0);
|
||||
VAL_ARRAY_END();
|
||||
VAL_OBJECT_END();
|
||||
END("{\"a\":[0]}");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_INT32(0);
|
||||
VAL_OBJECT_END();
|
||||
VAL_ARRAY_END();
|
||||
END("[{\"a\":0}]");
|
||||
|
||||
BEGIN();
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("a");
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("b");
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("c");
|
||||
VAL_INT32(1);
|
||||
VAL_OBJECT_END();
|
||||
VAL_INT32(2);
|
||||
VAL_ARRAY_END();
|
||||
VAL_NAME("d");
|
||||
VAL_INT32(3);
|
||||
VAL_OBJECT_END();
|
||||
VAL_NAME("e");
|
||||
VAL_INT32(4);
|
||||
VAL_OBJECT_END();
|
||||
VAL_INT32(5);
|
||||
VAL_ARRAY_END();
|
||||
END("[{\"a\":{\"b\":[{\"c\":1},2],\"d\":3},\"e\":4},5]");
|
||||
|
||||
/* Examples from RFC 7159 */
|
||||
BEGIN();
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("Image");
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("Width");
|
||||
VAL_INT32(800);
|
||||
VAL_NAME("Height");
|
||||
VAL_INT32(600);
|
||||
VAL_NAME("Title");
|
||||
VAL_STRING("View from 15th Floor");
|
||||
VAL_NAME("Thumbnail");
|
||||
VAL_OBJECT_BEGIN();
|
||||
VAL_NAME("Url");
|
||||
VAL_STRING("http://www.example.com/image/481989943");
|
||||
VAL_NAME("Height");
|
||||
VAL_INT32(125);
|
||||
VAL_NAME("Width");
|
||||
VAL_INT32(100);
|
||||
VAL_OBJECT_END();
|
||||
VAL_NAME("Animated");
|
||||
VAL_FALSE();
|
||||
VAL_NAME("IDs");
|
||||
VAL_ARRAY_BEGIN();
|
||||
VAL_INT32(116);
|
||||
VAL_INT32(943);
|
||||
VAL_INT32(234);
|
||||
VAL_INT32(38793);
|
||||
VAL_ARRAY_END();
|
||||
VAL_OBJECT_END();
|
||||
VAL_OBJECT_END();
|
||||
END(
|
||||
"{\"Image\":"
|
||||
"{"
|
||||
"\"Width\":800,"
|
||||
"\"Height\":600,"
|
||||
"\"Title\":\"View from 15th Floor\","
|
||||
"\"Thumbnail\":{"
|
||||
"\"Url\":\"http://www.example.com/image/481989943\","
|
||||
"\"Height\":125,"
|
||||
"\"Width\":100"
|
||||
"},"
|
||||
"\"Animated\":false,"
|
||||
"\"IDs\":[116,943,234,38793]"
|
||||
"}"
|
||||
"}");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CU_pSuite suite = NULL;
|
||||
unsigned int num_failures;
|
||||
|
||||
if (CU_initialize_registry() != CUE_SUCCESS) {
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
suite = CU_add_suite("json", NULL, NULL);
|
||||
if (suite == NULL) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
if (
|
||||
CU_add_test(suite, "write_literal", test_write_literal) == NULL ||
|
||||
CU_add_test(suite, "write_string_simple", test_write_string_simple) == NULL ||
|
||||
CU_add_test(suite, "write_string_escapes", test_write_string_escapes) == NULL ||
|
||||
CU_add_test(suite, "write_number_int32", test_write_number_int32) == NULL ||
|
||||
CU_add_test(suite, "write_number_uint32", test_write_number_uint32) == NULL ||
|
||||
CU_add_test(suite, "write_array", test_write_array) == NULL ||
|
||||
CU_add_test(suite, "write_object", test_write_object) == NULL ||
|
||||
CU_add_test(suite, "write_nesting", test_write_nesting) == NULL) {
|
||||
CU_cleanup_registry();
|
||||
return CU_get_error();
|
||||
}
|
||||
|
||||
CU_basic_set_mode(CU_BRM_VERBOSE);
|
||||
|
||||
CU_basic_run_tests();
|
||||
|
||||
num_failures = CU_get_number_of_failures();
|
||||
CU_cleanup_registry();
|
||||
|
||||
return num_failures;
|
||||
}
|
@ -13,3 +13,9 @@ test/lib/nvme/unit/nvme_qpair_c/nvme_qpair_ut
|
||||
make -C test/lib/ioat/unit CONFIG_WERROR=y
|
||||
|
||||
test/lib/ioat/unit/ioat_ut
|
||||
|
||||
make -C test/lib/json CONFIG_WERROR=y
|
||||
|
||||
test/lib/json/parse/json_parse_ut
|
||||
test/lib/json/util/json_util_ut
|
||||
test/lib/json/write/json_write_ut
|
||||
|
Loading…
Reference in New Issue
Block a user