changeset 705:4f2c0ba43681

introduce json encoding API Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Tue, 19 Mar 2019 12:34:08 -0400
parents 2735e0d2f0b1
children fe60b160f306
files CMakeLists.txt fmt_json.c include/jeffpc/json.h mapfile-vers
diffstat 4 files changed, 353 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Mar 18 17:50:35 2019 -0400
+++ b/CMakeLists.txt	Tue Mar 19 12:34:08 2019 -0400
@@ -100,6 +100,7 @@
 	file_cache.c
 	${FILE_CACHE_EXTRA_SOURCE}
 	fmt_cbor.c
+	fmt_json.c
 	hexdump.c
 	init.c
 	io.c
@@ -169,6 +170,7 @@
 		include/jeffpc/int.h
 		include/jeffpc/io.h
 		include/jeffpc/jeffpc.h
+		include/jeffpc/json.h
 		include/jeffpc/list.h
 		include/jeffpc/mem.h
 		include/jeffpc/mmap.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fmt_json.c	Tue Mar 19 12:34:08 2019 -0400
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2017-2019 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <jeffpc/mem.h>
+#include <jeffpc/json.h>
+#include <jeffpc/nvl.h>
+
+/*
+ * pack
+ */
+
+int json_pack_uint(struct buffer *buffer, uint64_t v)
+{
+	char tmp[sizeof(v) * 2 * 2 + 1]; /* FIXME: hexdump would take 2x */
+
+	snprintf(tmp, sizeof(tmp), "%"PRIu64, v);
+
+	return buffer_append_cstr(buffer, tmp);
+}
+
+int json_pack_nint(struct buffer *buffer, uint64_t v)
+{
+	return -ENOTSUP; /* TODO */
+}
+
+int json_pack_int(struct buffer *buffer, int64_t v)
+{
+	if (v < 0)
+		return json_pack_nint(buffer, -v);
+
+	return json_pack_uint(buffer, v);
+}
+
+static int __escape_char(struct buffer *buffer, uint64_t c)
+{
+	char tmp[7];
+
+	/* FIXME: char outside of basic multilingual plane */
+	if (c > 0xffff)
+		return -ENOTSUP;
+
+	snprintf(tmp, sizeof(tmp), "\\u%04"PRIX64, c);
+
+	return buffer_append_cstr(buffer, tmp);
+}
+
+static int __escape_ctrl_char(struct buffer *buffer, uint8_t c)
+{
+	const char *mapped = NULL;
+
+	switch (c) {
+		case '\b':
+			mapped = "\\b";
+			break;
+		case '\f':
+			mapped = "\\f";
+			break;
+		case '\n':
+			mapped = "\\n";
+			break;
+		case '\r':
+			mapped = "\\r";
+			break;
+		case '\t':
+			mapped = "\\t";
+			break;
+	}
+
+	if (mapped)
+		return buffer_append_cstr(buffer, mapped);
+
+	return __escape_char(buffer, c);
+}
+
+int json_pack_cstr_len(struct buffer *buffer, const char *str, size_t len)
+{
+	size_t i;
+	int ret;
+
+	if ((ret = buffer_append_c(buffer, '"')))
+		return ret;
+
+	/* append the string... escaped */
+	for (i = 0; i < len; i++) {
+		uint8_t c = str[i];
+
+		if (c <= 0x1f) {
+			/* control character, must be escaped */
+			ret = __escape_ctrl_char(buffer, c);
+		} else if ((c == '"') || (c == '\\')) {
+			/* quote or backslash */
+			char tmp[3] = { '\\', c, '\0' };
+
+			ret = buffer_append_cstr(buffer, tmp);
+		} else {
+			/* no escape necessary */
+			ret = buffer_append_c(buffer, c);
+		}
+
+		if (ret)
+			return ret;
+	}
+
+	if ((ret = buffer_append_c(buffer, '"')))
+		return ret;
+
+	return 0;
+}
+
+int json_pack_str(struct buffer *buffer, struct str *str)
+{
+	return json_pack_cstr_len(buffer, str_cstr(str), str_len(str));
+}
+
+int json_pack_bool(struct buffer *buffer, bool b)
+{
+	return buffer_append_cstr(buffer, b ? "true" : "false");
+}
+
+int json_pack_null(struct buffer *buffer)
+{
+	return buffer_append_cstr(buffer, "null");
+}
+
+int json_pack_array_start(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, '[');
+}
+
+int json_pack_array_elem_sep(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, ',');
+}
+
+int json_pack_array_end(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, ']');
+}
+
+int json_pack_array_vals(struct buffer *buffer, struct val **vals, size_t nelem)
+{
+	size_t i;
+	int ret;
+
+	ret = json_pack_array_start(buffer);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nelem; i++) {
+		if (i) {
+			ret = json_pack_array_elem_sep(buffer);
+			if (ret)
+				return ret;
+		}
+
+		ret = json_pack_val(buffer, vals[i]);
+		if (ret)
+			return ret;
+	}
+
+	return json_pack_array_end(buffer);
+}
+
+int json_pack_map_start(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, '{');
+}
+
+int json_pack_map_name_sep(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, ':');
+}
+
+int json_pack_map_pair_sep(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, ',');
+}
+
+int json_pack_map_end(struct buffer *buffer)
+{
+	return buffer_append_c(buffer, '}');
+}
+
+int json_pack_map_val(struct buffer *buffer, struct val *val)
+{
+	struct rb_tree *tree = &val->_set_nvl.values;
+	struct nvpair *cur;
+	bool first;
+	int ret;
+
+	if (val->type != VT_NVL)
+		return -EINVAL;
+
+	ret = json_pack_map_start(buffer);
+	if (ret)
+		return ret;
+
+	first = true;
+
+	rb_for_each(tree, cur) {
+		if (!first) {
+			ret = json_pack_map_pair_sep(buffer);
+			if (ret)
+				return ret;
+		} else {
+			first = false;
+		}
+
+		ret = json_pack_str(buffer, cur->name);
+		if (ret)
+			return ret;
+
+		ret = json_pack_map_name_sep(buffer);
+		if (ret)
+			return ret;
+
+		ret = json_pack_val(buffer, cur->value);
+		if (ret)
+			return ret;
+	}
+
+	return json_pack_map_end(buffer);
+}
+
+int json_pack_val(struct buffer *buffer, struct val *val)
+{
+	switch (val->type) {
+		case VT_NULL:
+			return json_pack_null(buffer);
+		case VT_INT:
+			return json_pack_uint(buffer, val->i);
+		case VT_STR:
+			return json_pack_str(buffer, val_cast_to_str(val));
+		case VT_SYM:
+			return -ENOTSUP;
+		case VT_BOOL:
+			return json_pack_bool(buffer, val->b);
+		case VT_CONS:
+			return -ENOTSUP;
+		case VT_CHAR:
+			return -ENOTSUP;
+		case VT_BLOB:
+			return -ENOTSUP;
+		case VT_ARRAY:
+			/*
+			 * We need to pass non-const struct vals so that we
+			 * can use val_cast_to_*().
+			 */
+			return json_pack_array_vals(buffer, val->_set_array.vals,
+						    val->array.nelem);
+		case VT_NVL:
+			return json_pack_map_val(buffer, val);
+	}
+
+	return -ENOTSUP;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/jeffpc/json.h	Tue Mar 19 12:34:08 2019 -0400
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __JEFFPC_JSON_H
+#define __JEFFPC_JSON_H
+
+#include <jeffpc/buffer.h>
+#include <jeffpc/val.h>
+
+/*
+ * On failure, the buffer may contain partially encoded data items.  On
+ * success, a fully encoded data item is appended to the buffer.
+ */
+extern int json_pack_uint(struct buffer *buffer, uint64_t v);
+extern int json_pack_nint(struct buffer *buffer, uint64_t v);
+extern int json_pack_int(struct buffer *buffer, int64_t v);
+extern int json_pack_cstr_len(struct buffer *buffer, const char *str, size_t len);
+extern int json_pack_str(struct buffer *buffer, struct str *str);
+extern int json_pack_bool(struct buffer *buffer, bool b);
+extern int json_pack_null(struct buffer *buffer);
+extern int json_pack_array_start(struct buffer *buffer);
+extern int json_pack_array_elem_sep(struct buffer *buffer);
+extern int json_pack_array_end(struct buffer *buffer);
+extern int json_pack_array_vals(struct buffer *buffer, struct val **vals,
+				size_t nelem);
+extern int json_pack_map_start(struct buffer *buffer);
+extern int json_pack_map_name_sep(struct buffer *buffer);
+extern int json_pack_map_pair_sep(struct buffer *buffer);
+extern int json_pack_map_end(struct buffer *buffer);
+extern int json_pack_val(struct buffer *buffer, struct val *val);
+extern int json_pack_map_val(struct buffer *buffer, struct val *val);
+
+static inline int json_pack_cstr(struct buffer *buffer, const char *str)
+{
+	return json_pack_cstr_len(buffer, str, strlen(str));
+}
+
+#endif
--- a/mapfile-vers	Mon Mar 18 17:50:35 2019 -0400
+++ b/mapfile-vers	Tue Mar 19 12:34:08 2019 -0400
@@ -115,6 +115,25 @@
 		xread;
 		xwrite;
 
+		# json
+		json_pack_array_elem_sep;
+		json_pack_array_end;
+		json_pack_array_start;
+		json_pack_array_vals;
+		json_pack_bool;
+		json_pack_cstr_len;
+		json_pack_int;
+		json_pack_map_end;
+		json_pack_map_name_sep;
+		json_pack_map_pair_sep;
+		json_pack_map_start;
+		json_pack_map_val;
+		json_pack_nint;
+		json_pack_null;
+		json_pack_str;
+		json_pack_uint;
+		json_pack_val;
+
 		# list
 		list_create;
 		list_destroy;