common: more versatile data serialization via native-types.
authorKrisztian Litkey <kli@iki.fi>
Fri, 25 Oct 2013 09:38:29 +0000 (12:38 +0300)
committerKrisztian Litkey <kli@iki.fi>
Sun, 24 Nov 2013 19:25:18 +0000 (21:25 +0200)
Added native-types (and a primitive TLV encoder/decoder) to
provide more flexible and versatile data/type serialization.

Ideally (and eventually) we'd like to be able to throw any
natively declared data type at any supported transport and
have that automatically take care of the serialization and
deserialization necessary for transmission. Although with
this initial implementation we're far not there yet, this
should already provide a much more flexible framework to
work with and later build upon than what the original
MRP_TRANSPORT_MODE_DATA is capable of providing.

There are still quite a few rough edges and features either
missing or not fully implemented, including but not limited
to

  - support for unions,
  - implement blobs,
  - automatic handling of linked lists of known types
  - support all the (sensible) type/layout combinations
  - allow self- and cross-referencing data types (IOW
    allow forward-references to yet undeclared types, and
    provide a function for checking if a type is complete,
    ie. there are no undeclared types it references, and
    this holds true also for all its referenced types)

Eventually we'd like to get to the point where, instead of
having to keep the limitations of the available transport
encoding and decoding in mind, one could freely design data
types as seen fit, and the transports could always handle
them.

src/Makefile.am
src/common/native-types.c [new file with mode: 0644]
src/common/native-types.h [new file with mode: 0644]
src/common/tests/Makefile.am
src/common/tests/native-test.c [new file with mode: 0644]
src/common/tlv.c [new file with mode: 0644]
src/common/tlv.h [new file with mode: 0644]

index d9d431a..5a68ba6 100644 (file)
@@ -59,7 +59,9 @@ libmurphy_common_la_HEADERS =         \
                common/refcnt.h         \
                common/fragbuf.h        \
                common/json.h           \
-               common/transport.h
+               common/transport.h      \
+               common/tlv.h            \
+               common/native-types.h
 
 libmurphy_common_la_REGULAR_SOURCES =          \
                common/log.c                    \
@@ -77,7 +79,9 @@ libmurphy_common_la_REGULAR_SOURCES =         \
                common/transport.c              \
                common/stream-transport.c       \
                common/internal-transport.c     \
-               common/dgram-transport.c
+               common/dgram-transport.c        \
+               common/tlv.c                    \
+               common/native-types.c
 
 libmurphy_common_la_SOURCES =                          \
                $(libmurphy_common_la_REGULAR_SOURCES)  \
diff --git a/src/common/native-types.c b/src/common/native-types.c
new file mode 100644 (file)
index 0000000..31883a2
--- /dev/null
@@ -0,0 +1,1470 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/tlv.h>
+#include <murphy/common/native-types.h>
+
+
+/*
+ * TLV tags we use when encoding/decoding our native types
+ */
+
+typedef enum {
+    TAG_NONE = MRP_TLV_UNTAGGED,         /* untagged data */
+    TAG_STRUCT,                          /* a native structure */
+    TAG_MEMBER,                          /* a native structure member */
+    TAG_ARRAY,                           /* an array */
+    TAG_NELEM,                           /* size of an array (in elements) */
+} tag_t;
+
+
+/*
+ * extra header we use to keep track of memory while decoding
+ */
+
+typedef struct {
+    mrp_list_hook_t hook;                /* hook to chunk list */
+    char            data[0];             /* user-visible data */
+} chunk_t;
+
+
+static int encode_struct(mrp_tlv_t *tlv, void *data, mrp_native_type_t *t,
+                         mrp_typemap_t *idmap);
+static int decode_struct(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+                         void **datap, uint32_t *idp, mrp_typemap_t *idmap);
+static int print_struct(char **buf, size_t *size, int level,
+                        void *data, mrp_native_type_t *t);
+static void free_native(mrp_native_type_t *t);
+
+static void *alloc_chunk(mrp_list_hook_t **chunks, size_t size);
+static void free_chunks(mrp_list_hook_t *chunks);
+
+
+/*
+ * list and table of registered native types
+ */
+
+static MRP_LIST_HOOK(types);
+static int           ntype;
+
+static mrp_native_type_t **typetbl;
+
+
+static mrp_native_member_t *native_member(mrp_native_type_t *t, int idx)
+{
+    if (0 <= idx && idx < (int)t->nmember)
+        return t->members + idx;
+    else {
+        errno = EINVAL;
+        return NULL;
+    }
+}
+
+
+static int member_index(mrp_native_type_t *t, const char *name)
+{
+    mrp_native_member_t *m;
+    size_t               i;
+
+    for (i = 0, m = t->members; i < t->nmember; i++, m++)
+        if (!strcmp(m->any.name, name))
+            return m - t->members;
+
+    return -1;
+}
+
+
+static int copy_member(mrp_native_type_t *t, mrp_native_member_t *m)
+{
+    mrp_native_member_t *tm;
+
+    if ((tm = native_member(t, member_index(t, m->any.name))) != NULL)
+        return tm - t->members;
+    else
+        tm = t->members + t->nmember;
+
+    *tm = *m;
+
+    if ((tm->any.name = mrp_strdup(m->any.name)) != 0) {
+        t->nmember++;
+        return tm - t->members;
+    }
+    else
+        return -1;
+}
+
+
+static mrp_native_type_t *find_type(const char *type_name)
+{
+    mrp_native_type_t *t;
+    mrp_list_hook_t   *p, *n;
+
+    mrp_list_foreach(&types, p, n) {
+        t = mrp_list_entry(p, typeof(*t), hook);
+
+        if (!strcmp(t->name, type_name))
+            return t;
+    }
+
+    return NULL;
+}
+
+
+static mrp_native_type_t *lookup_type(uint32_t id)
+{
+    mrp_native_type_t *t;
+    mrp_list_hook_t   *p, *n;
+
+    /* XXX TODO: turn this into a real lookup instead of linear search */
+
+    if (1 <= id && id <= (uint32_t)ntype)
+        if ((t = typetbl[id]) != NULL && t->id == id)
+            return t;
+
+    mrp_log_warning("Type lookup for %u failed, doing linear search...\n", id);
+
+    mrp_list_foreach(&types, p, n) {
+        t = mrp_list_entry(p, typeof(*t), hook);
+
+        if (t->id == id)
+            return t;
+    }
+
+    return NULL;
+}
+
+
+static mrp_native_type_t *member_type(mrp_native_member_t *m)
+{
+    mrp_native_type_t *t;
+
+    if (m->any.type != MRP_TYPE_STRUCT)
+        t = lookup_type(m->any.type);
+    else
+        t = lookup_type(m->strct.data_type.id);
+
+    if (t == NULL)
+        errno = EINVAL;
+
+    return t;
+}
+
+
+static inline uint32_t map_type(uint32_t id, mrp_typemap_t *idmap)
+{
+    uint32_t mapped = MRP_INVALID_TYPE;
+
+    if (id < MRP_TYPE_STRUCT || idmap == NULL)
+        mapped = id;
+    else {
+        while (idmap->typeid != MRP_INVALID_TYPE) {
+            if (idmap->typeid == id) {
+                mapped = MRP_TYPE_STRUCT + idmap->mapped;
+                break;
+            }
+            else
+                idmap++;
+        }
+    }
+
+    return mapped;
+}
+
+
+static inline uint32_t mapped_type(uint32_t mapped, mrp_typemap_t *idmap)
+{
+    uint32_t id = MRP_INVALID_TYPE;
+
+    if (mapped < MRP_TYPE_STRUCT || idmap == NULL)
+        id = mapped;
+    else {
+        while (idmap->typeid != MRP_INVALID_TYPE) {
+            if (MRP_TYPE_STRUCT + idmap->mapped == mapped) {
+                id = idmap->typeid;
+                break;
+            }
+            else
+                idmap++;
+        }
+    }
+
+    return id;
+}
+
+
+uint32_t mrp_type_id(const char *type_name)
+{
+    mrp_native_type_t *t;
+
+    if ((t = find_type(type_name)) != NULL)
+        return t->id;
+    else
+        return MRP_INVALID_TYPE;
+}
+
+
+static size_t type_size(uint32_t id)
+{
+    mrp_native_type_t *t = lookup_type(id);
+
+    if (t != NULL)
+        return t->size;
+    else
+        return 0;
+}
+
+
+static int matching_types(mrp_native_type_t *t1, mrp_native_type_t *t2)
+{
+    MRP_UNUSED(t1);
+    MRP_UNUSED(t2);
+
+    /* XXX TODO */
+    return 0;
+}
+
+
+static void register_default_types(void)
+{
+#define DEFAULT_NTYPE (MRP_TYPE_STRUCT + 1)
+
+#define DECLARE_TYPE(_ctype, _mtype)            \
+    static mrp_native_type_t _mtype##_type = {  \
+        .name    = #_ctype,                     \
+        .id      = MRP_TYPE_##_mtype,           \
+        .size    = sizeof(_ctype),              \
+        .members = NULL,                        \
+        .nmember = 0,                           \
+        .hook    = { NULL, NULL }               \
+    }
+
+#define REGISTER_TYPE(_type)                    \
+    mrp_list_init(&(_type)->hook);              \
+    mrp_list_append(&types, &(_type)->hook);    \
+    typetbl[(_type)->id] = (_type)
+
+    if (mrp_reallocz(typetbl, 0, DEFAULT_NTYPE) == NULL) {
+        mrp_log_error("Failed to initialize native type table.");
+        abort();
+    }
+
+    DECLARE_TYPE( int8_t , INT8  );
+    DECLARE_TYPE(uint8_t , UINT8 );
+    DECLARE_TYPE(int16_t , INT16 );
+    DECLARE_TYPE(uint16_t, UINT16);
+    DECLARE_TYPE(int32_t , INT32 );
+    DECLARE_TYPE(uint32_t, UINT32);
+    DECLARE_TYPE(int64_t , INT64 );
+    DECLARE_TYPE(uint64_t, UINT64);
+    DECLARE_TYPE(float   , FLOAT );
+    DECLARE_TYPE(double  , DOUBLE);
+    DECLARE_TYPE(bool    , BOOL  );
+    DECLARE_TYPE(char *  , STRING);
+    DECLARE_TYPE(void *  , BLOB  );
+    DECLARE_TYPE(void *  , ARRAY );
+    DECLARE_TYPE(void *  , STRUCT);
+
+
+    REGISTER_TYPE(&INT8_type);
+    REGISTER_TYPE(&UINT8_type);
+    REGISTER_TYPE(&INT16_type);
+    REGISTER_TYPE(&UINT16_type);
+    REGISTER_TYPE(&INT32_type);
+    REGISTER_TYPE(&UINT32_type);
+    REGISTER_TYPE(&INT64_type);
+    REGISTER_TYPE(&UINT64_type);
+    REGISTER_TYPE(&FLOAT_type);
+    REGISTER_TYPE(&DOUBLE_type);
+    REGISTER_TYPE(&BOOL_type);
+    REGISTER_TYPE(&STRING_type);
+    REGISTER_TYPE(&BLOB_type);
+    REGISTER_TYPE(&ARRAY_type);
+    REGISTER_TYPE(&STRUCT_type);
+
+    ntype = DEFAULT_NTYPE;
+
+#undef DECLARE_TYPE
+#undef REGISTER_TYPE
+}
+
+
+uint32_t mrp_register_native(mrp_native_type_t *type)
+{
+    mrp_native_type_t   *existing = find_type(type->name);
+    mrp_native_type_t   *t, *elemt;
+    mrp_native_member_t *s, *d, *m;
+    int                  idx;
+
+    (void)member_type;
+
+    if (existing != NULL && !matching_types(existing, type)) {
+        errno = EEXIST;
+        return MRP_INVALID_TYPE;
+    }
+
+    if (ntype == 0)
+        register_default_types();
+
+    if ((t = mrp_allocz(sizeof(*t))) == NULL)
+        return MRP_INVALID_TYPE;
+
+    mrp_list_init(&t->hook);
+    t->name = mrp_strdup(type->name);
+
+    if (t->name == NULL)
+        goto fail;
+
+    t->size    = type->size;
+    t->members = mrp_allocz_array(mrp_native_member_t, type->nmember);
+
+    if (t->members == NULL && type->nmember != 0)
+        goto fail;
+
+    /*
+     * Notes:
+     *
+     *   While we copy the members, we also take care of reordering them
+     *   so that any member that another one depends on ('size' members)
+     *   get registered (and consequently encoded and decoded) before the
+     *   dependant members.
+     */
+
+    s = type->members;
+    d = t->members;
+    while (t->nmember < type->nmember) {
+        /* make sure there are no duplicate members */
+        if (native_member(type, member_index(type, s->any.name)) != s) {
+            errno = EINVAL;
+            goto fail;
+        }
+
+        /* skip already copied members */
+        while (member_index(t, s->any.name) >= 0)
+            s++;
+
+        switch (s->any.type) {
+        case MRP_TYPE_BLOB:
+            m = native_member(t, member_index(t, s->blob.size.name));
+
+            if (m == NULL) {
+                m = native_member(type,
+                                      member_index(type, s->blob.size.name));
+
+                if (m == NULL)
+                    goto fail;
+                else
+                    idx = copy_member(t, m);
+
+                if (idx < 0)
+                    goto fail;
+            }
+            else
+                idx = m - t->members;
+
+            if (copy_member(t, s) < 0)
+                goto fail;
+
+            d = t->members + t->nmember;
+            d->blob.size.idx = idx;
+
+            break;
+
+        case MRP_TYPE_ARRAY:
+            if (s->array.kind == MRP_ARRAY_SIZE_EXPLICIT) {
+                m = native_member(t, member_index(t, s->array.size.name));
+
+                if (m == NULL) {
+                    m = native_member(type,
+                                          member_index(type,
+                                                       s->array.size.name));
+
+                    if (m == NULL)
+                        goto fail;
+                    else
+                        idx = copy_member(t, m);
+
+                    if (idx < 0)
+                        goto fail;
+
+                }
+                else
+                    idx = m - t->members;
+
+                d = t->members + t->nmember;
+
+                if (copy_member(t, s) < 0)
+                    goto fail;
+
+                d->array.size.idx = idx;
+            }
+            else {
+                d = t->members + t->nmember;
+
+                if (copy_member(t, s) < 0)
+                    goto fail;
+            }
+
+            d->array.elem.id = mrp_type_id(d->array.elem.name);
+
+            if (d->array.elem.id == MRP_INVALID_TYPE)
+                goto fail;
+
+            if (s->array.kind == MRP_ARRAY_SIZE_GUARDED) {
+                elemt = lookup_type(d->array.elem.id);
+
+                if (elemt == NULL)
+                    goto fail;
+
+                idx = member_index(elemt, s->array.size.name);
+
+                d->array.size.idx = member_index(elemt, s->array.size.name);
+
+                if (d->array.size.idx == (uint32_t)-1)
+                    goto fail;
+            }
+
+            break;
+
+        case MRP_TYPE_STRUCT:
+            d = t->members + t->nmember;
+
+            if (copy_member(t, s) < 0)
+                goto fail;
+
+            d->strct.data_type.id = mrp_type_id(d->strct.data_type.name);
+
+            if (d->strct.data_type.id == MRP_INVALID_TYPE)
+                goto fail;
+            break;
+
+        default:
+            if (copy_member(t, s) < 0)
+                goto fail;
+        }
+    }
+
+    if (mrp_reallocz(typetbl, ntype, ntype + 1) == NULL)
+        goto fail;
+
+    t->id = ntype;
+    mrp_list_append(&types, &t->hook);
+    typetbl[ntype] = t;
+    ntype++;
+
+    return t->id;
+
+ fail:
+    free_native(t);
+
+    return MRP_INVALID_TYPE;
+}
+
+
+static void free_native(mrp_native_type_t *t)
+{
+    mrp_native_member_t *m;
+    size_t               i;
+
+    if (t == NULL)
+        return;
+
+    mrp_list_delete(&t->hook);
+
+    mrp_free(t->name);
+    for (i = 0, m = t->members; i < t->nmember; i++, m++)
+        mrp_free(m->any.name);
+    mrp_free(t);
+}
+
+
+static int encode_basic(mrp_tlv_t *tlv, mrp_type_t type, mrp_value_t *v)
+{
+    switch (type) {
+    case MRP_TYPE_INT8:    return mrp_tlv_push_int8  (tlv, TAG_NONE, v->s8);
+    case MRP_TYPE_UINT8:   return mrp_tlv_push_uint8 (tlv, TAG_NONE, v->u8);
+    case MRP_TYPE_INT16:   return mrp_tlv_push_int16 (tlv, TAG_NONE, v->s16);
+    case MRP_TYPE_UINT16:  return mrp_tlv_push_uint16(tlv, TAG_NONE, v->u16);
+    case MRP_TYPE_INT32:   return mrp_tlv_push_int32 (tlv, TAG_NONE, v->s32);
+    case MRP_TYPE_UINT32:  return mrp_tlv_push_uint32(tlv, TAG_NONE, v->u32);
+    case MRP_TYPE_INT64:   return mrp_tlv_push_int64 (tlv, TAG_NONE, v->s64);
+    case MRP_TYPE_UINT64:  return mrp_tlv_push_uint64(tlv, TAG_NONE, v->u64);
+    case MRP_TYPE_FLOAT:   return mrp_tlv_push_float (tlv, TAG_NONE, v->flt);
+    case MRP_TYPE_DOUBLE:  return mrp_tlv_push_double(tlv, TAG_NONE, v->dbl);
+    case MRP_TYPE_BOOL:    return mrp_tlv_push_bool  (tlv, TAG_NONE, v->bln);
+    case MRP_TYPE_STRING:  return mrp_tlv_push_string(tlv, TAG_NONE, v->str);
+    default:               return -1;
+    }
+}
+
+
+static inline int get_blob_size(void *base, mrp_native_type_t *t,
+                                mrp_native_blob_t *m, size_t *sizep)
+{
+    mrp_native_member_t *sizem;
+    mrp_value_t         *v;
+
+    if ((sizem = native_member(t, m->size.idx)) == NULL)
+        return -1;
+
+    if (sizem->any.layout == MRP_LAYOUT_INDIRECT)
+        v = *(void **)base;
+    else
+        v = base;
+
+    switch (sizem->any.type) {
+    case MRP_TYPE_INT8:   *sizep = v->s8;          return 0;
+    case MRP_TYPE_UINT8:  *sizep = v->u8;          return 0;
+    case MRP_TYPE_INT16:  *sizep = v->s16;         return 0;
+    case MRP_TYPE_UINT16: *sizep = v->u16;         return 0;
+    case MRP_TYPE_INT32:  *sizep = v->s32;         return 0;
+    case MRP_TYPE_UINT32: *sizep = v->u32;         return 0;
+    case MRP_TYPE_INT64:  *sizep = (size_t)v->s32; return 0;
+    case MRP_TYPE_UINT64: *sizep = (size_t)v->u32; return 0;
+    default:
+        errno = EINVAL;
+        return -1;
+    }
+}
+
+
+static int guard_offset_and_size(mrp_native_array_t *m, size_t *offsp,
+                                 size_t *sizep)
+{
+    mrp_native_type_t   *t = lookup_type(m->elem.id);
+    mrp_native_member_t *g;
+
+    if (t == NULL)
+        return -1;
+
+    switch (t->id) {
+    case MRP_TYPE_INT8:
+    case MRP_TYPE_UINT8:
+    case MRP_TYPE_INT16:
+    case MRP_TYPE_UINT16:
+    case MRP_TYPE_INT32:
+    case MRP_TYPE_UINT32:
+    case MRP_TYPE_INT64:
+    case MRP_TYPE_UINT64:
+    case MRP_TYPE_FLOAT:
+    case MRP_TYPE_DOUBLE:
+    case MRP_TYPE_BOOL:
+    case MRP_TYPE_STRING:
+        *offsp = 0;
+        *sizep = t->size;
+        return 0;
+
+    default:
+        if ((g = native_member(t, m->size.idx)) == NULL)
+            return -1;
+
+        *offsp = g->any.offs;
+        *sizep = type_size(g->any.type);
+        return 0;
+    }
+}
+
+
+static inline int get_explicit_array_size(void *base, mrp_native_type_t *t,
+                                          mrp_native_array_t *m)
+{
+    mrp_native_member_t *nelemm;
+    mrp_value_t         *v;
+    int                  n;
+
+    if ((nelemm = native_member(t, m->size.idx)) == NULL)
+        return -1;
+    if (nelemm->any.layout == MRP_LAYOUT_INDIRECT)
+        v = *(void **)(base + nelemm->any.offs);
+    else
+        v = base + nelemm->any.offs;
+
+    switch (nelemm->any.type) {
+    case MRP_TYPE_INT8:   n = v->s8;       break;
+    case MRP_TYPE_UINT8:  n = v->u8;       break;
+    case MRP_TYPE_INT16:  n = v->s16;      break;
+    case MRP_TYPE_UINT16: n = v->u16;      break;
+    case MRP_TYPE_INT32:  n = v->s32;      break;
+    case MRP_TYPE_UINT32: n = v->u32;      break;
+    case MRP_TYPE_INT64:  n = (int)v->s64; break;
+    case MRP_TYPE_UINT64: n = (int)v->u64; break;
+    default:
+        errno = EINVAL;
+        return -1;
+    }
+
+    return n;
+}
+
+
+static inline int get_guarded_array_size(void *arrp, mrp_native_array_t *m)
+{
+    mrp_value_t *guard;
+    size_t       goffs, gsize, esize;
+    int          n;
+
+    if ((esize = type_size(m->elem.id)) == 0)
+        return -1;
+
+    if (guard_offset_and_size(m, &goffs, &gsize) < 0)
+        return -1;
+
+    guard = &m->sentinel;
+
+    for (n = 0; memcmp(arrp + n * esize + goffs, guard, gsize); n++)
+            ;
+    return n;
+}
+
+
+static int get_array_size(void *base, mrp_native_type_t *t, void *arrp,
+                          mrp_native_array_t *m, size_t *nelemp,
+                          size_t *esizep)
+{
+    int n;
+
+    if ((*esizep = type_size(m->elem.id)) == 0)
+        return -1;
+
+    switch (m->kind) {
+    case MRP_ARRAY_SIZE_FIXED:
+        *nelemp = m->size.nelem;
+        return 0;
+
+    case MRP_ARRAY_SIZE_EXPLICIT:
+        if ((n = get_explicit_array_size(base, t, m)) < 0)
+            return -1;
+
+        *nelemp = (size_t)n;
+        return 0;
+
+    case MRP_ARRAY_SIZE_GUARDED:
+        if ((n = get_guarded_array_size(arrp, m)) < 0)
+            return -1;
+
+        *nelemp = (size_t)n;
+        return 0;
+
+    default:
+        return -1;
+    }
+}
+
+
+static int terminate_guarded_array(void *elem, mrp_native_array_t *m,
+                                   mrp_native_type_t *mt)
+{
+    mrp_native_member_t *g;
+
+    if (m->elem.id <= MRP_TYPE_STRING)
+        memcpy(elem, &m->sentinel, mt->size);
+    else if (m->elem.id > MRP_TYPE_STRUCT) {
+        if ((g = native_member(mt, m->size.idx)) == NULL)
+            return -1;
+
+        memcpy(elem + g->any.offs, &m->sentinel, type_size(g->any.type));
+    }
+
+    return 0;
+}
+
+
+static int encode_array(mrp_tlv_t *tlv, void *arrp, mrp_native_array_t *m,
+                        size_t nelem, size_t elem_size, mrp_typemap_t *idmap)
+{
+    mrp_native_type_t *t;
+    mrp_value_t       *v;
+    void              *elem;
+    size_t             i;
+
+    if (mrp_tlv_push_uint32(tlv, TAG_ARRAY, map_type(m->elem.id, idmap)) < 0)
+        return -1;
+
+    if (mrp_tlv_push_uint32(tlv, TAG_NELEM, nelem) < 0)
+        return -1;
+
+    if ((t = lookup_type(m->elem.id)) == NULL)
+        return -1;
+
+    for (i = 0, elem = arrp; i < nelem; i++, elem += elem_size) {
+        v = elem;
+
+        switch (t->id) {
+        case MRP_TYPE_INT8:
+        case MRP_TYPE_UINT8:
+        case MRP_TYPE_INT16:
+        case MRP_TYPE_UINT16:
+        case MRP_TYPE_INT32:
+        case MRP_TYPE_UINT32:
+        case MRP_TYPE_INT64:
+        case MRP_TYPE_UINT64:
+        case MRP_TYPE_FLOAT:
+        case MRP_TYPE_DOUBLE:
+        case MRP_TYPE_BOOL:
+        case MRP_TYPE_STRING:
+            if (encode_basic(tlv, t->id, v) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+            return -1;
+
+        case MRP_TYPE_ARRAY:
+            return -1;
+
+        default:
+            /* an MRP_TYPE_STRUCT */
+            if (encode_struct(tlv, elem, t, idmap) < 0)
+                return -1;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+
+static int encode_struct(mrp_tlv_t *tlv, void *data, mrp_native_type_t *t,
+                         mrp_typemap_t *idmap)
+{
+    mrp_native_member_t *m;
+    mrp_native_type_t   *mt;
+    mrp_value_t         *v;
+    uint32_t             idx;
+    size_t               size, nelem;
+
+    if (t == NULL)
+        return -1;
+
+    if (mrp_tlv_push_uint32(tlv, TAG_STRUCT, map_type(t->id, idmap)) < 0)
+        return -1;
+
+    for (idx = 0, m = t->members; idx < t->nmember; idx++, m++) {
+        if (mrp_tlv_push_uint32(tlv, TAG_MEMBER, idx) < 0)
+            return -1;
+
+        if (m->any.layout == MRP_LAYOUT_INDIRECT)
+            v = *(void **)(data + m->any.offs);
+        else
+            v = data + m->any.offs;
+
+        switch (m->any.type) {
+        case MRP_TYPE_INT8:
+        case MRP_TYPE_UINT8:
+        case MRP_TYPE_INT16:
+        case MRP_TYPE_UINT16:
+        case MRP_TYPE_INT32:
+        case MRP_TYPE_UINT32:
+        case MRP_TYPE_INT64:
+        case MRP_TYPE_UINT64:
+        case MRP_TYPE_FLOAT:
+        case MRP_TYPE_DOUBLE:
+        case MRP_TYPE_BOOL:
+        case MRP_TYPE_STRING:
+            if (encode_basic(tlv, m->any.type, v) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+            if (get_blob_size(data, t, &m->blob, &size) < 0)
+                return -1;
+            return -1;
+
+        case MRP_TYPE_ARRAY:
+            if (get_array_size(data, t, v->ptr, &m->array, &nelem, &size) < 0)
+                return -1;
+            if (encode_array(tlv, v->ptr, &m->array, nelem,
+                             size, idmap) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_STRUCT:
+            if ((mt = lookup_type(m->strct.data_type.id)) == NULL)
+                return -1;
+            if (encode_struct(tlv, v->ptr, mt, idmap) < 0)
+                return -1;
+            break;
+
+        default:
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+
+int mrp_encode_native(void *data, uint32_t id, size_t reserve, void **bufp,
+                      size_t *sizep, mrp_typemap_t *idmap)
+{
+    mrp_native_type_t *t = lookup_type(id);
+    mrp_tlv_t          tlv;
+
+    *bufp  = NULL;
+    *sizep = 0;
+
+    if (t == NULL)
+        return -1;
+
+    if (mrp_tlv_setup_write(&tlv, reserve + 4096) < 0)
+        return -1;
+
+    if (reserve > 0)
+        if (mrp_tlv_reserve(&tlv, reserve, 1) == NULL)
+            goto fail;
+
+    if (encode_struct(&tlv, data, t, idmap) < 0)
+        goto fail;
+
+    mrp_tlv_trim(&tlv);
+    mrp_tlv_steal(&tlv, bufp, sizep);
+
+    return 0;
+
+ fail:
+    mrp_tlv_cleanup(&tlv);
+    return -1;
+}
+
+
+static void *allocate_indirect(mrp_list_hook_t **chunks, mrp_value_t *v,
+                               mrp_native_member_t *m, mrp_typemap_t *idmap)
+{
+    size_t size;
+
+    switch (m->any.type) {
+    case MRP_TYPE_INT8:
+    case MRP_TYPE_UINT8:
+        return (v->ptr = alloc_chunk(chunks, sizeof(int8_t)));
+    case MRP_TYPE_INT16:
+    case MRP_TYPE_UINT16:
+        return (v->ptr = alloc_chunk(chunks, sizeof(int16_t)));
+    case MRP_TYPE_INT32:
+    case MRP_TYPE_UINT32:
+        return (v->ptr = alloc_chunk(chunks, sizeof(int32_t)));
+    case MRP_TYPE_INT64:
+    case MRP_TYPE_UINT64:
+        return (v->ptr = alloc_chunk(chunks, sizeof(int64_t)));
+    case MRP_TYPE_FLOAT:
+        return (v->ptr = alloc_chunk(chunks, sizeof(float)));
+    case MRP_TYPE_DOUBLE:
+        return (v->ptr = alloc_chunk(chunks, sizeof(double)));
+    case MRP_TYPE_BOOL:
+        return (v->ptr = alloc_chunk(chunks, sizeof(bool)));
+    case MRP_TYPE_STRING:
+        return v;                        /* will be allocated by TLV pull */
+    case MRP_TYPE_BLOB:
+        return v;                        /* will be allocated by decoder */
+    case MRP_TYPE_ARRAY:
+        return v;                        /* will be allocated by decoder */
+    case MRP_TYPE_STRUCT:
+        if ((size = type_size(mapped_type(m->strct.data_type.id, idmap))) == 0)
+            return NULL;
+        return (v->ptr = alloc_chunk(chunks, size));
+    default:
+        return NULL;
+    }
+}
+
+
+static void *alloc_str_chunk(size_t size, void *chunksp)
+{
+    return alloc_chunk((mrp_list_hook_t **)chunksp, size);
+}
+
+
+static int decode_basic(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+                        mrp_type_t type, mrp_value_t *v)
+{
+    switch (type) {
+    case MRP_TYPE_INT8:    return mrp_tlv_pull_int8  (tlv, TAG_NONE, &v->s8);
+    case MRP_TYPE_UINT8:   return mrp_tlv_pull_uint8 (tlv, TAG_NONE, &v->u8);
+    case MRP_TYPE_INT16:   return mrp_tlv_pull_int16 (tlv, TAG_NONE, &v->s16);
+    case MRP_TYPE_UINT16:  return mrp_tlv_pull_uint16(tlv, TAG_NONE, &v->u16);
+    case MRP_TYPE_INT32:   return mrp_tlv_pull_int32 (tlv, TAG_NONE, &v->s32);
+    case MRP_TYPE_UINT32:  return mrp_tlv_pull_uint32(tlv, TAG_NONE, &v->u32);
+    case MRP_TYPE_INT64:   return mrp_tlv_pull_int64 (tlv, TAG_NONE, &v->s64);
+    case MRP_TYPE_UINT64:  return mrp_tlv_pull_uint64(tlv, TAG_NONE, &v->u64);
+    case MRP_TYPE_FLOAT:   return mrp_tlv_pull_float (tlv, TAG_NONE, &v->flt);
+    case MRP_TYPE_DOUBLE:  return mrp_tlv_pull_double(tlv, TAG_NONE, &v->dbl);
+    case MRP_TYPE_BOOL:    return mrp_tlv_pull_bool  (tlv, TAG_NONE, &v->bln);
+    case MRP_TYPE_STRING:  return mrp_tlv_pull_string(tlv, TAG_NONE, &v->strp,-1,
+                                                      alloc_str_chunk, chunks);
+    default:               return -1;
+    }
+}
+
+
+static int decode_array(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+                        void **arrp, mrp_native_array_t *m,
+                        void *data, mrp_native_type_t *t,
+                        mrp_typemap_t *idmap)
+{
+    mrp_native_type_t *mt;
+    mrp_value_t       *v;
+    void              *elem, *base;
+    size_t             elem_size, i;
+    uint32_t           id, nelem;
+    int                n, guard;
+
+    if (mrp_tlv_pull_uint32(tlv, TAG_ARRAY, &id) < 0)
+        return -1;
+
+    if ((id = mapped_type(id, idmap)) != m->elem.id)
+        return -1;
+
+    if ((elem_size = type_size(id)) == 0)
+        return -1;
+
+    if (mrp_tlv_pull_uint32(tlv, TAG_NELEM, &nelem) < 0)
+        return -1;
+
+    if ((mt = lookup_type(m->elem.id)) == NULL)
+        return -1;
+
+    switch (m->kind) {
+    case MRP_ARRAY_SIZE_EXPLICIT:
+        if ((n = get_explicit_array_size(data, t, m)) < 0)
+            return -1;
+        guard = 0;
+        break;
+    case MRP_ARRAY_SIZE_FIXED:
+        n     = m->size.nelem;
+        guard = 0;
+        break;
+    case MRP_ARRAY_SIZE_GUARDED:
+        n     = nelem;
+        guard = 1;
+        break;
+    default:
+        return -1;
+    }
+
+    if (n != (int)nelem)
+        return -1;
+
+    switch (m->layout) {
+    case MRP_LAYOUT_INLINED:
+        base = (void *)arrp;
+        break;
+    case MRP_LAYOUT_INDIRECT:
+    case MRP_LAYOUT_DEFAULT:
+        if ((*arrp = alloc_chunk(chunks, (nelem + guard) * elem_size)) == NULL)
+            return (nelem + guard) ? -1 : 0;
+        base = *arrp;
+        break;
+    default:
+        return -1;
+    }
+
+    for (i = 0, elem = base; i < nelem; i++, elem += elem_size) {
+        v = elem;
+
+        switch (mt->id) {
+        case MRP_TYPE_INT8:
+        case MRP_TYPE_UINT8:
+        case MRP_TYPE_INT16:
+        case MRP_TYPE_UINT16:
+        case MRP_TYPE_INT32:
+        case MRP_TYPE_UINT32:
+        case MRP_TYPE_INT64:
+        case MRP_TYPE_UINT64:
+        case MRP_TYPE_FLOAT:
+        case MRP_TYPE_DOUBLE:
+        case MRP_TYPE_BOOL:
+        case MRP_TYPE_STRING:
+            if (decode_basic(tlv, chunks, mt->id, v) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+            return -1;
+
+        case MRP_TYPE_ARRAY:
+            return -1;
+
+        default:
+            /* an MRP_TYPE_STRUCT */
+            if (decode_struct(tlv, chunks, &elem, &id, idmap) < 0)
+                return -1;
+        }
+    }
+
+    if (guard) {
+        if (terminate_guarded_array(elem, m, mt) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+
+static int decode_struct(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+                         void **datap, uint32_t *idp, mrp_typemap_t *idmap)
+{
+    mrp_native_type_t   *t;
+    mrp_native_member_t *m;
+    mrp_value_t         *v;
+    char                *str, **strp;
+    size_t               max, i;
+    uint32_t             idx, id;
+
+    if (datap == NULL) {
+        errno = EFAULT;
+        return -1;
+    }
+
+    if (mrp_tlv_pull_uint32(tlv, TAG_STRUCT, &id) < 0)
+        return -1;
+    else
+        id = mapped_type(id, idmap);
+
+    if (*idp) {
+        if (*idp != id) {
+            errno = EINVAL;
+            return -1;
+        }
+    }
+    else
+        *idp = id;
+
+    if ((t = lookup_type(id)) == NULL)
+        return -1;
+
+    if (*datap == NULL)
+        if ((*datap = alloc_chunk(chunks, t->size)) == NULL)
+            return -1;
+
+    for (i = 0, m = t->members; i < t->nmember; i++, m++) {
+        if (mrp_tlv_pull_uint32(tlv, TAG_MEMBER, &idx) < 0)
+            return -1;
+
+        v = *datap + m->any.offs;
+
+        if (m->any.layout == MRP_LAYOUT_INDIRECT) {
+            if ((v = allocate_indirect(chunks, v, m, idmap)) == NULL)
+                return -1;
+        }
+
+        switch (m->any.type) {
+        case MRP_TYPE_INT8:
+        case MRP_TYPE_UINT8:
+        case MRP_TYPE_INT16:
+        case MRP_TYPE_UINT16:
+        case MRP_TYPE_INT32:
+        case MRP_TYPE_UINT32:
+        case MRP_TYPE_INT64:
+        case MRP_TYPE_UINT64:
+        case MRP_TYPE_FLOAT:
+        case MRP_TYPE_DOUBLE:
+        case MRP_TYPE_BOOL:
+            if (decode_basic(tlv, chunks, m->any.type, v) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_STRING:
+            if (m->any.layout == MRP_LAYOUT_INLINED) {
+                max  = m->str.size;
+                str  = v->str;
+                strp = &str;
+            }
+            else {
+                max  = (size_t)-1;
+                strp = &v->strp;
+            }
+            if (mrp_tlv_pull_string(tlv, TAG_NONE, strp, max,
+                                    alloc_str_chunk, chunks) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+            return -1;
+
+        case MRP_TYPE_ARRAY:
+            if (decode_array(tlv, chunks, &v->ptr, &m->array,
+                             *datap, t, idmap) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_STRUCT:
+            id = m->strct.data_type.id;
+            if (decode_struct(tlv, chunks, &v->ptr, &id, idmap) < 0)
+                return -1;
+            break;
+
+        default:
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+
+int mrp_decode_native(void **bufp, size_t *sizep, void **datap, uint32_t *idp,
+                      mrp_typemap_t *idmap)
+{
+    mrp_tlv_t        tlv;
+    mrp_list_hook_t *chunks;
+    void            *data;
+    size_t           diff;
+
+    chunks = NULL;
+    data   = NULL;
+
+    if (mrp_tlv_setup_read(&tlv, *bufp, *sizep) < 0)
+        return -1;
+
+    if (decode_struct(&tlv, &chunks, &data, idp, idmap) == 0) {
+        diff = mrp_tlv_offset(&tlv);
+
+        if (diff <= *sizep) {
+            *bufp  += diff;
+            *sizep -= diff;
+            *datap  = data;
+
+            return 0;
+        }
+    }
+
+    free_chunks(chunks);
+
+    return -1;
+}
+
+
+void mrp_free_native(void *data, uint32_t id)
+{
+    mrp_list_hook_t *chunks;
+
+    MRP_UNUSED(id);
+
+    if (data != NULL) {
+        chunks = ((void *)data) - MRP_OFFSET(chunk_t, data);
+        free_chunks(chunks);
+    }
+}
+
+
+#define INDENT(_level, _fmt) "%*.*s"_fmt, _level * 4, _level * 4, ""
+
+#define PRINT(_l, _p, _size, fmt, args...) do {                     \
+        ssize_t _n;                                                 \
+        _n = snprintf((_p), (_size), INDENT(_l, fmt), ## args);     \
+        if (_n >= (ssize_t)(_size))                                 \
+            return -1;                                              \
+        (_p)    += _n;                                              \
+        (_size) -= _n;                                              \
+    } while (0)
+
+
+static int print_basic(int level, char **bufp, size_t *sizep, int type,
+                       const char *name, mrp_value_t *v)
+{
+#define NAME name ? name : "", name ? " = " : ""
+    char    *p    = *bufp;
+    size_t   size = *sizep;
+
+    if (type >= MRP_TYPE_BLOB)
+        return -1;
+
+    switch (type) {
+        case MRP_TYPE_INT8:
+            PRINT(level, p, size, "%s%s%d\n", NAME, v->s8);
+            break;
+        case MRP_TYPE_UINT8:
+            PRINT(level, p, size, "%s%s%u\n", NAME, v->u8);
+            break;
+
+        case MRP_TYPE_INT16:
+            PRINT(level, p, size, "%s%s%d\n", NAME, v->s16);
+            break;
+        case MRP_TYPE_UINT16:
+            PRINT(level, p, size, "%s%s%u\n", NAME, v->u16);
+            break;
+
+        case MRP_TYPE_INT32:
+            PRINT(level, p, size, "%s%s%d\n", NAME, v->s32);
+            break;
+        case MRP_TYPE_UINT32:
+            PRINT(level, p, size, "%s%s%u\n", NAME, v->u32);
+            break;
+
+        case MRP_TYPE_INT64:
+            PRINT(level, p, size, "%s%s%lld\n", NAME, (long long)v->s64);
+            break;
+        case MRP_TYPE_UINT64:
+            PRINT(level, p, size, "%s%s%llu\n", NAME,
+                  (unsigned long long)v->s64);
+            break;
+
+        case MRP_TYPE_FLOAT:
+            PRINT(level, p, size, "%s%s%f\n", NAME, v->flt);
+            break;
+        case MRP_TYPE_DOUBLE:
+            PRINT(level, p, size, "%s%s%f\n", NAME, v->dbl);
+            break;
+
+        case MRP_TYPE_BOOL:
+            PRINT(level, p, size, "%s%s%s\n", NAME,
+                  v->bln ? "<true>" : "<false>");
+            break;
+
+        case MRP_TYPE_STRING:
+            PRINT(level, p, size, "%s%s%s\n", NAME,
+                  v->str ? v->str : "<null>");
+            break;
+    }
+
+    *bufp  = p;
+    *sizep = size;
+
+    return 0;
+
+#undef NAME
+}
+
+
+static int print_array(char **bufp, size_t *sizep, int level,
+                       void *arrp, mrp_native_array_t *a, size_t nelem,
+                       size_t elem_size)
+{
+    mrp_native_type_t *et;
+    mrp_value_t       *v;
+    void              *elem;
+    size_t             i;
+    char              *p;
+    size_t             size;
+
+    p    = *bufp;
+    size = *sizep;
+
+    if ((et = lookup_type(a->elem.id)) == NULL)
+        return -1;
+
+    PRINT(level, p, size, "%s = [%s", a->name, nelem == 0 ? "]" : "\n");
+    level++;
+
+    for (i = 0, elem = arrp; i < nelem; i++, elem += elem_size) {
+        v = elem;
+
+        switch (et->id) {
+        case MRP_TYPE_INT8:
+        case MRP_TYPE_UINT8:
+        case MRP_TYPE_INT16:
+        case MRP_TYPE_UINT16:
+        case MRP_TYPE_INT32:
+        case MRP_TYPE_UINT32:
+        case MRP_TYPE_INT64:
+        case MRP_TYPE_UINT64:
+        case MRP_TYPE_FLOAT:
+        case MRP_TYPE_DOUBLE:
+        case MRP_TYPE_BOOL:
+        case MRP_TYPE_STRING:
+            if (print_basic(level, &p, &size, et->id, NULL, v) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_BLOB:
+            PRINT(level, p, size, "<blob>\n");
+            break;
+
+        case MRP_TYPE_ARRAY:
+            return -1;
+
+        default:
+            /* an MRP_TYPE_STRUCT */
+            if (print_struct(&p, &size, level, elem, et) < 0)
+                return -1;
+            break;
+        }
+    }
+
+    level--;
+    PRINT(level, p, size, "%s\n", nelem == 0 ? "" : "]");
+
+    *bufp  = p;
+    *sizep = size;
+
+    return 0;
+}
+
+
+static int print_struct(char **bufp, size_t *sizep, int level,
+                        void *data, mrp_native_type_t *t)
+{
+    mrp_native_member_t *m;
+    mrp_native_type_t   *mt;
+    mrp_value_t         *v;
+    uint32_t             idx;
+    size_t               esize, nelem;
+    char                *p;
+    size_t               size;
+
+    if (data == NULL) {
+        **bufp = '\0';
+
+        return 0;
+    }
+
+    if (t == NULL)
+        return -1;
+
+    p    = *bufp;
+    size = *sizep;
+    PRINT(level, p, size, "{\n");
+    level++;
+
+    for (idx = 0, m = t->members; idx < t->nmember; idx++, m++) {
+        if (m->any.layout == MRP_LAYOUT_INDIRECT)
+            v = *(void **)(data + m->any.offs);
+        else
+            v = data + m->any.offs;
+
+        switch (m->any.type) {
+        case MRP_TYPE_INT8:
+        case MRP_TYPE_UINT8:
+        case MRP_TYPE_INT16:
+        case MRP_TYPE_UINT16:
+        case MRP_TYPE_INT32:
+        case MRP_TYPE_UINT32:
+        case MRP_TYPE_INT64:
+        case MRP_TYPE_UINT64:
+        case MRP_TYPE_FLOAT:
+        case MRP_TYPE_DOUBLE:
+        case MRP_TYPE_BOOL:
+        case MRP_TYPE_STRING:
+            if (print_basic(level, &p, &size, m->any.type, m->any.name, v) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+            PRINT(level, p, size, "%s = <blob>\n", m->any.name);
+            break;
+
+        case MRP_TYPE_ARRAY:
+            if (get_array_size(data, t, v->ptr, &m->array, &nelem, &esize) < 0)
+                return -1;
+            if (print_array(&p, &size, level, v->ptr, &m->array,
+                            nelem, esize) < 0)
+                return -1;
+            break;
+
+        case MRP_TYPE_STRUCT:
+            if ((mt = lookup_type(m->strct.data_type.id)) == NULL)
+                return -1;
+            if (print_struct(&p, &size, level, v->ptr, mt) < 0)
+                return -1;
+            break;
+
+        default:
+            return -1;
+        }
+    }
+
+    level--;
+    PRINT(level, p, size, "}\n");
+
+    *bufp  = p;
+    *sizep = size;
+
+    return 0;
+}
+
+
+ssize_t mrp_print_native(char *buf, size_t size, void *data, uint32_t id)
+{
+    mrp_native_type_t *t;
+    char              *p;
+
+    p = buf;
+
+    if (id < MRP_TYPE_STRUCT || (t = lookup_type(id)) == NULL) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (print_struct(&p, &size, 0, data, t) == 0)
+        return (ssize_t)(p - buf);
+    else
+        return -1;
+}
+
+
+static inline size_t chunk_size(size_t size)
+{
+    return MRP_OFFSET(chunk_t, data[size]);
+}
+
+
+static void *alloc_chunk(mrp_list_hook_t **chunks, size_t size)
+{
+    chunk_t *chunk;
+
+    if (size == 0)
+        return NULL;
+
+    if (*chunks == NULL) {
+        if ((*chunks = mrp_allocz(sizeof(*chunks))) == NULL)
+            return NULL;
+        else
+            mrp_list_init(*chunks);
+    }
+
+    if ((chunk = mrp_allocz(chunk_size(size))) == NULL)
+        return NULL;
+
+    mrp_list_init(&chunk->hook);
+    mrp_list_append(*chunks, &chunk->hook);
+
+    return &chunk->data[0];
+}
+
+
+static void free_chunks(mrp_list_hook_t *chunks)
+{
+    mrp_list_hook_t *p, *n;
+
+    if (chunks != NULL) {
+        mrp_list_foreach(chunks, p, n) {
+            mrp_list_delete(p);
+
+            if (p != chunks)
+                mrp_free(p);
+        }
+
+        mrp_free(chunks);
+    }
+}
diff --git a/src/common/native-types.h b/src/common/native-types.h
new file mode 100644 (file)
index 0000000..768cbb8
--- /dev/null
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_COMMON_NATIVE_TYPES_H__
+#define __MURPHY_COMMON_NATIVE_TYPES_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_INVALID_TYPE ((uint32_t)-1)
+
+
+/**
+ * pre-defined native type ids
+ */
+
+typedef enum {
+    MRP_TYPE_UNKNOWN = 0,
+    MRP_TYPE_INT8,
+    MRP_TYPE_UINT8,
+    MRP_TYPE_INT16,
+    MRP_TYPE_UINT16,
+    MRP_TYPE_INT32,
+    MRP_TYPE_UINT32,
+    MRP_TYPE_INT64,
+    MRP_TYPE_UINT64,
+    MRP_TYPE_FLOAT,
+    MRP_TYPE_DOUBLE,
+    MRP_TYPE_BOOL,
+    MRP_TYPE_STRING,
+    MRP_TYPE_BLOB,
+    MRP_TYPE_ARRAY,
+    MRP_TYPE_STRUCT,
+    MRP_TYPE_MAX
+} mrp_type_t;
+
+
+/**
+ * data type values
+ */
+
+typedef union {
+    int8_t    s8;
+    int8_t   *s8p;
+    uint8_t   u8;
+    uint8_t  *u8p;
+    int16_t   s16;
+    int16_t  *s16p;
+    uint16_t  u16;
+    uint16_t *u16p;
+    int32_t   s32;
+    int32_t  *s32p;
+    uint32_t  u32;
+    uint32_t *u32p;
+    int64_t   s64;
+    int64_t  *s64p;
+    uint64_t  u64;
+    uint64_t *u64p;
+    float     flt;
+    float    *fltp;
+    double    dbl;
+    double   *dblp;
+    bool      bln;
+    bool     *blnp;
+    void     *blb;
+    char      str[0];
+    char     *strp;
+    void     *ptr;
+    void    **ptrp;
+} mrp_value_t;
+
+
+/**
+ * type id map (for transport-specific mapping of type ids)
+ */
+
+typedef struct {
+    uint32_t typeid;                     /* native type id */
+    uint32_t mapped;                     /* mapped type id */
+} mrp_typemap_t;
+
+
+/** Macro to initialize a typemap entry. */
+#define MRP_MAP_TYPE(_mapped_id, _type_id)                              \
+    (mrp_typemap_t) { .typeid = _type_id, .mapped = _mapped_id }
+
+/** Macro to set a typemap termination entry. */
+#define MRP_TYPEMAP_END                                         \
+    (mrp_typemap_t) { MRP_INVALID_TYPE, MRP_INVALID_TYPE }
+
+/**
+ * type and member descriptors
+ */
+
+typedef enum {
+    MRP_LAYOUT_DEFAULT = 0,              /* default, type-specific layout */
+    MRP_LAYOUT_INLINED,                  /* inlined/embedded layout */
+    MRP_LAYOUT_INDIRECT,                 /* indirect layout */
+} mrp_layout_t;
+
+#define MRP_NATIVE_COMMON_FIELDS         /* fields common to all members */ \
+    char              *name;             /* name of this member */          \
+    uint32_t           type;             /* type id of this member */       \
+    size_t             offs;             /* offset from base pointer */     \
+    mrp_layout_t       layout            /* member layout */
+
+typedef struct {
+    MRP_NATIVE_COMMON_FIELDS;            /* common fields to all members */
+} mrp_native_any_t;
+
+typedef struct {                         /* a blob member */
+    MRP_NATIVE_COMMON_FIELDS;            /* common member fields */
+    union {                              /* size-indicating member */
+        char     *name;                  /*     name */
+        uint32_t  idx;                   /*     or index */
+    } size;
+} mrp_native_blob_t;
+
+typedef enum {
+    MRP_ARRAY_SIZE_EXPLICIT,             /* explicitly sized array */
+    MRP_ARRAY_SIZE_GUARDED,              /* sentinel-guarded array */
+    MRP_ARRAY_SIZE_FIXED,                /* a fixed size array */
+} mrp_array_size_t;
+
+typedef struct {
+    MRP_NATIVE_COMMON_FIELDS;            /* common member fields */
+    size_t size;                         /* inlined buffer size */
+} mrp_native_string_t;
+
+typedef struct {                         /* an array member */
+    MRP_NATIVE_COMMON_FIELDS;            /* common member fields */
+    mrp_array_size_t kind;               /* which kind of array */
+    union {                              /* contained element type */
+        char     *name;                  /*     name */
+        uint32_t  id;                    /*     or type id */
+    } elem;
+    union {                              /* size or guard member */
+        char     *name;                  /*     name */
+        uint32_t  idx;                   /*     or index */
+        size_t    nelem;                 /*     or number of elements */
+    } size;
+    mrp_value_t sentinel;                /* sentinel value, if guarded */
+} mrp_native_array_t;
+
+typedef struct {                         /* member of type struct */
+    MRP_NATIVE_COMMON_FIELDS;            /* common member fields */
+    union {                              /* struct type */
+        char     *name;                  /*     name */
+        uint32_t  id;                    /*     or type id */
+    } data_type;
+} mrp_native_struct_t;
+
+typedef union {
+    mrp_native_any_t    any;
+    mrp_native_string_t str;
+    mrp_native_blob_t   blob;
+    mrp_native_array_t  array;
+    mrp_native_struct_t strct;
+} mrp_native_member_t;
+
+typedef struct {
+    char                *name;           /* name of this type */
+    uint32_t             id;             /* assigned id for this type */
+    size_t               size;           /* size of this type */
+    mrp_native_member_t *members;        /* members of this type if any */
+    size_t               nmember;        /* number of members */
+    mrp_list_hook_t      hook;           /* to list of registered types */
+} mrp_native_type_t;
+
+
+/** Helper macro to initialize native member fields. */
+#define __MRP_MEMBER_INIT(_objtype, _member, _type)                     \
+        .name   = #_member,                                             \
+        .type   = _type,                                                \
+        .offs   = MRP_OFFSET(_objtype, _member)
+
+/** Helper macro to declare a native member with a given type an layout. */
+#define __MRP_MEMBER(_objtype, _type, _member, _layout)                 \
+    {                                                                   \
+        .any = {                                                        \
+            __MRP_MEMBER_INIT(_objtype, _member, _type),                \
+            .layout = MRP_LAYOUT_##_layout,                             \
+        }                                                               \
+    }
+
+/** Declare an indirect string member of the native type. */
+#define MRP_INDIRECT_STRING(_objtype, _member, _size)                   \
+    __MRP_MEMBER(_objtype, _member, MRP_TYPE_STRING, INDIRECT)
+
+/** Declare an inlined string member of the native type. */
+#define MRP_INLINED_STRING(_objtype, _member, _size)                    \
+    {                                                                   \
+        .str = {                                                        \
+            __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_STRING),      \
+            .layout = MRP_LAYOUT_INLINED,                               \
+            .size   = _size,                                            \
+        }                                                               \
+    }
+
+/** By default declare a string members indirect. */
+#define MRP_DEFAULT_STRING(_objtype, _member, _size)                    \
+    __MRP_MEMBER(_objtype, MRP_TYPE_STRING, _member, INDIRECT)
+
+/** Declare an explicitly sized array member of the native typet. */
+#define MRP_SIZED_ARRAY(_objtype, _member, _layout, _type, _size)       \
+    {                                                                   \
+        .array = {                                                      \
+            __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY),       \
+            .layout  = MRP_LAYOUT_##_layout,                            \
+            .kind    = MRP_ARRAY_SIZE_EXPLICIT,                         \
+            .elem    = { .name = #_type, },                             \
+            .size    = { .name = #_size, },                             \
+        }                                                               \
+    }
+
+/** Declare a sentinel-guarded array member of the native type. */
+#define MRP_GUARDED_ARRAY(_objtype, _member, _layout, _type, _guard,    \
+                          ...)                                          \
+    {                                                                   \
+        .array = {                                                      \
+            __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY),       \
+            .layout   = MRP_LAYOUT_##_layout,                           \
+            .kind     = MRP_ARRAY_SIZE_GUARDED,                         \
+            .elem     = { .name = #_type, },                            \
+            .size     = { .name = #_guard, },                           \
+            .sentinel = { __VA_ARGS__ },                                \
+        }                                                               \
+    }
+
+/** Declare a fixed array member of the native type. */
+#define MRP_FIXED_ARRAY(_objtype, _member, _layout, _type)              \
+    {                                                                   \
+        .array = {                                                      \
+            __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY),       \
+            .layout   = MRP_LAYOUT_##_layout,                           \
+            .kind     = MRP_ARRAY_SIZE_FIXED,                           \
+            .elem     = { .name = #_type, },                            \
+            .size     = {                                               \
+                .nelem = MRP_ARRAY_SIZE(((_objtype *)0x0)->_member)     \
+            },                                                          \
+        }                                                               \
+    }
+
+/** Declare a struct member of the native type. */
+#define MRP_STRUCT(_objtype, _member, _layout, _type)                   \
+    {                                                                   \
+        .strct = {                                                      \
+            __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_STRUCT),      \
+            .data_type = { .name = #_type },                            \
+        }                                                               \
+    }
+
+/** Macros for declaring basic members of the native type. */
+#define MRP_INT8(_ot, _m, _l)   __MRP_MEMBER(_ot, MRP_TYPE_INT8  , _m, _l)
+#define MRP_UINT8(_ot, _m, _l)  __MRP_MEMBER(_ot, MRP_TYPE_UINT8 , _m, _l)
+#define MRP_INT16(_ot, _m, _l)  __MRP_MEMBER(_ot, MRP_TYPE_INT16 , _m, _l)
+#define MRP_UINT16(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT16, _m, _l)
+#define MRP_INT32(_ot, _m, _l)  __MRP_MEMBER(_ot, MRP_TYPE_INT32 , _m, _l)
+#define MRP_UINT32(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT32, _m, _l)
+#define MRP_INT64(_ot, _m, _l)  __MRP_MEMBER(_ot, MRP_TYPE_INT64 , _m, _l)
+#define MRP_UINT64(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT64, _m, _l)
+#define MRP_FLOAT(_ot, _m, _l)  __MRP_MEMBER(_ot, MRP_TYPE_FLOAT , _m, _l)
+#define MRP_DOUBLE(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_DOUBLE, _m, _l)
+#define MRP_BOOL(_ot, _m, _l)   __MRP_MEMBER(_ot, MRP_TYPE_BOOL  , _m, _l)
+
+/** Macro for declaring string members of the native type. */
+#define MRP_STRING(_objtype, _member, _layout)                  \
+    MRP_##_layout##_STRING(_objtype, _member,                   \
+                           sizeof(((_objtype *)0x0)->_member))
+
+/** Macro for declaring array members of the native type. */
+#define MRP_ARRAY(_objtype, _member, _layout, _kind, ...)       \
+    MRP_##_kind##_ARRAY(_objtype, _member, _layout, __VA_ARGS__)
+
+/** Macro to declare a native type. */
+#define MRP_NATIVE_TYPE(_var, _type, ...)                       \
+    mrp_native_member_t _var##_members[] = {                    \
+        __VA_ARGS__                                             \
+    };                                                          \
+    mrp_native_type_t   _var = {                                \
+        .id      = -1,                                          \
+        .name    = #_type,                                      \
+        .size    = sizeof(_type),                               \
+        .members = _var##_members,                              \
+        .nmember = MRP_ARRAY_SIZE(_var##_members),              \
+        .hook    = { NULL, NULL },                              \
+    }
+
+/** Declare and register the given native type. */
+uint32_t mrp_register_native(mrp_native_type_t *type);
+
+/** Look up the type id of the given native type name. */
+uint32_t mrp_native_id(const char *type_name);
+
+/** Encode data of the given native type. */
+int mrp_encode_native(void *data, uint32_t id, size_t reserve, void **bufp,
+                      size_t *sizep, mrp_typemap_t *idmap);
+
+/** Decode data of (the given) native type (if specified). */
+int mrp_decode_native(void **bufp, size_t *sizep, void **datap, uint32_t *idp,
+                      mrp_typemap_t *idmap);
+
+/** Free data of the given native type, obtained from mrp_decode_native. */
+void mrp_free_native(void *data, uint32_t id);
+
+/** Print data of the given native type. */
+ssize_t mrp_print_native(char *buf, size_t size, void *data, uint32_t id);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_COMMON_NATIVE_TYPES_H__ */
index 3588dfa..c13266a 100644 (file)
@@ -1,7 +1,7 @@
 AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
 
 noinst_PROGRAMS  = mm-test hash-test msg-test transport-test \
-            internal-transport-test process-watch-test
+            internal-transport-test process-watch-test native-test
 if DBUS_ENABLED
 noinst_PROGRAMS += mainloop-test dbus-test
 endif
@@ -51,6 +51,11 @@ msg_test_SOURCES = msg-test.c
 msg_test_CFLAGS  = $(AM_CFLAGS)
 msg_test_LDADD   = ../../libmurphy-common.la
 
+# native type test
+native_test_SOURCES = native-test.c
+native_test_CFLAGS  = $(AM_CFLAGS)
+native_test_LDADD   = ../../libmurphy-common.la
+
 # transport test
 transport_test_SOURCES = transport-test.c
 transport_test_CFLAGS  = $(AM_CFLAGS)
@@ -75,11 +80,6 @@ dbus_test_CFLAGS  = $(AM_CFLAGS) $(DBUS_CFLAGS)
 dbus_test_LDADD   = ../../libmurphy-dbus.la ../../libmurphy-common.la $(DBUS_LIBS)
 endif
 
-## databuf test
-#databuf_test_SOURCES = databuf-test.c
-#databuf_test_CFLAGS  = $(AM_CFLAGS)
-#databuf_test_LDADD   = ../../libmurphy-common.la
-
 # fragbuf test
 fragbuf_test_SOURCES = fragbuf-test.c
 fragbuf_test_CFLAGS  = $(AM_CFLAGS)
diff --git a/src/common/tests/native-test.c b/src/common/tests/native-test.c
new file mode 100644 (file)
index 0000000..5b90e93
--- /dev/null
@@ -0,0 +1,287 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/native-types.h>
+
+
+typedef enum {
+    MUSIC,
+    MOVIE,
+    BOOK,
+    PAINTING,
+} art_type_t;
+
+
+typedef struct {
+    art_type_t  type;
+    char       *artist;
+    char       *title;
+    uint16_t    year;
+    char       *location;
+    double      price;
+} art_t;
+
+
+typedef enum {
+    LEFT = 0,
+    RIGHT,
+    BOTH
+} hand_t;
+
+
+typedef enum {
+    MALE = 0,
+    FEMALE = 1,
+} gender_t;
+
+
+typedef struct {
+    char     *name;
+    gender_t  gender;
+    uint16_t  age;
+    uint16_t  height;
+    float     weight;
+    char      nationality[32];
+    hand_t    hand;
+    bool      glasses;
+    art_t    *favourites;
+    uint32_t  nfavourite;
+} person_t;
+
+
+typedef struct {
+    person_t *father;
+    person_t *mother;
+    person_t *children;
+} family_t;
+
+
+art_t paps_favourites[] = {
+    {
+        BOOK ,
+        "Douglas Adams", "Dirk Gently's Holistic Detective Agency",
+        1987, "bookshelf", 9.5
+    },
+    {
+        MUSIC,
+        "Megadeth", "Sweating Bullets",
+        1992, "pocket", 12.5
+    },
+    {
+        MUSIC,
+        "Sentenced", "Noose",
+        1996, "phone", 12
+    },
+    {
+        MOVIE,
+        "Bananas", "Woody Allen",
+        1971, "PVR", 20.5
+    }
+};
+
+
+person_t pap = {
+    .name        = "Pap",
+    .gender      = MALE,
+    .age         = 30,
+    .height      = 180,
+    .weight      = 84.5,
+    .nationality = "martian",
+    .hand        = RIGHT,
+    .glasses     = false,
+    .favourites  = paps_favourites,
+    .nfavourite  = MRP_ARRAY_SIZE(paps_favourites),
+};
+
+
+art_t moms_favourites[] = {
+    {
+        BOOK ,
+        "Douglas Adams", "THHGTTG",
+        1982, "bookshelf", 11.8
+    },
+    {
+        MUSIC,
+        "Megadeth", "Sweating Bullets",
+        1992, "pocket", 12.5
+    },
+    {
+        MOVIE,
+        "Hottie Chick", "GGW-II",
+        1996, "PVR", 0.5
+    },
+    {
+        BOOK ,
+        "Douglas Adams", "The Long Dark Tea-Time of the Soul",
+        1988, "Kindle Touch", 8.50
+    }
+};
+
+
+person_t mom = {
+    .name        = "Mom",
+    .gender      = FEMALE,
+    .age         = 28,
+    .height      = 165,
+    .weight      = 57.8,
+    .nationality = "venusian",
+    .hand        = LEFT,
+    .glasses     = true,
+    .favourites  = moms_favourites,
+    .nfavourite  = MRP_ARRAY_SIZE(moms_favourites),
+};
+
+
+person_t tom_dick_and_harry[] = {
+    {
+        .name        = "Tom",
+        .gender      = MALE,
+        .age         = 10,
+        .height      = 135,
+        .weight      = 40.5,
+        .nationality = "UFO",
+        .hand        = BOTH,
+        .glasses     = false,
+        .favourites  = NULL,
+        .nfavourite  = 0,
+    },
+    {
+        .name        = "Dick",
+        .gender      = MALE,
+        .age         = 12,
+        .height      = 145,
+        .weight      = 45.5,
+        .nationality = "UFO",
+        .hand        = RIGHT,
+        .glasses     = true,
+        .favourites  = paps_favourites + 1,
+        .nfavourite  = MRP_ARRAY_SIZE(paps_favourites) - 2,
+    },
+    {
+        .name        = "Harry",
+        .gender      = MALE,
+        .age         = 14,
+        .height      = 165,
+        .weight      = 60.5,
+        .nationality = "UFO",
+        .hand        = LEFT,
+        .glasses     = false,
+        .favourites  = moms_favourites + 1,
+        .nfavourite  = MRP_ARRAY_SIZE(moms_favourites) - 2,
+    },
+    {
+        .name        = NULL,
+    },
+};
+
+
+family_t family = { &pap, &mom, &tom_dick_and_harry[0] };
+
+
+int main(int argc, char *argv[])
+{
+    MRP_NATIVE_TYPE(art_type, art_t,
+                    MRP_UINT32(art_t, type    , DEFAULT),
+                    MRP_STRING(art_t, artist  , DEFAULT),
+                    MRP_STRING(art_t, title   , DEFAULT),
+                    MRP_UINT16(art_t, year    , DEFAULT),
+                    MRP_STRING(art_t, location, DEFAULT),
+                    MRP_DOUBLE(art_t, price   , DEFAULT));
+    MRP_NATIVE_TYPE(person_type, person_t,
+                    MRP_STRING(person_t, name       , DEFAULT),
+                    MRP_UINT32(person_t, gender     , DEFAULT),
+                    MRP_UINT16(person_t, age        , DEFAULT),
+                    MRP_UINT16(person_t, height     , DEFAULT),
+                    MRP_FLOAT (person_t, weight     , DEFAULT),
+                    MRP_STRING(person_t, nationality, INLINED),
+                    MRP_UINT32(person_t, hand       , DEFAULT),
+                    MRP_BOOL  (person_t, glasses    , DEFAULT),
+                    MRP_ARRAY (person_t, favourites , DEFAULT, SIZED,
+                               art_t, nfavourite),
+                    MRP_UINT32(person_t, nfavourite , DEFAULT));
+    MRP_NATIVE_TYPE(family_type, family_t,
+                    MRP_STRUCT(family_t, father  , DEFAULT, person_t),
+                    MRP_STRUCT(family_t, mother  , DEFAULT, person_t),
+                    MRP_ARRAY (family_t, children, DEFAULT, GUARDED,
+                               person_t, name, .strp = NULL));
+    mrp_typemap_t map[4];
+
+    uint32_t  art_type_id, person_type_id, family_type_id;
+    void     *ebuf;
+    size_t    esize;
+    int       fd;
+    void     *dbuf;
+    family_t *decoded;
+    char      dump[16 * 1024];
+
+    MRP_UNUSED(argc);
+    MRP_UNUSED(argv);
+
+    mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_INFO));
+
+    art_type_id = mrp_register_native(&art_type);
+
+    if (art_type_id == MRP_INVALID_TYPE)
+        mrp_log_error("Failed to register art_t type.");
+    else
+        mrp_log_info("Type art_t sucessfully registered.");
+
+    person_type_id = mrp_register_native(&person_type);
+
+    if (person_type_id == MRP_INVALID_TYPE)
+        mrp_log_error("Failed to register person_t type.");
+    else
+        mrp_log_info("Type person_t sucessfully registered.");
+
+    family_type_id = mrp_register_native(&family_type);
+
+    if (family_type_id == MRP_INVALID_TYPE)
+        mrp_log_error("Failed to register family_t type.");
+    else
+        mrp_log_info("Type family_t sucessfully registered.");
+
+    ebuf = NULL;
+
+    map[0] = MRP_MAP_TYPE(1, art_type_id   );
+    map[1] = MRP_MAP_TYPE(2, person_type_id);
+    map[2] = MRP_MAP_TYPE(3, family_type_id);
+    map[3] = MRP_TYPEMAP_END;
+
+    if (mrp_encode_native(&family, family_type_id, 0, &ebuf, &esize, map) < 0) {
+        mrp_log_error("Failed to encode test data.");
+        exit(1);
+    }
+    else
+        mrp_log_info("Test data successfully encoded (%zd bytes).", esize);
+
+    if ((fd = open("type-test.encoded",
+                   O_CREAT | O_TRUNC | O_WRONLY, 0644)) >= 0) {
+        if (write(fd, ebuf, esize) != (ssize_t)esize)
+            mrp_log_error("Failed to write encoded data.");
+        close(fd);
+    }
+
+    if (mrp_decode_native(&ebuf, &esize, &dbuf, &family_type_id, map) < 0) {
+        mrp_log_error("Failed to decode test data.");
+        exit(1);
+    }
+    else
+        mrp_log_info("Test data sucessfully decoded.");
+
+    decoded = dbuf;
+
+    if (mrp_print_native(dump, sizeof(dump), decoded, family_type_id) >= 0)
+        mrp_log_info("dump of decoded data: %s", dump);
+    else
+        mrp_log_error("Failed to dump decoded data.");
+
+    mrp_free_native(dbuf, family_type_id);
+
+    return 0;
+}
diff --git a/src/common/tlv.c b/src/common/tlv.c
new file mode 100644 (file)
index 0000000..fd216a3
--- /dev/null
@@ -0,0 +1,662 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/tlv.h>
+
+#define TLV_MIN_PREALLOC 4096
+#define TLV_MIN_CHUNK      64
+
+int mrp_tlv_setup_write(mrp_tlv_t *tlv, size_t prealloc)
+{
+    if (prealloc < TLV_MIN_PREALLOC)
+        prealloc = TLV_MIN_PREALLOC;
+
+    if ((tlv->buf = mrp_allocz(prealloc)) == NULL)
+        return -1;
+
+    tlv->size  = prealloc;
+    tlv->p     = tlv->buf;
+    tlv->write = 1;
+
+    return 0;
+}
+
+
+static inline size_t tlv_space(mrp_tlv_t *tlv)
+{
+    if (tlv->size > 0 && tlv->write)
+        return tlv->size - (tlv->p - tlv->buf);
+    else
+        return 0;
+}
+
+
+static inline size_t tlv_data(mrp_tlv_t *tlv)
+{
+    if (!tlv->write)
+        return tlv->size - (tlv->p - tlv->buf);
+    else
+        return tlv->p - tlv->buf;
+}
+
+
+int mrp_tlv_ensure(mrp_tlv_t *tlv, size_t size)
+{
+    size_t left, diff;
+
+    if (!tlv->write)
+        return -1;
+
+    if ((left = tlv_space(tlv)) < size) {
+        diff = size - left;
+
+        if (diff < TLV_MIN_CHUNK)
+            diff = TLV_MIN_CHUNK;
+
+        tlv->p -= (ptrdiff_t)tlv->buf;
+
+        if (mrp_realloc(tlv->buf, tlv->size + diff) == NULL) {
+            tlv->p += (ptrdiff_t)tlv->buf;
+
+            return -1;
+        }
+
+        memset(tlv->buf + tlv->size, 0, diff);
+
+        tlv->size += diff;
+        tlv->p    += (ptrdiff_t)tlv->buf;
+    }
+
+    return 0;
+}
+
+
+void *mrp_tlv_reserve(mrp_tlv_t *tlv, size_t size, int align)
+{
+    void      *reserved;
+    ptrdiff_t  offs, pad;
+    size_t     len;
+
+    offs = tlv->p - tlv->buf;
+
+    if (align > 1)
+        pad = align - (offs & (align - 1));
+    else
+        pad = 0;
+
+    len = size + pad;
+
+    if (mrp_tlv_ensure(tlv, len) < 0)
+        return NULL;
+
+    if (pad)
+        memset(tlv->p, 0, pad);
+
+    reserved = tlv->p + pad;
+    tlv->p  += len;
+
+    return reserved;
+}
+
+
+int mrp_tlv_setup_read(mrp_tlv_t *tlv, void *buf, size_t size)
+{
+    tlv->buf   = tlv->p = buf;
+    tlv->size  = size;
+    tlv->write = 0;
+
+    return 0;
+}
+
+
+static void *tlv_consume(mrp_tlv_t *tlv, size_t size)
+{
+    char *p;
+
+    if (tlv_data(tlv) < size)
+        return NULL;
+
+    p = tlv->p;
+    tlv->p += size;
+
+    return p;
+}
+
+
+void mrp_tlv_trim(mrp_tlv_t *tlv)
+{
+    size_t left;
+
+    if (!tlv->write)
+        return;
+
+    if ((left = tlv_space(tlv)) == 0)
+        return;
+
+    tlv->p -= (ptrdiff_t)tlv->buf;
+
+    if (mrp_realloc(tlv->buf, tlv->size - left) != NULL) {
+        tlv->size -= left;
+        tlv->p    += (ptrdiff_t)tlv->buf;
+    }
+}
+
+
+size_t mrp_tlv_offset(mrp_tlv_t *tlv)
+{
+    return (size_t)(tlv->p - tlv->buf);
+}
+
+
+void mrp_tlv_cleanup(mrp_tlv_t *tlv)
+{
+    if (tlv->write)
+        mrp_free(tlv->buf);
+
+    tlv->buf  = tlv->p = NULL;
+    tlv->size = 0;
+}
+
+
+void mrp_tlv_steal(mrp_tlv_t *tlv, void **bufp, size_t *sizep)
+{
+    if (tlv->write) {
+        *bufp  = tlv->buf;
+        *sizep = tlv->p - tlv->buf;
+
+        tlv->buf  = tlv->p = NULL;
+        tlv->size = 0;
+    }
+    else {
+        *bufp  = NULL;
+        *sizep = 0;
+    }
+}
+
+
+static inline int push_tag(mrp_tlv_t *tlv, uint32_t tag)
+{
+    uint32_t *tagp;
+
+    if (tag) {
+        if ((tagp = mrp_tlv_reserve(tlv, sizeof(*tagp), 1)) == NULL)
+            return -1;
+        else
+            *tagp = htobe32(tag);
+    }
+
+    return 0;
+}
+
+
+int mrp_tlv_push_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t v)
+{
+    int8_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = v;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t v)
+{
+    uint8_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = v;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t v)
+{
+    int16_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = htobe16(v);
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t v)
+{
+    uint16_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = htobe16(v);
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t v)
+{
+    int32_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = htobe32(v);
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t v)
+{
+    uint32_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = htobe32(v);
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t v)
+{
+    int64_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = htobe64(v);
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t v)
+{
+    uint64_t *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = htobe64(v);
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_float(mrp_tlv_t *tlv, uint32_t tag, float v)
+{
+    float *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = v;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_double(mrp_tlv_t *tlv, uint32_t tag, double v)
+{
+    double *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = v;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_bool(mrp_tlv_t *tlv, uint32_t tag, bool v)
+{
+    bool *p;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+        *p = v;
+
+        return 0;
+    }
+
+    return -1;
+}
+
+
+int mrp_tlv_push_string(mrp_tlv_t *tlv, uint32_t tag, const char *str)
+{
+    uint32_t *sizep;
+    char     *strp;
+    size_t    len = str ? strlen(str) + 1 : 0;
+
+    if (push_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((sizep = mrp_tlv_reserve(tlv, sizeof(*sizep), 1)) == NULL)
+        return -1;
+
+    *sizep = htobe32((uint32_t)len);
+
+    if (len > 0) {
+        if ((strp = mrp_tlv_reserve(tlv, len, 1)) == NULL)
+            return -1;
+
+        strcpy(strp, str);
+    }
+
+    return 0;
+}
+
+
+int pull_tag(mrp_tlv_t *tlv, uint32_t tag)
+{
+    uint32_t *tagp;
+
+    if (tag) {
+        if ((tagp = tlv_consume(tlv, sizeof(*tagp))) == NULL)
+            return -1;
+
+        if (be32toh(*tagp) != tag)
+            return -1;
+    }
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t *v)
+{
+    int8_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = *p;
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t *v)
+{
+    uint8_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = *p;
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t *v)
+{
+    int16_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = be16toh(*p);
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t *v)
+{
+    uint16_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = be16toh(*p);
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t *v)
+{
+    int32_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = be32toh(*p);
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t *v)
+{
+    uint32_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = be32toh(*p);
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t *v)
+{
+    int64_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = be64toh(*p);
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t *v)
+{
+    uint64_t *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = be64toh(*p);
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_float(mrp_tlv_t *tlv, uint32_t tag, float *v)
+{
+    float *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = *p;
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_double(mrp_tlv_t *tlv, uint32_t tag, double *v)
+{
+    double *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = *p;
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_bool(mrp_tlv_t *tlv, uint32_t tag, bool *v)
+{
+    bool *p;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+        return -1;
+
+    *v = *p;
+
+    return 0;
+}
+
+
+int mrp_tlv_pull_string(mrp_tlv_t *tlv, uint32_t tag, char **v, size_t max,
+                        void *(alloc)(size_t, void *), void *alloc_data)
+{
+    uint32_t *sizep, size;
+    char     *str;
+
+    if (pull_tag(tlv, tag) < 0)
+        return -1;
+
+    if ((sizep = tlv_consume(tlv, sizeof(*sizep))) == NULL)
+        return -1;
+
+    size = be32toh(*sizep);
+
+    if (max != (size_t)-1 && max < size) {
+        errno = EOVERFLOW;
+        return -1;
+    }
+
+    if (size > 0) {
+        if ((str = tlv_consume(tlv, size)) == NULL)
+            return -1;
+
+        if (*v == NULL)
+            if ((*v = alloc(size, alloc_data)) == NULL)
+                return -1;
+
+        strncpy(*v, str, size - 1);
+        (*v)[size - 1] = '\0';
+    }
+    else
+        *v = NULL;
+
+    return 0;
+}
diff --git a/src/common/tlv.h b/src/common/tlv.h
new file mode 100644 (file)
index 0000000..b746546
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MRP_COMMON_TLV_H__
+#define __MRP_COMMON_TLV_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_TLV_UNTAGGED 0
+
+/**
+ * a tagged-value-list encoding/decoding buffer
+ */
+
+typedef struct {
+    void   *buf;                         /* actual data buffer */
+    size_t  size;                        /* allocated buffer size */
+    void   *p;                           /* encoding/decoding pointer */
+    int     write : 1;                   /* whether set up for writing */
+} mrp_tlv_t;
+
+/** Set up the given TLV buffer for encoding. */
+int mrp_tlv_setup_write(mrp_tlv_t *tlv, size_t prealloc);
+
+/** Set up the given TLV buffer for decoding. */
+int mrp_tlv_setup_read(mrp_tlv_t *tlv, void *buf, size_t size);
+
+/** Clean up the given TLV buffer. */
+void mrp_tlv_cleanup(mrp_tlv_t *tlv);
+
+/** Ensure the given amount of space is available in the TLV buffer. */
+int mrp_tlv_ensure(mrp_tlv_t *tlv, size_t size);
+
+/** Reserve the given amount of buffer space from the TLV buffer. */
+void *mrp_tlv_reserve(mrp_tlv_t *tlv, size_t size, int align);
+
+/** Take ownership of the data buffer from the TLV buffer. */
+void mrp_tlv_steal(mrp_tlv_t *tlv, void **bufp, size_t *sizep);
+
+/** Trim the data buffer of the TLV buffer to current amount of data. */
+void mrp_tlv_trim(mrp_tlv_t *tlv);
+
+/** Get the current read/write offset from the TLV buffer. */
+size_t mrp_tlv_offset(mrp_tlv_t *tlv);
+
+/** Add an int8_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t v);
+
+/** Add an uint8_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t v);
+
+/** Add an int16_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t v);
+
+/** Add an uint16_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t v);
+
+/** Add an int32_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t v);
+
+/** Add an uint32_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t v);
+
+/** Add an int64_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t v);
+
+/** Add an uint64_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t v);
+
+/** Add an float with an optional tag to the TLV buffer. */
+int mrp_tlv_push_float(mrp_tlv_t *tlv, uint32_t tag, float v);
+
+/** Add an double with an optional tag to the TLV buffer. */
+int mrp_tlv_push_double(mrp_tlv_t *tlv, uint32_t tag, double v);
+
+/** Add a boolean with an optional tag to the TLV buffer. */
+int mrp_tlv_push_bool(mrp_tlv_t *tlv, uint32_t tag, bool v);
+
+/** Add a string with an optional tag to the TLV buffer. */
+int mrp_tlv_push_string(mrp_tlv_t *tlv, uint32_t tag, const char *str);
+
+
+int mrp_tlv_pull_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t *v);
+int mrp_tlv_pull_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t *v);
+int mrp_tlv_pull_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t *v);
+int mrp_tlv_pull_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t *v);
+int mrp_tlv_pull_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t *v);
+int mrp_tlv_pull_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t *v);
+int mrp_tlv_pull_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t *v);
+int mrp_tlv_pull_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t *v);
+int mrp_tlv_pull_float(mrp_tlv_t *tlv, uint32_t tag, float *v);
+int mrp_tlv_pull_double(mrp_tlv_t *tlv, uint32_t tag, double *v);
+int mrp_tlv_pull_bool(mrp_tlv_t *tlv, uint32_t tag, bool *v);
+int mrp_tlv_pull_string(mrp_tlv_t *tlv, uint32_t tag, char **v, size_t max,
+                        void *(alloc)(size_t, void *), void *alloc_data);
+
+MRP_CDECL_END
+
+#endif /* __MRP_COMMON_TLV_H__ */