changeset 465:c4ac06c3ee4a

val: move nvl packing code to live closer to val code This is in preparation for packing getting refactored to be a part of val code. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sun, 01 Apr 2018 13:17:25 -0400
parents b1e4c7607050
children 8635533bc2b6
files CMakeLists.txt nvl_fmt_cbor.c nvl_fmt_json.c nvl_impl.h nvl_pack.c nvl_unpack.c val_fmt_cbor.c val_fmt_json.c val_impl_packing.h val_pack.c val_unpack.c
diffstat 11 files changed, 686 insertions(+), 686 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Sat Mar 31 17:34:16 2018 -0400
+++ b/CMakeLists.txt	Sun Apr 01 13:17:25 2018 -0400
@@ -99,10 +99,6 @@
 	mem_array.c
 	nvl.c
 	nvl_convert.c
-	nvl_fmt_cbor.c
-	nvl_fmt_json.c
-	nvl_pack.c
-	nvl_unpack.c
 	padding.c
 	qstring.c
 	rand.c
@@ -121,7 +117,11 @@
 	uuid.c
 	val.c
 	val_array.c
+	val_fmt_cbor.c
+	val_fmt_json.c
 	val_nvl.c
+	val_pack.c
+	val_unpack.c
 	version.c
 	${UMEM_EXTRA_SOURCE}
 )
--- a/nvl_fmt_cbor.c	Sat Mar 31 17:34:16 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2017-2018 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/nvl.h>
-#include <jeffpc/cbor.h>
-
-#include "nvl_impl.h"
-
-/*
- * nvl packing interface
- */
-
-static int cbor_nvl_prologue(struct buffer *buffer, struct nvlist *nvl)
-{
-	return cbor_pack_map_start(buffer, CBOR_UNKNOWN_NELEM);
-}
-
-static int cbor_nvl_epilogue(struct buffer *buffer, struct nvlist *nvl)
-{
-	return cbor_pack_map_end(buffer, CBOR_UNKNOWN_NELEM);
-}
-
-static int cbor_array_prologue(struct buffer *buffer, struct val *const *vals,
-			       size_t nelem)
-{
-	return cbor_pack_array_start(buffer, nelem);
-}
-
-static int cbor_array_epilogue(struct buffer *buffer, struct val *const *vals,
-			       size_t nelem)
-{
-	return cbor_pack_array_end(buffer, nelem);
-}
-
-const struct nvops nvops_cbor = {
-	.pack = {
-		.nvl_prologue = cbor_nvl_prologue,
-		.nvl_epilogue = cbor_nvl_epilogue,
-
-		.array_prologue = cbor_array_prologue,
-		.array_epilogue = cbor_array_epilogue,
-
-		.val_blob = cbor_pack_blob,
-		.val_bool = cbor_pack_bool,
-		.val_int = cbor_pack_uint,
-		.val_null = cbor_pack_null,
-		.val_str = cbor_pack_cstr,
-	}
-};
--- a/nvl_fmt_json.c	Sat Mar 31 17:34:16 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2017-2018 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/nvl.h>
-
-#include "nvl_impl.h"
-
-static int json_nvl_prologue(struct buffer *buffer, struct nvlist *nvl)
-{
-	return buffer_append_c(buffer, '{');
-}
-
-static int json_nvl_name_sep(struct buffer *buffer, const struct nvpair *pair)
-{
-	return buffer_append_c(buffer, ':');
-}
-
-static int json_nvl_val_sep(struct buffer *buffer, const struct nvpair *pair)
-{
-	return buffer_append_c(buffer, ',');
-}
-
-static int json_nvl_epilogue(struct buffer *buffer, struct nvlist *nvl)
-{
-	return buffer_append_c(buffer, '}');
-}
-
-static int json_array_prologue(struct buffer *buffer, struct val *const *vals,
-			       size_t nelem)
-{
-	return buffer_append_c(buffer, '[');
-}
-
-static int json_array_val_sep(struct buffer *buffer, const struct val *val)
-{
-	return buffer_append_c(buffer, ',');
-}
-
-static int json_array_epilogue(struct buffer *buffer, struct val *const *vals,
-			       size_t nelem)
-{
-	return buffer_append_c(buffer, ']');
-}
-
-static int json_val_bool(struct buffer *buffer, bool b)
-{
-	return buffer_append_cstr(buffer, b ? "true" : "false");
-}
-
-static int json_val_int(struct buffer *buffer, uint64_t i)
-{
-	char tmp[sizeof(i) * 2 * 2 + 1]; /* FIXME: hexdump would take 2x */
-
-	snprintf(tmp, sizeof(tmp), "%"PRIu64, i);
-
-	return buffer_append_cstr(buffer, tmp);
-}
-
-static int json_val_null(struct buffer *buffer)
-{
-	return buffer_append_cstr(buffer, "null");
-}
-
-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);
-}
-
-static int json_val_str(struct buffer *buffer, const char *str)
-{
-	int ret;
-
-	if ((ret = buffer_append_c(buffer, '"')))
-		return ret;
-
-	/* append the string... escaped */
-	for (; *str; str++) {
-		uint8_t c = *str;
-
-		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;
-}
-
-const struct nvops nvops_json = {
-	.pack = {
-		.nvl_prologue = json_nvl_prologue,	/* { */
-		.nvl_name_sep = json_nvl_name_sep,	/* : */
-		.nvl_val_sep = json_nvl_val_sep,	/* , */
-		.nvl_epilogue = json_nvl_epilogue,	/* } */
-
-		.array_prologue = json_array_prologue,	/* [ */
-		.array_val_sep = json_array_val_sep,	/* , */
-		.array_epilogue = json_array_epilogue,	/* ] */
-
-		.val_bool = json_val_bool,
-		.val_int = json_val_int,
-		.val_null = json_val_null,
-		.val_str = json_val_str,
-	}
-};
--- a/nvl_impl.h	Sat Mar 31 17:34:16 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * Copyright (c) 2017-2018 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_NVL_IMPL_H
-#define __JEFFPC_NVL_IMPL_H
-
-#include <jeffpc/nvl.h>
-#include <jeffpc/buffer.h>
-
-#define CALL(ops, op, args)						\
-	({								\
-		int _ret = 0;						\
-									\
-		if (ops->op)						\
-			_ret = ops->op args;				\
-									\
-		_ret;							\
-	 })
-
-#define CALL_OR_FAIL(ops, op, args)					\
-	({								\
-		int _ret = -ENOTSUP;					\
-									\
-		if (ops->op)						\
-			_ret = ops->op args;				\
-									\
-		_ret;							\
-	 })
-
-/*
- * Packing operations
- *
- * A required function pointer is only required when an item of that type
- * must be encoded.  For example, if an encoder doesn't implement packing of
- * strings, packing of all nvlists *with* strings will fail because of this
- * limitation.  Packing of nvlists *without* strings may or may not fail for
- * other reasons.
- */
-struct nvpackops {
-	/*
-	 * top-level encoder hooks
-	 *
-	 * Optional: All.
-	 */
-	int (*buffer_finish)(struct buffer *buffer);
-
-	/*
-	 * nvlist encoders
-	 *
-	 * Required: At least one of prologue or epilogue.
-	 * Optional: The rest.
-	 */
-	int (*nvl_prologue)(struct buffer *buffer, struct nvlist *nvl);
-	int (*nvl_name_sep)(struct buffer *buffer, const struct nvpair *pair);
-	int (*nvl_val_sep)(struct buffer *buffer, const struct nvpair *pair);
-	int (*nvl_epilogue)(struct buffer *buffer, struct nvlist *nvl);
-
-	/*
-	 * array encoders
-	 *
-	 * Required: At least one of prologue or epilogue.
-	 * Optional: The rest.
-	 */
-	int (*array_prologue)(struct buffer *buffer, struct val *const *vals,
-			      size_t nelem);
-	int (*array_val_sep)(struct buffer *buffer, const struct val *val);
-	int (*array_epilogue)(struct buffer *buffer, struct val *const *vals,
-			      size_t nelem);
-
-	/*
-	 * nvpair
-	 *
-	 * Optional: All.
-	 */
-	int (*pair_prologue)(struct buffer *buffer, const struct nvpair *pair);
-	int (*pair_epilogue)(struct buffer *buffer, const struct nvpair *pair);
-
-	/*
-	 * value encoders
-	 *
-	 * Required: All.
-	 */
-	int (*val_blob)(struct buffer *buffer, const void *data, size_t size);
-	int (*val_bool)(struct buffer *buffer, bool b);
-	int (*val_int)(struct buffer *buffer, uint64_t i);
-	int (*val_null)(struct buffer *buffer);
-	int (*val_str)(struct buffer *buffer, const char *str);
-};
-
-struct nvunpackops {
-	/* TODO */
-};
-
-struct nvops {
-	const struct nvpackops pack;
-	const struct nvunpackops unpack;
-};
-
-extern const struct nvops nvops_cbor;
-extern const struct nvops nvops_json;
-
-static inline const struct nvops *select_ops(enum nvformat format)
-{
-	switch (format) {
-		case NVF_CBOR:
-			return &nvops_cbor;
-		case NVF_JSON:
-			return &nvops_json;
-	}
-
-	return NULL;
-}
-
-#endif
--- a/nvl_pack.c	Sat Mar 31 17:34:16 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2017-2018 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/nvl.h>
-#include <jeffpc/buffer.h>
-
-#include "nvl_impl.h"
-
-static int pack_nvl(const struct nvpackops *ops, struct buffer *buffer,
-		    struct nvlist *nvl);
-static int pack_val(const struct nvpackops *ops, struct buffer *buffer,
-		    struct val *val);
-
-static int pack_array(const struct nvpackops *ops, struct buffer *buffer,
-		      struct val **vals, size_t nelem)
-{
-	bool first = true;
-	size_t i;
-	int ret;
-
-	if (!ops->array_prologue && !ops->array_epilogue)
-		return -ENOTSUP;
-
-	if ((ret = CALL(ops, array_prologue, (buffer, vals, nelem))))
-		return ret;
-
-	for (i = 0; i < nelem; i++) {
-		struct val *val = vals[i];
-
-		if (first) {
-			first = false;
-			if ((ret = CALL(ops, array_val_sep, (buffer, val))))
-				return ret;
-		}
-
-		if ((ret = pack_val(ops, buffer, val)))
-			return ret;
-	}
-
-	if ((ret = CALL(ops, array_epilogue, (buffer, vals, nelem))))
-		return ret;
-
-	return 0;
-}
-
-static int pack_blob(const struct nvpackops *ops, struct buffer *buffer,
-		     const void *data, size_t size)
-{
-	return CALL_OR_FAIL(ops, val_blob, (buffer, data, size));
-}
-
-static int pack_bool(const struct nvpackops *ops, struct buffer *buffer,
-		     bool b)
-{
-	return CALL_OR_FAIL(ops, val_bool, (buffer, b));
-}
-
-static int pack_int(const struct nvpackops *ops, struct buffer *buffer,
-		    uint64_t i)
-{
-	return CALL_OR_FAIL(ops, val_int, (buffer, i));
-}
-
-static int pack_null(const struct nvpackops *ops, struct buffer *buffer)
-{
-	return CALL_OR_FAIL(ops, val_null, (buffer));
-}
-
-static int pack_string(const struct nvpackops *ops, struct buffer *buffer,
-		       struct str *str)
-{
-	const char *s;
-
-	/*
-	 * Even though we try to avoid NULL pointers to indicate empty
-	 * strings, they can crop up from many places.  So, it is safer to
-	 * do the check here.
-	 */
-	s = str ? str_cstr(str) : "";
-
-	return CALL_OR_FAIL(ops, val_str, (buffer, s));
-}
-
-static int pack_val(const struct nvpackops *ops, struct buffer *buffer,
-		    struct val *val)
-{
-	int ret = -ENOTSUP;
-
-	switch (val->type) {
-		case VT_ARRAY:
-			ret = pack_array(ops, buffer, val->array.vals,
-					 val->array.nelem);
-			break;
-		case VT_BLOB:
-			ret = pack_blob(ops, buffer, val->blob.ptr,
-					val->blob.size);
-			break;
-		case VT_BOOL:
-			ret = pack_bool(ops, buffer, val->b);
-			break;
-		case VT_INT:
-			ret = pack_int(ops, buffer, val->i);
-			break;
-		case VT_NULL:
-			ret = pack_null(ops, buffer);
-			break;
-		case VT_NVL:
-			ret = pack_nvl(ops, buffer, val_cast_to_nvl(val));
-			break;
-		case VT_STR:
-			ret = pack_string(ops, buffer, val_cast_to_str(val));
-			break;
-		case VT_SYM:
-		case VT_CONS:
-		case VT_CHAR:
-			/* not supported */
-			break;
-	}
-
-	return ret;
-}
-
-static int pack_pair(const struct nvpackops *ops, struct buffer *buffer,
-		     const struct nvpair *pair)
-{
-	int ret;
-
-	if ((ret = CALL(ops, pair_prologue, (buffer, pair))))
-		return ret;
-
-	if ((ret = pack_string(ops, buffer, pair->name)))
-		return ret;
-
-	if ((ret = CALL(ops, nvl_name_sep, (buffer, pair))))
-		return ret;
-
-	if ((ret = pack_val(ops, buffer, pair->value)))
-		return ret;
-
-	if ((ret = CALL(ops, pair_epilogue, (buffer, pair))))
-		return ret;
-
-	return 0;
-}
-
-static int pack_nvl(const struct nvpackops *ops, struct buffer *buffer,
-		    struct nvlist *nvl)
-{
-	const struct nvpair *pair;
-	bool first = true;
-	int ret;
-
-	if (!ops->nvl_prologue && !ops->nvl_epilogue)
-		return -ENOTSUP;
-
-	if ((ret = CALL(ops, nvl_prologue, (buffer, nvl))))
-		return ret;
-
-	nvl_for_each(pair, nvl) {
-		if (first) {
-			first = false;
-		} else {
-			if ((ret = CALL(ops, nvl_val_sep, (buffer, pair))))
-				return ret;
-		}
-
-		if ((ret = pack_pair(ops, buffer, pair)))
-			return ret;
-	}
-
-	if ((ret = CALL(ops, nvl_epilogue, (buffer, nvl))))
-		return ret;
-
-	return 0;
-}
-
-static int __nvl_pack(const struct nvpackops *ops, struct buffer *buffer,
-		      struct nvlist *nvl, bool call_finish)
-{
-	int ret;
-
-	ret = pack_nvl(ops, buffer, nvl);
-	if (ret)
-		return ret;
-
-	if (!call_finish)
-		return 0;
-
-	return CALL(ops, buffer_finish, (buffer));
-}
-
-struct buffer *nvl_pack(struct nvlist *nvl, enum nvformat format)
-{
-	const struct nvops *ops;
-	struct buffer *buffer;
-	int ret;
-
-	ops = select_ops(format);
-	if (!ops)
-		return ERR_PTR(-ENOTSUP);
-
-	buffer = buffer_alloc(1024);
-	if (IS_ERR(buffer))
-		return buffer;
-
-	ret = __nvl_pack(&ops->pack, buffer, nvl, true);
-	if (!ret)
-		return buffer;
-
-	buffer_free(buffer);
-	return ERR_PTR(ret);
-}
-
-ssize_t nvl_size(struct nvlist *nvl, enum nvformat format)
-{
-	const struct nvops *ops;
-	struct buffer buffer;
-	int ret;
-
-	ops = select_ops(format);
-	if (!ops)
-		return -ENOTSUP;
-
-	buffer_init_sink(&buffer);
-
-	ret = __nvl_pack(&ops->pack, &buffer, nvl, false);
-
-	return ret ? ret : buffer_used(&buffer);
-}
--- a/nvl_unpack.c	Sat Mar 31 17:34:16 2018 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2017 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/nvl.h>
-#include <jeffpc/buffer.h>
-
-#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;
-
-	ops = select_ops(format);
-	if (!ops)
-		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);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/val_fmt_cbor.c	Sun Apr 01 13:17:25 2018 -0400
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017-2018 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/nvl.h>
+#include <jeffpc/cbor.h>
+
+#include "val_impl_packing.h"
+
+/*
+ * nvl packing interface
+ */
+
+static int cbor_nvl_prologue(struct buffer *buffer, struct nvlist *nvl)
+{
+	return cbor_pack_map_start(buffer, CBOR_UNKNOWN_NELEM);
+}
+
+static int cbor_nvl_epilogue(struct buffer *buffer, struct nvlist *nvl)
+{
+	return cbor_pack_map_end(buffer, CBOR_UNKNOWN_NELEM);
+}
+
+static int cbor_array_prologue(struct buffer *buffer, struct val *const *vals,
+			       size_t nelem)
+{
+	return cbor_pack_array_start(buffer, nelem);
+}
+
+static int cbor_array_epilogue(struct buffer *buffer, struct val *const *vals,
+			       size_t nelem)
+{
+	return cbor_pack_array_end(buffer, nelem);
+}
+
+const struct nvops nvops_cbor = {
+	.pack = {
+		.nvl_prologue = cbor_nvl_prologue,
+		.nvl_epilogue = cbor_nvl_epilogue,
+
+		.array_prologue = cbor_array_prologue,
+		.array_epilogue = cbor_array_epilogue,
+
+		.val_blob = cbor_pack_blob,
+		.val_bool = cbor_pack_bool,
+		.val_int = cbor_pack_uint,
+		.val_null = cbor_pack_null,
+		.val_str = cbor_pack_cstr,
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/val_fmt_json.c	Sun Apr 01 13:17:25 2018 -0400
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2017-2018 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/nvl.h>
+
+#include "val_impl_packing.h"
+
+static int json_nvl_prologue(struct buffer *buffer, struct nvlist *nvl)
+{
+	return buffer_append_c(buffer, '{');
+}
+
+static int json_nvl_name_sep(struct buffer *buffer, const struct nvpair *pair)
+{
+	return buffer_append_c(buffer, ':');
+}
+
+static int json_nvl_val_sep(struct buffer *buffer, const struct nvpair *pair)
+{
+	return buffer_append_c(buffer, ',');
+}
+
+static int json_nvl_epilogue(struct buffer *buffer, struct nvlist *nvl)
+{
+	return buffer_append_c(buffer, '}');
+}
+
+static int json_array_prologue(struct buffer *buffer, struct val *const *vals,
+			       size_t nelem)
+{
+	return buffer_append_c(buffer, '[');
+}
+
+static int json_array_val_sep(struct buffer *buffer, const struct val *val)
+{
+	return buffer_append_c(buffer, ',');
+}
+
+static int json_array_epilogue(struct buffer *buffer, struct val *const *vals,
+			       size_t nelem)
+{
+	return buffer_append_c(buffer, ']');
+}
+
+static int json_val_bool(struct buffer *buffer, bool b)
+{
+	return buffer_append_cstr(buffer, b ? "true" : "false");
+}
+
+static int json_val_int(struct buffer *buffer, uint64_t i)
+{
+	char tmp[sizeof(i) * 2 * 2 + 1]; /* FIXME: hexdump would take 2x */
+
+	snprintf(tmp, sizeof(tmp), "%"PRIu64, i);
+
+	return buffer_append_cstr(buffer, tmp);
+}
+
+static int json_val_null(struct buffer *buffer)
+{
+	return buffer_append_cstr(buffer, "null");
+}
+
+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);
+}
+
+static int json_val_str(struct buffer *buffer, const char *str)
+{
+	int ret;
+
+	if ((ret = buffer_append_c(buffer, '"')))
+		return ret;
+
+	/* append the string... escaped */
+	for (; *str; str++) {
+		uint8_t c = *str;
+
+		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;
+}
+
+const struct nvops nvops_json = {
+	.pack = {
+		.nvl_prologue = json_nvl_prologue,	/* { */
+		.nvl_name_sep = json_nvl_name_sep,	/* : */
+		.nvl_val_sep = json_nvl_val_sep,	/* , */
+		.nvl_epilogue = json_nvl_epilogue,	/* } */
+
+		.array_prologue = json_array_prologue,	/* [ */
+		.array_val_sep = json_array_val_sep,	/* , */
+		.array_epilogue = json_array_epilogue,	/* ] */
+
+		.val_bool = json_val_bool,
+		.val_int = json_val_int,
+		.val_null = json_val_null,
+		.val_str = json_val_str,
+	}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/val_impl_packing.h	Sun Apr 01 13:17:25 2018 -0400
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2017-2018 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_NVL_IMPL_H
+#define __JEFFPC_NVL_IMPL_H
+
+#include <jeffpc/nvl.h>
+#include <jeffpc/buffer.h>
+
+#define CALL(ops, op, args)						\
+	({								\
+		int _ret = 0;						\
+									\
+		if (ops->op)						\
+			_ret = ops->op args;				\
+									\
+		_ret;							\
+	 })
+
+#define CALL_OR_FAIL(ops, op, args)					\
+	({								\
+		int _ret = -ENOTSUP;					\
+									\
+		if (ops->op)						\
+			_ret = ops->op args;				\
+									\
+		_ret;							\
+	 })
+
+/*
+ * Packing operations
+ *
+ * A required function pointer is only required when an item of that type
+ * must be encoded.  For example, if an encoder doesn't implement packing of
+ * strings, packing of all nvlists *with* strings will fail because of this
+ * limitation.  Packing of nvlists *without* strings may or may not fail for
+ * other reasons.
+ */
+struct nvpackops {
+	/*
+	 * top-level encoder hooks
+	 *
+	 * Optional: All.
+	 */
+	int (*buffer_finish)(struct buffer *buffer);
+
+	/*
+	 * nvlist encoders
+	 *
+	 * Required: At least one of prologue or epilogue.
+	 * Optional: The rest.
+	 */
+	int (*nvl_prologue)(struct buffer *buffer, struct nvlist *nvl);
+	int (*nvl_name_sep)(struct buffer *buffer, const struct nvpair *pair);
+	int (*nvl_val_sep)(struct buffer *buffer, const struct nvpair *pair);
+	int (*nvl_epilogue)(struct buffer *buffer, struct nvlist *nvl);
+
+	/*
+	 * array encoders
+	 *
+	 * Required: At least one of prologue or epilogue.
+	 * Optional: The rest.
+	 */
+	int (*array_prologue)(struct buffer *buffer, struct val *const *vals,
+			      size_t nelem);
+	int (*array_val_sep)(struct buffer *buffer, const struct val *val);
+	int (*array_epilogue)(struct buffer *buffer, struct val *const *vals,
+			      size_t nelem);
+
+	/*
+	 * nvpair
+	 *
+	 * Optional: All.
+	 */
+	int (*pair_prologue)(struct buffer *buffer, const struct nvpair *pair);
+	int (*pair_epilogue)(struct buffer *buffer, const struct nvpair *pair);
+
+	/*
+	 * value encoders
+	 *
+	 * Required: All.
+	 */
+	int (*val_blob)(struct buffer *buffer, const void *data, size_t size);
+	int (*val_bool)(struct buffer *buffer, bool b);
+	int (*val_int)(struct buffer *buffer, uint64_t i);
+	int (*val_null)(struct buffer *buffer);
+	int (*val_str)(struct buffer *buffer, const char *str);
+};
+
+struct nvunpackops {
+	/* TODO */
+};
+
+struct nvops {
+	const struct nvpackops pack;
+	const struct nvunpackops unpack;
+};
+
+extern const struct nvops nvops_cbor;
+extern const struct nvops nvops_json;
+
+static inline const struct nvops *select_ops(enum nvformat format)
+{
+	switch (format) {
+		case NVF_CBOR:
+			return &nvops_cbor;
+		case NVF_JSON:
+			return &nvops_json;
+	}
+
+	return NULL;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/val_pack.c	Sun Apr 01 13:17:25 2018 -0400
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2017-2018 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/nvl.h>
+#include <jeffpc/buffer.h>
+
+#include "val_impl_packing.h"
+
+static int pack_nvl(const struct nvpackops *ops, struct buffer *buffer,
+		    struct nvlist *nvl);
+static int pack_val(const struct nvpackops *ops, struct buffer *buffer,
+		    struct val *val);
+
+static int pack_array(const struct nvpackops *ops, struct buffer *buffer,
+		      struct val **vals, size_t nelem)
+{
+	bool first = true;
+	size_t i;
+	int ret;
+
+	if (!ops->array_prologue && !ops->array_epilogue)
+		return -ENOTSUP;
+
+	if ((ret = CALL(ops, array_prologue, (buffer, vals, nelem))))
+		return ret;
+
+	for (i = 0; i < nelem; i++) {
+		struct val *val = vals[i];
+
+		if (first) {
+			first = false;
+			if ((ret = CALL(ops, array_val_sep, (buffer, val))))
+				return ret;
+		}
+
+		if ((ret = pack_val(ops, buffer, val)))
+			return ret;
+	}
+
+	if ((ret = CALL(ops, array_epilogue, (buffer, vals, nelem))))
+		return ret;
+
+	return 0;
+}
+
+static int pack_blob(const struct nvpackops *ops, struct buffer *buffer,
+		     const void *data, size_t size)
+{
+	return CALL_OR_FAIL(ops, val_blob, (buffer, data, size));
+}
+
+static int pack_bool(const struct nvpackops *ops, struct buffer *buffer,
+		     bool b)
+{
+	return CALL_OR_FAIL(ops, val_bool, (buffer, b));
+}
+
+static int pack_int(const struct nvpackops *ops, struct buffer *buffer,
+		    uint64_t i)
+{
+	return CALL_OR_FAIL(ops, val_int, (buffer, i));
+}
+
+static int pack_null(const struct nvpackops *ops, struct buffer *buffer)
+{
+	return CALL_OR_FAIL(ops, val_null, (buffer));
+}
+
+static int pack_string(const struct nvpackops *ops, struct buffer *buffer,
+		       struct str *str)
+{
+	const char *s;
+
+	/*
+	 * Even though we try to avoid NULL pointers to indicate empty
+	 * strings, they can crop up from many places.  So, it is safer to
+	 * do the check here.
+	 */
+	s = str ? str_cstr(str) : "";
+
+	return CALL_OR_FAIL(ops, val_str, (buffer, s));
+}
+
+static int pack_val(const struct nvpackops *ops, struct buffer *buffer,
+		    struct val *val)
+{
+	int ret = -ENOTSUP;
+
+	switch (val->type) {
+		case VT_ARRAY:
+			ret = pack_array(ops, buffer, val->array.vals,
+					 val->array.nelem);
+			break;
+		case VT_BLOB:
+			ret = pack_blob(ops, buffer, val->blob.ptr,
+					val->blob.size);
+			break;
+		case VT_BOOL:
+			ret = pack_bool(ops, buffer, val->b);
+			break;
+		case VT_INT:
+			ret = pack_int(ops, buffer, val->i);
+			break;
+		case VT_NULL:
+			ret = pack_null(ops, buffer);
+			break;
+		case VT_NVL:
+			ret = pack_nvl(ops, buffer, val_cast_to_nvl(val));
+			break;
+		case VT_STR:
+			ret = pack_string(ops, buffer, val_cast_to_str(val));
+			break;
+		case VT_SYM:
+		case VT_CONS:
+		case VT_CHAR:
+			/* not supported */
+			break;
+	}
+
+	return ret;
+}
+
+static int pack_pair(const struct nvpackops *ops, struct buffer *buffer,
+		     const struct nvpair *pair)
+{
+	int ret;
+
+	if ((ret = CALL(ops, pair_prologue, (buffer, pair))))
+		return ret;
+
+	if ((ret = pack_string(ops, buffer, pair->name)))
+		return ret;
+
+	if ((ret = CALL(ops, nvl_name_sep, (buffer, pair))))
+		return ret;
+
+	if ((ret = pack_val(ops, buffer, pair->value)))
+		return ret;
+
+	if ((ret = CALL(ops, pair_epilogue, (buffer, pair))))
+		return ret;
+
+	return 0;
+}
+
+static int pack_nvl(const struct nvpackops *ops, struct buffer *buffer,
+		    struct nvlist *nvl)
+{
+	const struct nvpair *pair;
+	bool first = true;
+	int ret;
+
+	if (!ops->nvl_prologue && !ops->nvl_epilogue)
+		return -ENOTSUP;
+
+	if ((ret = CALL(ops, nvl_prologue, (buffer, nvl))))
+		return ret;
+
+	nvl_for_each(pair, nvl) {
+		if (first) {
+			first = false;
+		} else {
+			if ((ret = CALL(ops, nvl_val_sep, (buffer, pair))))
+				return ret;
+		}
+
+		if ((ret = pack_pair(ops, buffer, pair)))
+			return ret;
+	}
+
+	if ((ret = CALL(ops, nvl_epilogue, (buffer, nvl))))
+		return ret;
+
+	return 0;
+}
+
+static int __nvl_pack(const struct nvpackops *ops, struct buffer *buffer,
+		      struct nvlist *nvl, bool call_finish)
+{
+	int ret;
+
+	ret = pack_nvl(ops, buffer, nvl);
+	if (ret)
+		return ret;
+
+	if (!call_finish)
+		return 0;
+
+	return CALL(ops, buffer_finish, (buffer));
+}
+
+struct buffer *nvl_pack(struct nvlist *nvl, enum nvformat format)
+{
+	const struct nvops *ops;
+	struct buffer *buffer;
+	int ret;
+
+	ops = select_ops(format);
+	if (!ops)
+		return ERR_PTR(-ENOTSUP);
+
+	buffer = buffer_alloc(1024);
+	if (IS_ERR(buffer))
+		return buffer;
+
+	ret = __nvl_pack(&ops->pack, buffer, nvl, true);
+	if (!ret)
+		return buffer;
+
+	buffer_free(buffer);
+	return ERR_PTR(ret);
+}
+
+ssize_t nvl_size(struct nvlist *nvl, enum nvformat format)
+{
+	const struct nvops *ops;
+	struct buffer buffer;
+	int ret;
+
+	ops = select_ops(format);
+	if (!ops)
+		return -ENOTSUP;
+
+	buffer_init_sink(&buffer);
+
+	ret = __nvl_pack(&ops->pack, &buffer, nvl, false);
+
+	return ret ? ret : buffer_used(&buffer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/val_unpack.c	Sun Apr 01 13:17:25 2018 -0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017-2018 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/nvl.h>
+#include <jeffpc/buffer.h>
+
+#include "val_impl_packing.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;
+
+	ops = select_ops(format);
+	if (!ops)
+		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);
+}