Mercurial > libjeffpc
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);