changeset 716:264c92a1d709

cbor: implement cbor_unpack_val Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Mon, 18 Mar 2019 17:50:48 -0400
parents 60ad787ca52c
children 8babb41c0f94
files fmt_cbor.c include/jeffpc/cbor.h mapfile-vers tests/test_cbor_unpack.c
diffstat 4 files changed, 315 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/fmt_cbor.c	Tue Mar 19 13:12:28 2019 -0400
+++ b/fmt_cbor.c	Mon Mar 18 17:50:48 2019 -0400
@@ -365,6 +365,8 @@
  * unpack
  */
 
+static struct val *unpack_cbor_val(struct buffer *buffer);
+
 static int read_cbor_type(struct buffer *buffer, enum major_type *type,
 			  uint8_t *extra)
 {
@@ -772,3 +774,273 @@
 
 	return cbor_unpack_break(buffer);
 }
+
+static struct val *unpack_cbor_null(struct buffer *buffer)
+{
+	int ret;
+
+	ret = cbor_unpack_null(buffer);
+
+	return ret ? ERR_PTR(ret) : VAL_ALLOC_NULL();
+}
+
+static struct val *unpack_cbor_uint(struct buffer *buffer)
+{
+	uint64_t tmp;
+	int ret;
+
+	ret = cbor_unpack_uint(buffer, &tmp);
+
+	return ret ? ERR_PTR(ret) : val_alloc_int(tmp);
+}
+
+static struct val *unpack_cbor_str(struct buffer *buffer)
+{
+	struct str *str;
+	int ret;
+
+	ret = cbor_unpack_str(buffer, &str);
+
+	return ret ? ERR_PTR(ret) : str_cast_to_val(str);
+}
+
+static struct val *unpack_cbor_bool(struct buffer *buffer)
+{
+	bool tmp;
+	int ret;
+
+	ret = cbor_unpack_bool(buffer, &tmp);
+
+	return ret ? ERR_PTR(ret) : val_alloc_bool(tmp);
+}
+
+static struct val *unpack_cbor_blob(struct buffer *buffer)
+{
+	size_t size;
+	void *data;
+	int ret;
+
+	ret = cbor_unpack_blob(buffer, &data, &size);
+
+	return ret ? ERR_PTR(ret) : val_alloc_blob(data, size);
+}
+
+static struct val *unpack_cbor_array(struct buffer *buffer)
+{
+	bool end_required;
+	struct val **arr;
+	uint64_t nelem;
+	int ret;
+
+	ret = cbor_unpack_array_start(buffer, &nelem, &end_required);
+	if (ret)
+		return ERR_PTR(ret);
+
+	arr = NULL;
+
+	if (end_required) {
+		/* indef */
+		nelem = 0;
+
+		for (;;) {
+			struct val **tmparr;
+			struct val *elem;
+
+			ret = cbor_unpack_break(buffer);
+			if (!ret)
+				break; /* end of map */
+			/* TODO: check for errors */
+
+			elem = unpack_cbor_val(buffer);
+			if (IS_ERR(elem)) {
+				ret = PTR_ERR(elem);
+				goto err;
+			}
+
+			tmparr = mem_reallocarray(arr, nelem + 1,
+						  sizeof(struct val *));
+			if (!tmparr) {
+				ret = -ENOMEM;
+				goto err;
+			}
+
+			arr = tmparr;
+
+			arr[nelem++] = elem;
+		}
+	} else {
+		/* def */
+		size_t i;
+
+		if (nelem > SIZE_MAX)
+			return ERR_PTR(-ENOMEM);
+
+		arr = mem_recallocarray(NULL, 0, nelem, sizeof(struct val *));
+		if (!arr)
+			return ERR_PTR(-ENOMEM);
+
+		for (i = 0; i < nelem; i++) {
+			struct val *elem;
+
+			elem = unpack_cbor_val(buffer);
+			if (IS_ERR(elem)) {
+				ret = PTR_ERR(elem);
+				goto err;
+			}
+
+			arr[i] = elem;
+		}
+	}
+
+	ret = cbor_unpack_array_end(buffer, end_required);
+	if (ret)
+		goto err;
+
+	return val_alloc_array(arr, nelem);
+
+err:
+	if (arr) {
+		size_t i;
+
+		for (i = 0; i < nelem; i++)
+			val_putref(arr[i]);
+
+		free(arr);
+	}
+
+	return ERR_PTR(ret);
+}
+
+static int __unpack_cbor_pair(struct buffer *buffer, struct nvlist *nvl)
+{
+	struct nvpair pair;
+	struct val *name;
+	struct val *value;
+
+	name = unpack_cbor_str(buffer);
+	if (IS_ERR(name))
+		return PTR_ERR(name);
+
+	value = unpack_cbor_val(buffer);
+	if (IS_ERR(value)) {
+		val_putref(name);
+		return PTR_ERR(value);
+	}
+
+	pair.name = val_cast_to_str(name);
+	pair.value = value;
+
+	return nvl_set_pair(nvl, &pair);
+}
+
+static struct val *unpack_cbor_nvl(struct buffer *buffer)
+{
+	struct nvlist *nvl;
+	bool end_required;
+	uint64_t npairs;
+	int ret;
+
+	ret = cbor_unpack_map_start(buffer, &npairs, &end_required);
+	if (ret)
+		return ERR_PTR(ret);
+
+	nvl = nvl_alloc();
+	if (IS_ERR(nvl))
+		return ERR_CAST(nvl);
+
+	if (end_required) {
+		/* indef */
+		for (;;) {
+			ret = cbor_unpack_break(buffer);
+			if (!ret)
+				break; /* end of map */
+			/* TODO: check for errors */
+
+			ret = __unpack_cbor_pair(buffer, nvl);
+			if (ret)
+				goto err;
+		}
+	} else {
+		/* def */
+		size_t i;
+
+		if (npairs > SIZE_MAX) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		for (i = 0; i < npairs; i++) {
+			ret = __unpack_cbor_pair(buffer, nvl);
+			if (ret)
+				goto err;
+		}
+	}
+
+	ret = cbor_unpack_map_end(buffer, end_required);
+	if (ret)
+		goto err;
+
+	return nvl_cast_to_val(nvl);
+
+err:
+	nvl_putref(nvl);
+
+	return ERR_PTR(ret);
+}
+
+static struct val *unpack_cbor_val(struct buffer *buffer)
+{
+	enum val_type type;
+	int ret;
+
+	ret = cbor_peek_type(buffer, &type);
+	if (ret)
+		return ERR_PTR(ret);
+
+	switch (type) {
+		case VT_NULL:
+			return unpack_cbor_null(buffer);
+		case VT_INT:
+			return unpack_cbor_uint(buffer);
+		case VT_STR:
+			return unpack_cbor_str(buffer);
+		case VT_SYM:
+			return ERR_PTR(-ENOTSUP);
+		case VT_BOOL:
+			return unpack_cbor_bool(buffer);
+		case VT_CONS:
+			return ERR_PTR(-ENOTSUP);
+		case VT_CHAR:
+			return ERR_PTR(-ENOTSUP);
+		case VT_BLOB:
+			return unpack_cbor_blob(buffer);
+		case VT_ARRAY:
+			return unpack_cbor_array(buffer);
+		case VT_NVL:
+			return unpack_cbor_nvl(buffer);
+	}
+
+	return ERR_PTR(-ENOTSUP);
+}
+
+struct val *cbor_unpack_val(struct buffer *buffer)
+{
+	struct buffer tmp;
+	struct val *val;
+	int ret;
+
+	buffer_init_static(&tmp, buffer_data_current(buffer),
+			   buffer_remain(buffer), false);
+
+	val = unpack_cbor_val(&tmp);
+	if (IS_ERR(val))
+		return val;
+
+	ret = sync_buffers(buffer, &tmp);
+	if (!ret)
+		return val;
+
+	val_putref(val);
+
+	return ERR_PTR(ret);
+}
--- a/include/jeffpc/cbor.h	Tue Mar 19 13:12:28 2019 -0400
+++ b/include/jeffpc/cbor.h	Mon Mar 18 17:50:48 2019 -0400
@@ -78,5 +78,6 @@
 extern int cbor_unpack_array_start(struct buffer *buffer, uint64_t *nelem,
 				   bool *end_required);
 extern int cbor_unpack_array_end(struct buffer *buffer, bool end_required);
+extern struct val *cbor_unpack_val(struct buffer *buffer);
 
 #endif
--- a/mapfile-vers	Tue Mar 19 13:12:28 2019 -0400
+++ b/mapfile-vers	Mon Mar 18 17:50:48 2019 -0400
@@ -77,6 +77,7 @@
 		cbor_unpack_nint;
 		cbor_unpack_null;
 		cbor_unpack_str;
+		cbor_unpack_val;
 		cbor_unpack_uint;
 
 		# cstr
--- a/tests/test_cbor_unpack.c	Tue Mar 19 13:12:28 2019 -0400
+++ b/tests/test_cbor_unpack.c	Mon Mar 18 17:50:48 2019 -0400
@@ -53,7 +53,7 @@
 		ret = fxn;						\
 									\
 		check_rets((exp_ret), ret,				\
-			   "failed to unpack value directly");		\
+			   "failed to unpack value directly:");		\
 									\
 		if (ret) {						\
 			/* failed, so there is no value to compare */	\
@@ -63,9 +63,43 @@
 									\
 		wrap = alloc;						\
 		if (!sexpr_equal(wrap, val_getref(exp)))		\
-			fail("not equal");				\
+			fail("direct unpack not equal");		\
+									\
+		fprintf(stderr, "ok.\n");				\
+	} while (0)
+
+#define RUN_ONE_VAL(in, _exp)						\
+	do {								\
+		struct val *exp = (_exp);				\
+		struct buffer tmp;					\
+		struct val *ret;					\
+									\
+		buffer_init_static(&tmp, buffer_data(in),		\
+				   buffer_size(in), false);		\
+									\
+		fprintf(stderr, "unpack via cbor_unpack_val (should %s)...",\
+			IS_ERR(exp) ? "fail" : "succeed");		\
+									\
+		ret = cbor_unpack_val(&tmp);				\
 									\
-		/* TODO: unpack via cbor_unpack_val & compare value */	\
+		check_rets(IS_ERR(exp) ? PTR_ERR(exp) : 0,		\
+			   IS_ERR(ret) ? PTR_ERR(ret) : 0,		\
+			   "failed to unpack value indirectly:");	\
+									\
+		if (IS_ERR(ret)) {					\
+			/* failed, so there is no value to compare */	\
+			fprintf(stderr, "ok.\n");			\
+			break;						\
+		}							\
+									\
+		if (sexpr_is_null(exp)) {				\
+			if ((ret->type != VT_ARRAY) ||			\
+			    (ret->array.nelem != 0))			\
+				fail("indirect unpack not empty array");\
+		} else if (!sexpr_equal(ret, val_getref(exp))) {	\
+			fail("indirect unpack not equal");		\
+		}							\
+									\
 		fprintf(stderr, "ok.\n");				\
 	} while (0)
 
@@ -331,6 +365,10 @@
 		val_dump(expected, 1);
 	}
 
+	/* test generic unpacking */
+	RUN_ONE_VAL(in, expected);
+
+	/* test specific unpacking */
 	switch (expected->type) {
 		case VT_NULL:
 			check_null(in, expected);