view 2020-01-08-1-WIP__nvlist__unpack_framework.patch @ 1:2df0a7f85837 default tip

today's batch
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Wed, 08 Jan 2020 15:37:43 -0500
parents
children
line wrap: on
line source

# HG changeset patch
# User Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
# Date 1502202289 -10800
#      Tue Aug 08 17:24:49 2017 +0300
# Node ID 09eb09529f368dd34076f81cc4bfb3f61f47299c
# Parent  4241b66d58237305a29789dc7657cad5eb8c5d19
# EXP-Topic nvlist-unpack-framework
WIP: nvlist: unpack framework

Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>

diff --git a/nvl_fmt_cbor.c b/nvl_fmt_cbor.c
--- a/nvl_fmt_cbor.c
+++ b/nvl_fmt_cbor.c
@@ -51,6 +51,389 @@ static int cbor_array_epilogue(struct bu
 	return cbor_pack_array_end(buffer, nelem);
 }
 
+/*
+ * unpack
+ */
+
+static int parse_addl(struct buffer *buffer, uint64_t *addl)
+{
+	const uint8_t *bytes;
+	uint8_t additional;
+	ssize_t ret;
+	size_t len;
+	size_t i;
+
+	bytes = buffer_data_current(buffer);
+
+	additional = bytes[0] & 0x1f;
+
+	if (additional <= 23) {
+		len = 0;
+		*addl = additional;
+	} else if (additional == ADDL_UINT8) {
+		len = 1;
+		*addl = 0;
+	} else if (additional == ADDL_UINT16) {
+		len = 2;
+		*addl = 0;
+	} else if (additional == ADDL_UINT32) {
+		len = 4;
+		*addl = 0;
+	} else if (additional == ADDL_UINT64) {
+		len = 8;
+		*addl = 0;
+	} else {
+		return -EILSEQ;
+	}
+
+	if (buffer_remain(buffer) < len)
+		return -ESRCH;
+
+	for (i = 0; i < len; i++) {
+		*addl <<= 8;
+		*addl |= bytes[i + 1];
+	}
+
+	ret = buffer_seek(buffer, len + 1, SEEK_CUR);
+
+	return (ret < 0) ? ret : 0;
+}
+
+static int cbor_parse_nvl(struct buffer *buffer, uint8_t byte,
+			  struct nvval *val);
+static int cbor_parse_val(struct buffer *buffer, struct nvval *val);
+
+static int cbor_parse_bool(struct buffer *buffer, uint8_t byte,
+			   struct nvval *val)
+{
+	ssize_t ret;
+
+	val->type = NVT_BOOL;
+	val->b = (byte & 0x1f) == ADDL_FLOAT_TRUE;
+
+	ret = buffer_seek(buffer, 1, SEEK_CUR);
+
+	return (ret < 0) ? ret : 0;
+}
+
+static int cbor_parse_null(struct buffer *buffer, struct nvval *val)
+{
+	ssize_t ret;
+
+	val->type = NVT_NULL;
+
+	ret = buffer_seek(buffer, 1, SEEK_CUR);
+
+	return (ret < 0) ? ret : 0;
+}
+
+static int cbor_parse_uint(struct buffer *buffer, struct nvval *val)
+{
+	val->type = NVT_INT;
+
+	return parse_addl(buffer, &val->i);
+}
+
+static int cbor_parse_length(struct buffer *buffer, size_t *len)
+{
+	uint64_t tmp;
+	int ret;
+
+	ret = parse_addl(buffer, &tmp);
+	if (ret)
+		return ret;
+
+	if (tmp >= SIZE_MAX)
+		return -E2BIG;
+
+	*len = tmp;
+
+	ret = buffer_seek(buffer, 1, SEEK_CUR);
+
+	return (ret < 0) ? ret : 0;
+}
+
+static int inline get_byte_string(struct buffer *buffer, bool nulterm,
+				  char **buf_r, size_t *len_r)
+{
+	ssize_t ret;
+	size_t len;
+	char *tmp;
+
+	ret = cbor_parse_length(buffer, &len);
+	if (ret)
+		return ret;
+
+	tmp = malloc(len + (nulterm ? 1 : 0));
+	if (!tmp)
+		return -ENOMEM;
+
+	ret = buffer_read(buffer, tmp, len);
+	if (ret < 0)
+		return ret;
+
+	if (ret != len)
+		return -ESRCH;
+
+	if (nulterm)
+		tmp[len] = '\0';
+
+	*buf_r = tmp;
+	*len_r = len;
+
+	return 0;
+}
+
+static int cbor_parse_byte(struct buffer *buffer, struct nvval *val)
+{
+	size_t len;
+	char *tmp;
+	int ret;
+
+	ret = get_byte_string(buffer, false, &tmp, &len);
+	if (ret)
+		return ret;
+
+	val->type = NVT_BLOB;
+	val->blob.ptr = tmp;
+	val->blob.size = len;
+
+	return 0;
+}
+
+static int cbor_parse_text(struct buffer *buffer, struct nvval *val)
+{
+	struct str *str;
+	size_t len;
+	char *tmp;
+	int ret;
+
+	ret = get_byte_string(buffer, true, &tmp, &len);
+	if (ret)
+		return ret;
+
+	str = str_alloc(tmp);
+	if (!str)
+		return -ENOMEM;
+
+	val->type = NVT_STR;
+	val->str = str;
+
+	return 0;
+}
+
+static int cbor_parse_array_indef(struct buffer *buffer, struct nvval *val)
+{
+	struct nvval *vals;
+	size_t nvals;
+	ssize_t ret;
+
+	/* seek past the 1 byte with major type */
+	ret = buffer_seek(buffer, 1, SEEK_CUR);
+	if (ret)
+		return ret;
+
+	vals = NULL;
+	nvals = 0;
+
+	for (;;) {
+		const uint8_t *cur;
+		struct nvval *tmp;
+
+		cur = buffer_data_current(buffer);
+		if (!cur) {
+			ret = -ESRCH;
+			break;
+		}
+
+		if (*cur == MKTYPE_STATIC(CMT_FLOAT, ADDL_FLOAT_BREAK))
+			goto done;
+
+		tmp = reallocarray(vals, nvals + 1, sizeof(struct nvval));
+		if (!tmp) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		vals = tmp;
+		nvals++;
+
+		ret = cbor_parse_val(buffer, &vals[nvals - 1]);
+		if (ret)
+			goto done;
+	}
+
+	nvval_release_array(vals, nvals);
+
+	free(vals);
+
+	return ret;
+
+done:
+	val->type = NVT_ARRAY;
+	val->array.vals = vals;
+	val->array.nelem = nvals;
+
+	return 0;
+}
+
+static int cbor_parse_array(struct buffer *buffer, uint8_t byte,
+			    struct nvval *val)
+{
+	if (byte == MKTYPE_STATIC(CMT_ARRAY, ADDL_ARRAY_INDEF))
+		return cbor_parse_array_indef(buffer, val);
+
+	/* definite length map */
+	return -ENOTSUP; /* FIXME */
+}
+
+static struct str *cbor_parse_str(struct buffer *buffer)
+{
+	struct nvval val;
+	int ret;
+
+	ret = cbor_parse_text(buffer, &val);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (val.type != NVT_STR) {
+		nvval_release(&val);
+		return ERR_PTR(-ENOTSUP);
+	}
+
+	return val.str;
+}
+
+static int cbor_parse_val(struct buffer *buffer, struct nvval *val)
+{
+	const uint8_t *cur;
+	uint8_t byte;
+
+	cur = buffer_data_current(buffer);
+	if (!cur)
+		return -ESRCH;
+
+	byte = *cur;
+
+	switch ((enum major_type) (byte >> 5)) {
+		case CMT_UINT:
+			return cbor_parse_uint(buffer, val);
+		case CMT_NINT:
+			return -ENOTSUP;
+		case CMT_BYTE:
+			return cbor_parse_byte(buffer, val);
+		case CMT_TEXT:
+			return cbor_parse_text(buffer, val);
+		case CMT_ARRAY:
+			return cbor_parse_array(buffer, byte, val);
+		case CMT_MAP:
+			return cbor_parse_nvl(buffer, byte, val);
+		case CMT_TAG:
+			return -ENOTSUP;
+		case CMT_FLOAT: {
+			switch (byte & 0x1f) {
+				case ADDL_FLOAT_FALSE:
+				case ADDL_FLOAT_TRUE:
+					return cbor_parse_bool(buffer, byte,
+							       val);
+				case ADDL_FLOAT_NULL:
+					return cbor_parse_null(buffer, val);
+				case ADDL_FLOAT_BREAK:
+					return -EILSEQ;
+			}
+
+			return -EILSEQ;
+		}
+	}
+
+	panic("%s encountered an impossible major type: %u", __func__,
+	      *cur >> 5);
+}
+
+static int cbor_parse_nvl_indef(struct buffer *buffer, struct nvlist *nvl)
+{
+	ssize_t ret;
+
+	/* seek past the 1 byte with major type */
+	ret = buffer_seek(buffer, 1, SEEK_CUR);
+	if (ret)
+		return ret;
+
+	for (;;) {
+		const uint8_t *cur;
+		struct nvval val;
+		struct str *name;
+
+		cur = buffer_data_current(buffer);
+		if (!cur)
+			return -ESRCH;
+
+		if (*cur == MKTYPE_STATIC(CMT_FLOAT, ADDL_FLOAT_BREAK))
+			return 0;
+
+		/* only string keys are supported */
+		if ((*cur >> 5) != CMT_TEXT)
+			return -ENOTSUP;
+
+		/* read the key */
+		name = cbor_parse_str(buffer);
+		if (IS_ERR(name))
+			return PTR_ERR(name);
+
+		/* read the value */
+		ret = cbor_parse_val(buffer, &val);
+		if (ret) {
+			str_putref(name);
+			return ret;
+		}
+
+		/* set the <key,value> pair */
+		ret = nvl_set(nvl, str_cstr(name), &val);
+		if (ret)
+			return ret;
+
+		str_putref(name);
+	}
+}
+
+static int cbor_parse_nvl(struct buffer *buffer, uint8_t byte,
+			  struct nvval *val)
+{
+	val->type = NVT_NVL;
+	val->nvl = nvl_alloc();
+	if (!val->nvl)
+		return -ENOMEM;
+
+	if (byte == MKTYPE_STATIC(CMT_MAP, ADDL_MAP_INDEF))
+		return cbor_parse_nvl_indef(buffer, val->nvl);
+
+	/* definite length nvlist */
+	nvl_putref(val->nvl);
+	return -ENOTSUP; /* FIXME */
+}
+
+static struct nvlist *cbor_parse(struct buffer *buffer)
+{
+	const uint8_t *cur;
+	struct nvval val;
+	int ret;
+
+	cur = buffer_data_current(buffer);
+	if (!cur)
+		return ERR_PTR(-ESRCH);
+
+	if ((*cur >> 5) != CMT_MAP)
+		return ERR_PTR(-ENOTSUP);
+
+	ret = cbor_parse_nvl(buffer, *cur, &val);
+	if (ret)
+		return ERR_PTR(ret);
+
+	ASSERT3U(val.type, ==, NVT_NVL);
+
+	return val.nvl;
+}
+
 const struct nvops nvops_cbor = {
 	.pack = {
 		.nvl_prologue = cbor_nvl_prologue,
@@ -65,4 +448,7 @@ const struct nvops nvops_cbor = {
 		.val_null = cbor_pack_null,
 		.val_str = cbor_pack_cstr,
 	}
+	.unpack = {
+		.parse = cbor_parse,
+	},
 };
diff --git a/nvl_fmt_json.c b/nvl_fmt_json.c
--- a/nvl_fmt_json.c
+++ b/nvl_fmt_json.c
@@ -24,6 +24,10 @@
 
 #include "nvl_impl.h"
 
+/*
+ * pack
+ */
+
 static int json_nvl_prologue(struct buffer *buffer, struct nvlist *nvl)
 {
 	return buffer_append_c(buffer, '{');
@@ -155,6 +159,123 @@ static int json_val_str(struct buffer *b
 	return 0;
 }
 
+/*
+ * unpack
+ */
+
+static const char *consume_whitespace(struct buffer *buffer)
+{
+	const char *cur;
+	int ret;
+
+	while ((cur = buffer_data_current(buffer))) {
+		switch (*cur) {
+			case ' ':
+			case '\t':
+			case '\n':
+			case '\r':
+				break;
+			default:
+				return cur;
+		}
+
+		ret = buffer_seek(buffer, 1, SEEK_CUR);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+
+	return NULL;
+}
+
+static inline bool sanity_check(struct buffer *buffer, const char *expected)
+{
+	const char *cur;
+	size_t rem;
+
+	cur = buffer_data_current(buffer);
+	if (!cur)
+		return false;
+
+	rem = buffer_remain(buffer);
+
+	rem = MIN(rem, strlen(expected));
+
+	return strncmp(cur, expected, rem) == 0;
+}
+
+static int json_parse_type(struct buffer *buffer, enum nvtype *type,
+			   uint64_t *addl)
+{
+	const char *cur;
+
+	cur = consume_whitespace(buffer);
+	if (!cur)
+		return -ESRCH;
+	if (IS_ERR(cur))
+		return PTR_ERR(cur);
+
+	*addl = ~0ul;
+
+	switch (*cur) {
+		case '{':
+			*type = NVT_NVL;
+			break;
+		case '[':
+			*type = NVT_ARRAY;
+			break;
+		case '"':
+			*type = NVT_STR;
+			break;
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			*type = NVT_INT;
+			*addl = *cur - '0';
+			break;
+		case 'n':
+			if (sanity_check(buffer, "null"))
+				*type = NVT_NULL;
+			else
+				return -EILSEQ;
+			break;
+		case 't':
+			if (sanity_check(buffer, "true")) {
+				*type = NVT_BOOL;
+				*addl = 1;
+			} else {
+				return -EILSEQ;
+			}
+			break;
+		case 'f':
+			if (sanity_check(buffer, "false")) {
+				*type = NVT_BOOL;
+				*addl = 0;
+			} else {
+				return -EILSEQ;
+			}
+			break;
+		default:
+			return -EILSEQ;
+	}
+
+	return 0;
+}
+
+static int json_parse_bool(struct buffer *buffer, uint64_t addl, bool *b)
+{
+	/* we already did all the parsing in the peek-type function */
+
+	*b = !!addl;
+
+	return 0;
+}
+
+static int json_parse_int(struct buffer *buffer, uint64_t addl, uint64_t *i)
+{
+	*i = addl;
+
+	return -ENOTSUP;
+}
+
 const struct nvops nvops_json = {
 	.pack = {
 		.nvl_prologue = json_nvl_prologue,	/* { */
@@ -170,5 +291,11 @@ const struct nvops nvops_json = {
 		.val_int = json_val_int,
 		.val_null = json_val_null,
 		.val_str = json_val_str,
-	}
+	},
+	.unpack = {
+		.parse_type = json_parse_type,
+
+		.parse_bool = json_parse_bool,
+		.parse_int = json_parse_int,
+	},
 };
diff --git a/nvl_impl.h b/nvl_impl.h
--- a/nvl_impl.h
+++ b/nvl_impl.h
@@ -46,6 +46,21 @@
 		_ret;							\
 	 })
 
+struct unpack_tok {
+	enum unpack_tok_type {
+		UTT_VAL,	/* string, int, bool, null */
+		UTT_ARR_START,	/* start of array */
+		UTT_NVL_START,	/* start of nvl */
+		UTT_BREAK,	/* end of array or nvl */
+	} type;
+
+	/* UTT_VAL */
+	struct nvval val;
+
+	/* UTT_{NVL,ARR}_START */
+	size_t nelem;
+};
+
 /*
  * Packing operations
  *
@@ -107,7 +122,13 @@ struct nvpackops {
 };
 
 struct nvunpackops {
-	/* TODO */
+	/*
+	 * Returns:
+	 *   nvlist on success
+	 *   -ESRCH if end of buffer
+	 *   -EILSEQ if malformed input
+	 */
+	struct nvlist *(*parse)(struct buffer *buffer);
 };
 
 struct nvops {
diff --git a/nvl_unpack.c b/nvl_unpack.c
--- a/nvl_unpack.c
+++ b/nvl_unpack.c
@@ -25,35 +25,16 @@
 
 #include "nvl_impl.h"
 
-static int unpack_nvl(const struct nvunpackops *ops, const struct buffer *buffer,
-		      struct nvlist *nvl)
-{
-	return -ENOTSUP; /* FIXME */
-}
-
 struct nvlist *nvl_unpack(const void *ptr, size_t len, enum nvformat format)
 {
-	const struct buffer buffer = {
-		.data = (void *) ptr,
-		.used = len,
-		.allocsize = len,
-	};
 	const struct nvops *ops;
-	struct nvlist *nvl;
-	int ret;
+	struct buffer buffer;
+
+	buffer_init_const(&buffer, ptr, len);
 
 	ops = select_ops(format);
-	if (!ops)
+	if (!ops || !ops->unpack.parse)
 		return ERR_PTR(-ENOTSUP);
 
-	nvl = nvl_alloc();
-	if (!nvl)
-		return ERR_PTR(-ENOMEM);
-
-	ret = unpack_nvl(&ops->unpack, &buffer, nvl);
-	if (!ret)
-		return nvl;
-
-	nvl_putref(nvl);
-	return ERR_PTR(ret);
+	return ops->unpack->parse(&buffer);
 }