Commit f9193f4c authored by Daniel Verkamp's avatar Daniel Verkamp
Browse files

json: add JSON parser and encoder libraries



Change-Id: Id73fb7e300d66d31a7c3986c0334b6f56e284905
Signed-off-by: default avatarDaniel Verkamp <daniel.verkamp@intel.com>
parent 3a94688d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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

include/spdk/json.h

0 → 100644
+208 −0
Original line number Diff line number Diff line
/*-
 *   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
+1 −1
Original line number Diff line number Diff line
@@ -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)

lib/json/Makefile

0 → 100644
+39 −0
Original line number Diff line number Diff line
#
#  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 −0
Original line number Diff line number Diff line
/*-
 *   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
Loading