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