This is an automated email from the ASF dual-hosted git repository.

sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git

commit 5a318392aacad77bf43906946db403538adcaa62
Author: Szymon Janc <[email protected]>
AuthorDate: Wed Oct 7 10:50:35 2026 +0200

    encoding/cborattr: Import cborattr from mcumgr
    
    encoding/cborattr is now a regular package with cborattr code and unit
    tests imported unmodified from apache/mynewt-mcumgr at commit 7bdd41ffb,
    instead of being a link to @apache-mynewt-mcumgr/cborattr. Only package
    names and dependencies in pkg.yml files are updated, Zephyr build file
    (CMakeLists.txt) is not imported.
    
    Assisted-by: Claude:claude-opus-5-5
---
 encoding/cborattr/include/cborattr/cborattr.h      | 227 +++++++
 encoding/cborattr/pkg.yml                          |  13 +-
 encoding/cborattr/src/cborattr.c                   | 753 +++++++++++++++++++++
 encoding/cborattr/{pkg.yml => syscfg.yml}          |  15 +-
 encoding/cborattr/{ => test}/pkg.yml               |  20 +-
 encoding/cborattr/test/src/test_cborattr.c         |  52 ++
 encoding/cborattr/test/src/test_cborattr.h         |  62 ++
 encoding/cborattr/test/src/test_cborattr_utils.c   |  50 ++
 .../cborattr/test/src/testcases/cborattr_decode1.c |  83 +++
 .../src/testcases/cborattr_decode_bool_array.c     | 101 +++
 .../test/src/testcases/cborattr_decode_int_array.c | 143 ++++
 .../test/src/testcases/cborattr_decode_nesting.c   |  99 +++
 .../test/src/testcases/cborattr_decode_obj_array.c | 109 +++
 .../test/src/testcases/cborattr_decode_object.c    | 198 ++++++
 .../src/testcases/cborattr_decode_object_array.c   | 126 ++++
 .../test/src/testcases/cborattr_decode_partial.c   |  47 ++
 .../test/src/testcases/cborattr_decode_simple.c    | 123 ++++
 .../src/testcases/cborattr_decode_string_array.c   | 135 ++++
 .../cborattr_decode_string_array_bounds.c          | 102 +++
 .../src/testcases/cborattr_decode_string_bounds.c  | 119 ++++
 .../src/testcases/cborattr_decode_substring_key.c  | 111 +++
 .../src/testcases/cborattr_decode_unnamed_array.c  |  99 +++
 .../test/src/testcases/cborattr_encode_omit.c      | 140 ++++
 .../test/src/testcases/cborattr_encode_simple.c    | 143 ++++
 24 files changed, 3058 insertions(+), 12 deletions(-)

diff --git a/encoding/cborattr/include/cborattr/cborattr.h 
b/encoding/cborattr/include/cborattr/cborattr.h
new file mode 100644
index 000000000..d025e6404
--- /dev/null
+++ b/encoding/cborattr/include/cborattr/cborattr.h
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#ifndef CBORATTR_H
+#define CBORATTR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include "tinycbor/cbor.h"
+
+#ifdef MYNEWT
+#include <os/os_mbuf.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This library wraps the tinycbor decoder with a attribute based decoder
+ * suitable for decoding a binary version of json.  Specifically, the
+ * contents of the cbor contains pairs of attributes.  where the attribute
+ * is a key/value pair.  keys are always text strings, but values can be
+ * many different things (enumerated below) */
+
+typedef enum CborAttrType {
+    CborAttrIntegerType = 1,
+    CborAttrUnsignedIntegerType,
+    CborAttrByteStringType,
+    CborAttrTextStringType,
+    CborAttrBooleanType,
+    CborAttrHalfFloatType,
+    CborAttrFloatType,
+    CborAttrDoubleType,
+    CborAttrArrayType,
+    CborAttrObjectType,
+    CborAttrStructObjectType,
+    CborAttrNullType,
+} CborAttrType;
+
+struct cbor_attr_t;
+
+struct cbor_enum_t {
+    char *name;
+    long long int value;
+};
+
+struct cbor_array_t {
+    CborAttrType element_type;
+    union {
+        struct {
+            const struct cbor_attr_t *subtype;
+            char *base;
+            size_t stride;
+        } objects;
+        struct {
+            char **ptrs;
+            char *store;
+            int storelen;
+        } strings;
+        struct {
+            long long int *store;
+        } integers;
+        struct {
+            long long unsigned int *store;
+        } uintegers;
+        struct {
+            double *store;
+        } reals;
+        struct{
+            uint16_t *store;
+        } halffloats;
+        struct {
+            bool *store;
+        } booleans;
+    } arr;
+    int *count;
+    int maxlen;
+};
+
+struct cbor_attr_t {
+    char *attribute;
+    CborAttrType type;
+    union {
+        long long int *integer;
+        long long unsigned int *uinteger;
+        uint16_t *halffloat;
+        double *real;
+        float *fval;
+        char *string;
+        bool *boolean;
+        struct byte_string {
+            uint8_t *data;
+            size_t *len;
+        } bytestring;
+        struct cbor_array_t array;
+        size_t offset;
+        struct cbor_attr_t *obj;
+    } addr;
+    union {
+        long long int integer;
+        double real;
+        bool boolean;
+        float fval;
+        uint16_t halffloat;
+    } dflt;
+    size_t len;
+    bool nodefault;
+};
+
+#ifndef __ZEPHYR__
+/** An array value to be encoded as CBOR. */
+struct cbor_out_arr_val_t {
+    struct cbor_out_val_t *elems;
+    size_t len;
+};
+
+/** A single value to be encoded as CBOR. */
+struct cbor_out_val_t {
+    /** The type of data. */
+    CborAttrType type;
+
+    /** The data value. */
+    union {
+        long long int integer;
+        long long unsigned int uinteger;
+        double real;
+        float fval;
+        uint16_t halffloat;
+        const char *string;
+        bool boolean;
+        struct {
+            const uint8_t *data;
+            size_t len;
+        } bytestring;
+        struct cbor_out_arr_val_t array;
+        struct cbor_out_attr_t *obj; /* Terminated with a type=0 entry. */
+    };
+};
+
+/** An object key-value pair to be encoded as CBOR. */
+struct cbor_out_attr_t {
+    const char *attribute;      /** The attribute name (key). */
+    struct cbor_out_val_t val;  /** The attribute value. */
+    bool omit;                  /** Attribute ignored if true. */
+};
+#endif
+
+/*
+ * Use the following macros to declare template initializers for
+ * CborAttrStructObjectType arrays. Writing the equivalents out by hand is
+ * error-prone.
+ *
+ * CBOR_STRUCT_OBJECT takes a structure name s, and a fieldname f in s.
+ *
+ * CBOR_STRUCT_ARRAY takes the name of a structure array, a pointer to a an
+ * initializer defining the subobject type, and the address of an integer to
+ * store the length in.
+ */
+#define CBORATTR_STRUCT_OBJECT(s, f)        .addr.offset = offsetof(s, f)
+#define CBORATTR_STRUCT_ARRAY(a, e, n)                                  \
+    .addr.array.element_type = CborAttrStructObjectType,                \
+    .addr.array.arr.objects.subtype = e,                                \
+    .addr.array.arr.objects.base = (char*)a,                            \
+    .addr.array.arr.objects.stride = sizeof(a[0]),                      \
+    .addr.array.count = n,                                              \
+    .addr.array.maxlen = (int)(sizeof(a)/sizeof(a[0]))
+
+#define CBORATTR_ATTR_UNNAMED (char *)(-1)
+
+int cbor_read_object(struct CborValue *, const struct cbor_attr_t *);
+int cbor_read_array(struct CborValue *, const struct cbor_array_t *);
+
+int cbor_read_flat_attrs(const uint8_t *data, int len,
+                         const struct cbor_attr_t *attrs);
+#ifdef MYNEWT
+int cbor_read_mbuf_attrs(struct os_mbuf *m, uint16_t off, uint16_t len,
+                         const struct cbor_attr_t *attrs);
+
+/**
+ * @brief Encodes a CBOR representation of the specified key-value map.
+ *
+ * @param enc                   The CBOR encoder to write to.
+ * @param attrs                 The key-value map to encode.
+ *
+ * @return                      0 on success; SYS_E[...] error on failure.
+ */
+int cbor_write_object(struct CborEncoder *enc,
+                      const struct cbor_out_attr_t *attrs);
+
+/**
+ * @brief Encodes a CBOR representation of the specified key-value map into an
+ * msys mbuf chain.
+ *
+ * @param attrs                 The key-value map to encode.
+ * @param out_om                On success, points to the populate mbuf chain.
+ *
+ * @return                      0 on success; SYS_E[...] error on failure.
+ */
+int cbor_write_object_msys(const struct cbor_out_attr_t *attrs,
+                           struct os_mbuf **out_om);
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CBORATTR_H */
+
diff --git a/encoding/cborattr/pkg.yml b/encoding/cborattr/pkg.yml
index 99207ebfc..6f19678b5 100644
--- a/encoding/cborattr/pkg.yml
+++ b/encoding/cborattr/pkg.yml
@@ -6,7 +6,7 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-#
+# 
 #  http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing,
@@ -18,5 +18,12 @@
 #
 
 pkg.name: encoding/cborattr
-pkg.type: transient
-pkg.link: "@apache-mynewt-mcumgr/cborattr"
+pkg.description: CBOR encoding/decoding library 
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - "@apache-mynewt-core/encoding/tinycbor"
+
+pkg.cflags.FLOAT_USER: -DFLOAT_SUPPORT
diff --git a/encoding/cborattr/src/cborattr.c b/encoding/cborattr/src/cborattr.c
new file mode 100644
index 000000000..797d96a6f
--- /dev/null
+++ b/encoding/cborattr/src/cborattr.c
@@ -0,0 +1,753 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "cborattr/cborattr.h"
+#include "tinycbor/cbor.h"
+#include "tinycbor/cbor_buf_reader.h"
+
+#ifdef __ZEPHYR__
+#include <zephyr/kernel.h>
+#ifdef CONFIG_MGMT_CBORATTR_MAX_SIZE
+#define CBORATTR_MAX_SIZE CONFIG_MGMT_CBORATTR_MAX_SIZE
+#else
+#define CBORATTR_MAX_SIZE 512
+#endif
+#endif
+
+#ifdef MYNEWT
+#include "syscfg/syscfg.h"
+#include "tinycbor/cbor_mbuf_reader.h"
+#include "tinycbor/cbor_mbuf_writer.h"
+#include "os/os_mbuf.h"
+#define CBORATTR_MAX_SIZE MYNEWT_VAL(CBORATTR_MAX_SIZE)
+#define CBORATTR_MAX_NESTING MYNEWT_VAL(CBORATTR_MAX_NESTING)
+#endif
+
+#ifndef CBORATTR_MAX_NESTING
+#define CBORATTR_MAX_NESTING 16
+#endif
+
+/* this maps a CborType to a matching CborAtter Type. The mapping is not
+ * one-to-one because of signedness of integers
+ * and therefore we need a function to do this trickery */
+static int
+valid_attr_type(CborType ct, CborAttrType at)
+{
+    switch (at) {
+    case CborAttrIntegerType:
+    case CborAttrUnsignedIntegerType:
+        if (ct == CborIntegerType) {
+            return 1;
+        }
+        break;
+    case CborAttrByteStringType:
+        if (ct == CborByteStringType) {
+            return 1;
+        }
+        break;
+    case CborAttrTextStringType:
+        if (ct == CborTextStringType) {
+            return 1;
+        }
+        break;
+    case CborAttrBooleanType:
+        if (ct == CborBooleanType) {
+            return 1;
+        }
+       break;
+#if FLOAT_SUPPORT
+    case CborAttrHalfFloatType:
+        if (ct == CborHalfFloatType) {
+            return 1;
+        }
+        break;
+    case CborAttrFloatType:
+        if (ct == CborFloatType) {
+            return 1;
+        }
+        break;
+    case CborAttrDoubleType:
+        if (ct == CborDoubleType) {
+            return 1;
+        }
+        break;
+#endif
+    case CborAttrArrayType:
+        if (ct == CborArrayType) {
+            return 1;
+        }
+        break;
+    case CborAttrObjectType:
+        if (ct == CborMapType) {
+            return 1;
+        }
+        break;
+    case CborAttrNullType:
+        if (ct == CborNullType) {
+            return 1;
+        }
+        break;
+    default:
+        break;
+    }
+    return 0;
+}
+
+/*
+ * Advances past the value at *it. Unlike cbor_value_advance(), containers are
+ * walked iteratively with a fixed size stack, and values nested deeper than
+ * CBORATTR_MAX_NESTING are rejected with CborErrorNestingTooDeep.
+ */
+static CborError
+cborattr_advance(CborValue *it)
+{
+    CborValue stack[CBORATTR_MAX_NESTING];
+    CborValue *cur;
+    CborValue *parent;
+    CborError err;
+    int depth;
+
+    if (!cbor_value_is_container(it)) {
+        return cbor_value_advance(it);
+    }
+
+    err = cbor_value_enter_container(it, &stack[0]);
+    if (err) {
+        return err;
+    }
+    depth = 1;
+
+    while (depth > 0) {
+        cur = &stack[depth - 1];
+        if (cbor_value_at_end(cur)) {
+            parent = (depth == 1) ? it : &stack[depth - 2];
+            err = cbor_value_leave_container(parent, cur);
+            if (err) {
+                return err;
+            }
+            depth--;
+        } else if (cbor_value_is_container(cur)) {
+            if (depth >= CBORATTR_MAX_NESTING) {
+                return CborErrorNestingTooDeep;
+            }
+            err = cbor_value_enter_container(cur, &stack[depth]);
+            if (err) {
+                return err;
+            }
+            depth++;
+        } else {
+            err = cbor_value_advance(cur);
+            if (err) {
+                return err;
+            }
+        }
+    }
+
+    return CborNoError;
+}
+
+/* this function find the pointer to the memory location to
+  * write or read and attribute from the cbor_attr_r structure */
+static char *
+cbor_target_address(const struct cbor_attr_t *cursor,
+                    const struct cbor_array_t *parent, int offset)
+{
+    char *targetaddr = NULL;
+
+    if (parent == NULL || parent->element_type != CborAttrStructObjectType) {
+        /* ordinary case - use the address in the cursor structure */
+        switch (cursor->type) {
+        case CborAttrNullType:
+            targetaddr = NULL;
+            break;
+        case CborAttrIntegerType:
+            targetaddr = (char *)&cursor->addr.integer[offset];
+            break;
+        case CborAttrUnsignedIntegerType:
+            targetaddr = (char *)&cursor->addr.uinteger[offset];
+            break;
+#if FLOAT_SUPPORT
+        case CborAttrHalfFloatType:
+            targetaddr = (char *)&cursor->addr.halffloat[offset];
+            break;
+        case CborAttrFloatType:
+            targetaddr = (char *)&cursor->addr.fval[offset];
+            break;
+        case CborAttrDoubleType:
+            targetaddr = (char *)&cursor->addr.real[offset];
+            break;
+#endif
+        case CborAttrByteStringType:
+            targetaddr = (char *) cursor->addr.bytestring.data;
+            break;
+        case CborAttrTextStringType:
+            targetaddr = cursor->addr.string;
+            break;
+        case CborAttrBooleanType:
+            targetaddr = (char *)&cursor->addr.boolean[offset];
+            break;
+        default:
+            targetaddr = NULL;
+            break;
+        }
+    } else {
+        /* tricky case - hacking a member in an array of structures */
+        targetaddr =
+            parent->arr.objects.base + (offset * parent->arr.objects.stride) +
+            cursor->addr.offset;
+    }
+    return targetaddr;
+}
+
+static int
+cbor_internal_read_object(CborValue *root_value,
+                          const struct cbor_attr_t *attrs,
+                          const struct cbor_array_t *parent,
+                          int offset)
+{
+    const struct cbor_attr_t *cursor, *best_match;
+    char attrbuf[CBORATTR_MAX_SIZE + 1];
+    void *lptr;
+    CborValue cur_value;
+    CborError err = 0;
+    size_t len = 0;
+    CborType type = CborInvalidType;
+
+    /* stuff fields with defaults in case they're omitted in the JSON input */
+    for (cursor = attrs; cursor->attribute != NULL; cursor++) {
+        if (!cursor->nodefault) {
+            lptr = cbor_target_address(cursor, parent, offset);
+            if (lptr != NULL) {
+                switch (cursor->type) {
+                case CborAttrIntegerType:
+                    memcpy(lptr, &cursor->dflt.integer, sizeof(long long int));
+                    break;
+                case CborAttrUnsignedIntegerType:
+                    memcpy(lptr, &cursor->dflt.integer,
+                           sizeof(long long unsigned int));
+                    break;
+                case CborAttrBooleanType:
+                    memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
+                    break;
+                case CborAttrTextStringType:
+                    if (cursor->len > 0) {
+                        *(char *)lptr = '\0';
+                    }
+                    break;
+                case CborAttrByteStringType:
+                    if (cursor->addr.bytestring.len) {
+                        *cursor->addr.bytestring.len = 0;
+                    }
+                    break;
+#if FLOAT_SUPPORT
+                case CborAttrHalfFloatType:
+                    memcpy(lptr, &cursor->dflt.halffloat, sizeof(uint16_t));
+                    break;
+                case CborAttrFloatType:
+                    memcpy(lptr, &cursor->dflt.fval, sizeof(float));
+                    break;
+                case CborAttrDoubleType:
+                    memcpy(lptr, &cursor->dflt.real, sizeof(double));
+                    break;
+#endif
+                default:
+                    break;
+                }
+            }
+        }
+    }
+
+    if (cbor_value_is_map(root_value)) {
+        err |= cbor_value_enter_container(root_value, &cur_value);
+    } else {
+        err |= CborErrorIllegalType;
+        return err;
+    }
+
+    /* contains key value pairs */
+    while (cbor_value_is_valid(&cur_value) && !err) {
+        /* get the attribute */
+        if (cbor_value_is_text_string(&cur_value)) {
+            err |= cbor_value_calculate_string_length(&cur_value, &len);
+            if (err) {
+                break;
+            }
+            if (len > CBORATTR_MAX_SIZE) {
+                err |= CborErrorDataTooLarge;
+                break;
+            }
+            err |= cbor_value_copy_text_string(&cur_value, attrbuf, &len,
+                                               NULL);
+
+            /* at least get the type of the next value so we can match the
+             * attribute name and type for a perfect match */
+            err |= cborattr_advance(&cur_value);
+            if (cbor_value_is_valid(&cur_value)) {
+                type = cbor_value_get_type(&cur_value);
+            } else {
+                err |= CborErrorIllegalType;
+                break;
+            }
+        } else {
+            attrbuf[0] = '\0';
+            type = cbor_value_get_type(&cur_value);
+        }
+
+        /* find this attribute in our list */
+        best_match = NULL;
+        for (cursor = attrs; cursor->attribute != NULL; cursor++) {
+            if (valid_attr_type(type, cursor->type)) {
+                if (cursor->attribute == CBORATTR_ATTR_UNNAMED &&
+                    attrbuf[0] == '\0') {
+                    best_match = cursor;
+                } else if (strlen(cursor->attribute) == len &&
+                    !memcmp(cursor->attribute, attrbuf, len)) {
+                    break;
+                }
+            }
+        }
+        if (!cursor->attribute && best_match) {
+            cursor = best_match;
+        }
+        /* we found a match */
+        if (cursor->attribute != NULL) {
+            lptr = cbor_target_address(cursor, parent, offset);
+            switch (cursor->type) {
+            case CborAttrNullType:
+                /* nothing to do */
+                break;
+            case CborAttrBooleanType:
+                err |= cbor_value_get_boolean(&cur_value, lptr);
+                break;
+            case CborAttrIntegerType:
+                err |= cbor_value_get_int64(&cur_value, lptr);
+                break;
+            case CborAttrUnsignedIntegerType:
+                err |= cbor_value_get_uint64(&cur_value, lptr);
+                break;
+#if FLOAT_SUPPORT
+            case CborAttrHalfFloatType:
+                err |= cbor_value_get_half_float(&cur_value, lptr);
+                break;
+            case CborAttrFloatType:
+                err |= cbor_value_get_float(&cur_value, lptr);
+                break;
+            case CborAttrDoubleType:
+                err |= cbor_value_get_double(&cur_value, lptr);
+                break;
+#endif
+            case CborAttrByteStringType: {
+                size_t len = cursor->len;
+                CborError copy_err;
+
+                copy_err = cbor_value_copy_byte_string(&cur_value, lptr,
+                                                       &len, NULL);
+                err |= copy_err;
+                /* On error len is set to string length, not copied length */
+                *cursor->addr.bytestring.len = copy_err ? 0 : len;
+                break;
+            }
+            case CborAttrTextStringType: {
+                size_t len;
+                CborError copy_err;
+
+                if (cursor->len < 1) {
+                    err |= CborErrorOutOfMemory;
+                    break;
+                }
+                /* Reserve space for terminating NUL */
+                len = cursor->len - 1;
+                copy_err = cbor_value_copy_text_string(&cur_value, lptr,
+                                                       &len, NULL);
+                err |= copy_err;
+                ((char *)lptr)[copy_err ? 0 : len] = '\0';
+                break;
+            }
+            case CborAttrArrayType:
+                err |= cbor_read_array(&cur_value, &cursor->addr.array);
+                continue;
+            case CborAttrObjectType:
+                err |= cbor_internal_read_object(&cur_value, cursor->addr.obj,
+                                                 NULL, 0);
+                continue;
+            default:
+                err |= CborErrorIllegalType;
+            }
+        }
+        err |= cborattr_advance(&cur_value);
+    }
+    if (!err) {
+        /* that should be it for this container */
+        err |= cbor_value_leave_container(root_value, &cur_value);
+    }
+    return err;
+}
+
+int
+cbor_read_array(struct CborValue *value, const struct cbor_array_t *arr)
+{
+    CborError err = 0;
+    CborError adv_err = CborNoError;
+    struct CborValue elem;
+    int off, arrcount;
+    size_t len;
+    void *lptr;
+    char *tp;
+
+    err = cbor_value_enter_container(value, &elem);
+    if (err) {
+        return err;
+    }
+    arrcount = 0;
+    tp = arr->arr.strings.store;
+    for (off = 0; off < arr->maxlen; off++) {
+        switch (arr->element_type) {
+        case CborAttrBooleanType:
+            lptr = &arr->arr.booleans.store[off];
+            err |= cbor_value_get_boolean(&elem, lptr);
+            break;
+        case CborAttrIntegerType:
+            lptr = &arr->arr.integers.store[off];
+            err |= cbor_value_get_int64(&elem, lptr);
+            break;
+        case CborAttrUnsignedIntegerType:
+            lptr = &arr->arr.uintegers.store[off];
+            err |= cbor_value_get_uint64(&elem, lptr);
+            break;
+#if FLOAT_SUPPORT
+        case CborAttrHalfFloatType:
+            lptr = &arr->arr.halffloats.store[off];
+            err |= cbor_value_get_half_float(&elem, lptr);
+            break;
+        case CborAttrFloatType:
+        case CborAttrDoubleType:
+            lptr = &arr->arr.reals.store[off];
+            err |= cbor_value_get_double(&elem, lptr);
+            break;
+#endif
+        case CborAttrTextStringType:
+            len = arr->arr.strings.storelen - (tp - arr->arr.strings.store);
+            if (len < 1) {
+                err |= CborErrorOutOfMemory;
+                break;
+            }
+            /* Reserve space for terminating NUL */
+            len--;
+            err |= cbor_value_copy_text_string(&elem, tp, &len, NULL);
+            if (err) {
+                break;
+            }
+            tp[len] = '\0';
+            arr->arr.strings.ptrs[off] = tp;
+            tp += len + 1;
+            break;
+        case CborAttrStructObjectType:
+            err |= cbor_internal_read_object(&elem, arr->arr.objects.subtype,
+                                             arr, off);
+            break;
+        default:
+            err |= CborErrorIllegalType;
+            break;
+        }
+        if (err) {
+            break;
+        }
+        arrcount++;
+        if (arr->element_type != CborAttrStructObjectType) {
+            adv_err = cborattr_advance(&elem);
+            err |= adv_err;
+            if (adv_err) {
+                /* elem was not advanced, no further progress is possible */
+                break;
+            }
+        }
+        if (!cbor_value_is_valid(&elem)) {
+            break;
+        }
+    }
+    if (arr->count) {
+        *arr->count = arrcount;
+    }
+    while (!adv_err && !cbor_value_at_end(&elem)) {
+        err |= CborErrorDataTooLarge;
+        adv_err = cborattr_advance(&elem);
+        err |= adv_err;
+    }
+    if (adv_err) {
+        /* elem is not at the end of container so it cannot be left */
+        return err;
+    }
+    err |= cbor_value_leave_container(value, &elem);
+    return err;
+}
+
+int
+cbor_read_object(struct CborValue *value, const struct cbor_attr_t *attrs)
+{
+    int st;
+
+    st = cbor_internal_read_object(value, attrs, NULL, 0);
+    return st;
+}
+
+/*
+ * Read in cbor key/values from flat buffer pointed by data, and fill them
+ * into attrs.
+ *
+ * @param data         Pointer to beginning of cbor encoded data
+ * @param len          Number of bytes in the buffer
+ * @param attrs                Array of cbor objects to look for.
+ *
+ * @return             0 on success; non-zero on failure.
+ */
+int
+cbor_read_flat_attrs(const uint8_t *data, int len,
+                     const struct cbor_attr_t *attrs)
+{
+    struct cbor_buf_reader reader;
+    struct CborParser parser;
+    struct CborValue value;
+    CborError err;
+
+    cbor_buf_reader_init(&reader, data, len);
+    err = cbor_parser_init(&reader.r, 0, &parser, &value);
+    if (err != CborNoError) {
+        return -1;
+    }
+    return cbor_read_object(&value, attrs);
+}
+
+#ifdef MYNEWT
+static int cbor_write_val(struct CborEncoder *enc,
+                          const struct cbor_out_val_t *val);
+
+/*
+ * Read in cbor key/values from os_mbuf pointed by m, and fill them
+ * into attrs.
+ *
+ * @param m            Pointer to os_mbuf containing cbor encoded data
+ * @param off          Offset into mbuf where cbor data begins
+ * @param len          Number of bytes to decode
+ * @param attrs                Array of cbor objects to look for.
+ *
+ * @return             0 on success; non-zero on failure.
+ */
+int
+cbor_read_mbuf_attrs(struct os_mbuf *m, uint16_t off, uint16_t len,
+                     const struct cbor_attr_t *attrs)
+{
+    struct cbor_mbuf_reader cmr;
+    struct CborParser parser;
+    struct CborValue value;
+    CborError err;
+
+    cbor_mbuf_reader_init(&cmr, m, off);
+    err = cbor_parser_init(&cmr.r, 0, &parser, &value);
+    if (err != CborNoError) {
+        return -1;
+    }
+    return cbor_read_object(&value, attrs);
+}
+
+static int
+cbor_write_arr_val(struct CborEncoder *enc,
+                   const struct cbor_out_arr_val_t *arr)
+{
+    struct CborEncoder arr_enc;
+    size_t i;
+    int rc;
+
+    rc = cbor_encoder_create_array(enc, &arr_enc, arr->len);
+    if (rc != 0) {
+        return SYS_ENOMEM;
+    }
+
+    for (i = 0; i < arr->len; i++) {
+        rc = cbor_write_val(&arr_enc, &arr->elems[i]);
+        if (rc != 0) {
+            return SYS_ENOMEM;
+        }
+    }
+
+    rc = cbor_encoder_close_container(enc, &arr_enc);
+    if (rc != 0) {
+        return SYS_ENOMEM;
+    }
+
+    return 0;
+}
+
+static int
+cbor_write_val(struct CborEncoder *enc, const struct cbor_out_val_t *val)
+{
+    int len;
+    int rc;
+
+    switch (val->type) {
+    case CborAttrNullType:
+        rc = cbor_encode_null(enc);
+        break;
+
+    case CborAttrBooleanType:
+        rc = cbor_encode_boolean(enc, val->boolean);
+        break;
+
+    case CborAttrIntegerType:
+        rc = cbor_encode_int(enc, val->integer);
+        break;
+
+    case CborAttrUnsignedIntegerType:
+        rc = cbor_encode_uint(enc, val->uinteger);
+        break;
+
+#if FLOAT_SUPPORT
+    case CborAttrHalfFloatType:
+        rc = cbor_encode_half_float(enc, &val->halffloat);
+        break;
+
+    case CborAttrFloatType:
+        rc = cbor_encode_float(enc, val->fval);
+        break;
+
+    case CborAttrDoubleType:
+        rc = cbor_encode_double(enc, val->real);
+        break;
+#endif
+
+    case CborAttrByteStringType:
+        if (val->bytestring.data == NULL &&
+            val->bytestring.len != 0) {
+
+            return SYS_EINVAL;
+        }
+
+        rc = cbor_encode_byte_string(enc, val->bytestring.data,
+                                     val->bytestring.len);
+        break;
+
+    case CborAttrTextStringType:
+        if (val->string == NULL) {
+            len = 0;
+        } else {
+            len = strlen(val->string);
+        }
+        rc = cbor_encode_text_string(enc, val->string, len);
+        break;
+
+    case CborAttrObjectType:
+        rc = cbor_write_object(enc, val->obj);
+        break;
+
+    case CborAttrArrayType:
+        rc = cbor_write_arr_val(enc, &val->array);
+        break;
+
+    default:
+        return SYS_ENOTSUP;
+    }
+
+    if (rc != 0) {
+        return SYS_ENOMEM;
+    }
+
+    return 0;
+}
+
+static int
+cbor_write_attr(struct CborEncoder *enc, const struct cbor_out_attr_t *attr)
+{
+    int len;
+    int rc;
+
+    if (attr->omit) {
+        return 0;
+    }
+
+    if (!attr->attribute) {
+        rc = SYS_EINVAL;
+        return rc;
+    }
+
+    len = strlen(attr->attribute);
+    rc = cbor_encode_text_string(enc, attr->attribute, len);
+    if (rc != 0) {
+        return rc;
+    } 
+
+    rc = cbor_write_val(enc, &attr->val);
+    if (rc != 0) {
+        return rc;
+    } 
+
+    return 0;
+}
+
+int
+cbor_write_object(struct CborEncoder *enc, const struct cbor_out_attr_t *attrs)
+{
+    const struct cbor_out_attr_t *attr;
+    struct CborEncoder map;
+    int rc;
+
+    rc = cbor_encoder_create_map(enc, &map, CborIndefiniteLength);
+    if (rc != 0) {
+        return SYS_ENOMEM;
+    }
+
+    for (attr = attrs; attr->val.type != 0; attr++) {
+        rc = cbor_write_attr(&map, attr);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    rc = cbor_encoder_close_container(enc, &map);
+    if (rc != 0) {
+        return SYS_ENOMEM;
+    }
+
+    return 0;
+}
+
+int
+cbor_write_object_msys(const struct cbor_out_attr_t *attrs,
+                       struct os_mbuf **out_om)
+{
+    struct cbor_mbuf_writer writer;
+    struct CborEncoder encoder;
+    int rc;
+
+    *out_om = os_msys_get_pkthdr(0, 0);
+    if (*out_om == NULL) {
+        return SYS_ENOMEM;
+    }
+
+    cbor_mbuf_writer_init(&writer, *out_om);
+    cbor_encoder_init(&encoder, &writer.enc, 0);
+
+    rc = cbor_write_object(&encoder, attrs);
+    if (rc != 0) {
+        os_mbuf_free_chain(*out_om);
+        *out_om = NULL;
+        return rc;
+    }
+
+    return 0;
+}
+#endif
diff --git a/encoding/cborattr/pkg.yml b/encoding/cborattr/syscfg.yml
similarity index 68%
copy from encoding/cborattr/pkg.yml
copy to encoding/cborattr/syscfg.yml
index 99207ebfc..793ff7549 100644
--- a/encoding/cborattr/pkg.yml
+++ b/encoding/cborattr/syscfg.yml
@@ -1,4 +1,3 @@
-#
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -17,6 +16,14 @@
 # under the License.
 #
 
-pkg.name: encoding/cborattr
-pkg.type: transient
-pkg.link: "@apache-mynewt-mcumgr/cborattr"
+# Package: mgmt/imgmgr
+
+syscfg.defs:
+    CBORATTR_MAX_SIZE:
+        description: 'The maximum size of a CBOR attribute during decoding'
+        value: 512
+    CBORATTR_MAX_NESTING:
+        description: >
+            Maximum nesting depth of CBOR containers that can be skipped
+            while decoding. Values nested deeper are rejected.
+        value: 16
diff --git a/encoding/cborattr/pkg.yml b/encoding/cborattr/test/pkg.yml
similarity index 63%
copy from encoding/cborattr/pkg.yml
copy to encoding/cborattr/test/pkg.yml
index 99207ebfc..df0c50a8b 100644
--- a/encoding/cborattr/pkg.yml
+++ b/encoding/cborattr/test/pkg.yml
@@ -1,4 +1,3 @@
-#
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
 # distributed with this work for additional information
@@ -6,7 +5,7 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-#
+# 
 #  http://www.apache.org/licenses/LICENSE-2.0
 #
 # Unless required by applicable law or agreed to in writing,
@@ -16,7 +15,18 @@
 # specific language governing permissions and limitations
 # under the License.
 #
+pkg.name: encoding/cborattr/test
+pkg.type: unittest
+pkg.description: "CBOR attr unit tests."
+pkg.author: "Apache Mynewt <[email protected]>"
+pkg.homepage: "http://mynewt.apache.org/";
+pkg.keywords:
+
+pkg.deps:
+    - '@apache-mynewt-core/encoding/tinycbor'
+    - '@apache-mynewt-core/encoding/cborattr'
 
-pkg.name: encoding/cborattr
-pkg.type: transient
-pkg.link: "@apache-mynewt-mcumgr/cborattr"
+pkg.deps.SELFTEST:
+    - '@apache-mynewt-core/sys/console/stub'
+    - '@apache-mynewt-core/sys/log/full'
+    - '@apache-mynewt-core/test/testutil'
diff --git a/encoding/cborattr/test/src/test_cborattr.c 
b/encoding/cborattr/test/src/test_cborattr.c
new file mode 100644
index 000000000..e3406286e
--- /dev/null
+++ b/encoding/cborattr/test/src/test_cborattr.c
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "sysinit/sysinit.h"
+#include "syscfg/syscfg.h"
+#include "testutil/testutil.h"
+#include "test_cborattr.h"
+
+TEST_SUITE(test_cborattr_suite)
+{
+    test_cborattr_decode1();
+    test_cborattr_decode_partial();
+    test_cborattr_decode_simple();
+    test_cborattr_decode_object();
+    test_cborattr_decode_int_array();
+    test_cborattr_decode_bool_array();
+    test_cborattr_decode_string_array();
+    test_cborattr_decode_object_array();
+    test_cborattr_decode_unnamed_array();
+    test_cborattr_decode_substring_key();
+    test_cborattr_decode_nesting();
+    test_cborattr_decode_string_bounds();
+    test_cborattr_decode_string_array_bounds();
+}
+
+#if MYNEWT_VAL(SELFTEST)
+int
+main(int argc, char **argv)
+{
+    sysinit();
+
+    test_cborattr_suite();
+
+    return tu_any_failed;
+}
+#endif
diff --git a/encoding/cborattr/test/src/test_cborattr.h 
b/encoding/cborattr/test/src/test_cborattr.h
new file mode 100644
index 000000000..d423f25e0
--- /dev/null
+++ b/encoding/cborattr/test/src/test_cborattr.h
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef TEST_CBORATTR_H
+#define TEST_CBORATTR_H
+
+#include <assert.h>
+#include <string.h>
+#include "testutil/testutil.h"
+#include "test_cborattr.h"
+#include "tinycbor/cbor.h"
+#include "cborattr/cborattr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Returns test data.
+ */
+const uint8_t *test_str1(int *len);
+
+void cborattr_test_util_encode(const struct cbor_out_attr_t *attrs,
+                               const uint8_t *expected, int len);
+/*
+ * Testcases
+ */
+TEST_CASE_DECL(test_cborattr_decode1);
+TEST_CASE_DECL(test_cborattr_decode_partial);
+TEST_CASE_DECL(test_cborattr_decode_simple);
+TEST_CASE_DECL(test_cborattr_decode_object);
+TEST_CASE_DECL(test_cborattr_decode_int_array);
+TEST_CASE_DECL(test_cborattr_decode_bool_array);
+TEST_CASE_DECL(test_cborattr_decode_string_array);
+TEST_CASE_DECL(test_cborattr_decode_object_array);
+TEST_CASE_DECL(test_cborattr_decode_unnamed_array);
+TEST_CASE_DECL(test_cborattr_decode_substring_key);
+TEST_CASE_DECL(test_cborattr_decode_nesting);
+TEST_CASE_DECL(test_cborattr_decode_string_bounds);
+TEST_CASE_DECL(test_cborattr_decode_string_array_bounds);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TEST_CBORATTR_H */
+
diff --git a/encoding/cborattr/test/src/test_cborattr_utils.c 
b/encoding/cborattr/test/src/test_cborattr_utils.c
new file mode 100644
index 000000000..d520e069c
--- /dev/null
+++ b/encoding/cborattr/test/src/test_cborattr_utils.c
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "test_cborattr.h"
+/*
+ * {"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}
+ */
+static const uint8_t test_data1[] = {
+    0xa5, 0x61, 0x61, 0x61, 0x41, 0x61, 0x62, 0x61,
+    0x42, 0x61, 0x63, 0x61, 0x43, 0x61, 0x64, 0x61,
+    0x44, 0x61, 0x65, 0x61, 0x45
+};
+
+const uint8_t *
+test_str1(int *len)
+{
+    *len = sizeof(test_data1);
+    return (test_data1);
+}
+
+void
+cborattr_test_util_encode(const struct cbor_out_attr_t *attrs,
+                          const uint8_t *expected, int len)
+{
+    struct os_mbuf *om;
+    int rc;
+
+    rc = cbor_write_object_msys(attrs, &om);
+    TEST_ASSERT_FATAL(rc == 0);
+
+    TEST_ASSERT(os_mbuf_cmpf(om, 0, expected, len) == 0);
+
+    os_mbuf_free_chain(om);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode1.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode1.c
new file mode 100644
index 000000000..1b6cddf48
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode1.c
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Simple decoding.
+ */
+TEST_CASE(test_cborattr_decode1)
+{
+    const uint8_t *data;
+    int len;
+    int rc;
+    char test_str_a[4] = { '\0' };
+    char test_str_b[4] = { '\0' };
+    char test_str_c[4] = { '\0' };
+    char test_str_d[4] = { '\0' };
+    char test_str_e[4] = { '\0' };
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_a,
+            .len = sizeof(test_str_a),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = "b",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_b,
+            .len = sizeof(test_str_b),
+            .nodefault = true
+            },
+        [2] = {
+            .attribute = "c",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_c,
+            .len = sizeof(test_str_c),
+            .nodefault = true
+            },
+        [3] = {
+            .attribute = "d",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_d,
+            .len = sizeof(test_str_d),
+            .nodefault = true,
+            },
+        [4] = {
+            .attribute = "e",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_e,
+            .len = sizeof(test_str_e),
+            .nodefault = true,
+        },
+        [5] = {
+            .attribute = NULL
+        }
+    };
+
+    data = test_str1(&len);
+    rc = cbor_read_flat_attrs(data, len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(!strcmp(test_str_a, "A"));
+    TEST_ASSERT(!strcmp(test_str_b, "B"));
+    TEST_ASSERT(!strcmp(test_str_c, "C"));
+    TEST_ASSERT(!strcmp(test_str_d, "D"));
+    TEST_ASSERT(!strcmp(test_str_e, "E"));
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_bool_array.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_bool_array.c
new file mode 100644
index 000000000..71520c428
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_bool_array.c
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_bool_array(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * a: [true,true,false]
+     */
+    cbor_encode_text_stringz(&data, "a");
+
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+    cbor_encode_boolean(&array, true);
+    cbor_encode_boolean(&array, true);
+    cbor_encode_boolean(&array, false);
+    cbor_encoder_close_container(&data, &array);
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * array of booleans
+ */
+TEST_CASE(test_cborattr_decode_bool_array)
+{
+    int rc;
+    bool arr_data[5];
+    int arr_cnt = 0;
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrBooleanType,
+            .addr.array.arr.booleans.store = arr_data,
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = sizeof(arr_data) / sizeof(arr_data[0]),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_bool_array();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 3);
+    TEST_ASSERT(arr_data[0] == true);
+    TEST_ASSERT(arr_data[1] == true);
+    TEST_ASSERT(arr_data[2] == false);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_int_array.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_int_array.c
new file mode 100644
index 000000000..fde602cc7
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_int_array.c
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_int_array(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * a: [1,2,33,15,-4]
+     */
+    cbor_encode_text_stringz(&data, "a");
+
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+    cbor_encode_int(&array, 1);
+    cbor_encode_int(&array, 2);
+    cbor_encode_int(&array, 33);
+    cbor_encode_int(&array, 15);
+    cbor_encode_int(&array, -4);
+    cbor_encoder_close_container(&data, &array);
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * integer array
+ */
+TEST_CASE(test_cborattr_decode_int_array)
+{
+    int rc;
+    long long int arr_data[5];
+    long long int b_int;
+    int arr_cnt = 0;
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrIntegerType,
+            .addr.array.arr.integers.store = arr_data,
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = sizeof(arr_data) / sizeof(arr_data[0]),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = "b",
+            .type = CborAttrIntegerType,
+            .addr.integer = &b_int,
+            .dflt.integer = 1
+        },
+        [2] = {
+            .attribute = NULL
+        }
+    };
+    struct cbor_attr_t test_attrs_small[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrIntegerType,
+            .addr.array.arr.integers.store = arr_data,
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = 1,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = "b",
+            .type = CborAttrIntegerType,
+            .addr.integer = &b_int,
+            .dflt.integer = 1
+        },
+        [2] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_int_array();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 5);
+    TEST_ASSERT(arr_data[0] == 1);
+    TEST_ASSERT(arr_data[1] == 2);
+    TEST_ASSERT(arr_data[2] == 33);
+    TEST_ASSERT(arr_data[3] == 15);
+    TEST_ASSERT(arr_data[4] == -4);
+    TEST_ASSERT(b_int == 1);
+
+    memset(arr_data, 0, sizeof(arr_data));
+    b_int = 0;
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs_small);
+    TEST_ASSERT(rc == CborErrorDataTooLarge);
+    TEST_ASSERT(arr_cnt == 1);
+    TEST_ASSERT(arr_data[0] == 1);
+    TEST_ASSERT(arr_data[1] == 0);
+    TEST_ASSERT(b_int == 1);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_nesting.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_nesting.c
new file mode 100644
index 000000000..4d8524fe6
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_nesting.c
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "test_cborattr.h"
+
+#define TEST_NESTING_DEEP   200
+
+static uint8_t test_nesting_buf[TEST_NESTING_DEEP + 32];
+
+/*
+ * Encodes {"a": 1, "x": <value>} or {"a": [1, <value>]} where value is an
+ * integer nested in specified number of single element arrays.
+ */
+static int
+test_nesting_encode(int depth, bool in_array)
+{
+    int len = 0;
+    int i;
+
+    test_nesting_buf[len++] = in_array ? 0xa1 : 0xa2;
+    test_nesting_buf[len++] = 0x61;
+    test_nesting_buf[len++] = 'a';
+    if (in_array) {
+        test_nesting_buf[len++] = 0x82;
+        test_nesting_buf[len++] = 0x01;
+    } else {
+        test_nesting_buf[len++] = 0x01;
+        test_nesting_buf[len++] = 0x61;
+        test_nesting_buf[len++] = 'x';
+    }
+    for (i = 0; i < depth; i++) {
+        test_nesting_buf[len++] = 0x81;
+    }
+    test_nesting_buf[len++] = 0x00;
+
+    return len;
+}
+
+TEST_CASE_SELF(test_cborattr_decode_nesting)
+{
+    long long int a;
+    long long int arr[1];
+    int arr_cnt;
+    int len;
+    int rc;
+
+    const struct cbor_attr_t attrs[] = {
+        {
+            .attribute = "a",
+            .type = CborAttrIntegerType,
+            .addr.integer = &a,
+        },
+        { 0 },
+    };
+    const struct cbor_attr_t arr_attrs[] = {
+        {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrIntegerType,
+            .addr.array.arr.integers.store = arr,
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = 1,
+        },
+        { 0 },
+    };
+
+    /* Unknown attribute with nested value is skipped. */
+    len = test_nesting_encode(4, false);
+    a = 0;
+    rc = cbor_read_flat_attrs(test_nesting_buf, len, attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(a == 1);
+
+    /* Value nested deeper than allowed is rejected. */
+    len = test_nesting_encode(TEST_NESTING_DEEP, false);
+    rc = cbor_read_flat_attrs(test_nesting_buf, len, attrs);
+    TEST_ASSERT(rc != 0);
+
+    /* Excess array element nested deeper than allowed is rejected. */
+    len = test_nesting_encode(TEST_NESTING_DEEP, true);
+    rc = cbor_read_flat_attrs(test_nesting_buf, len, arr_attrs);
+    TEST_ASSERT(rc != 0);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_obj_array.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_obj_array.c
new file mode 100644
index 000000000..57c46cdf2
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_obj_array.c
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_obj_array(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+    CborEncoder obj;
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * a: [{ n:"a", v:1}, {n:"b", v:2} ]
+     */
+    cbor_encode_text_stringz(&data, "a");
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+
+    cbor_encoder_create_map(&array, &obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&obj, "n");
+    cbor_encode_text_stringz(&obj, "a");
+    cbor_encode_text_stringz(&obj, "v");
+    cbor_encode_int(&obj, 1);
+    cbor_encoder_close_container(&array, &obj);
+
+    cbor_encoder_create_map(&array, &obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&obj, "n");
+    cbor_encode_text_stringz(&obj, "b");
+    cbor_encode_text_stringz(&obj, "v");
+    cbor_encode_int(&obj, 2);
+    cbor_encoder_close_container(&array, &obj);
+
+    cbor_encoder_close_container(&data, &array);
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * object array
+ */
+TEST_CASE(test_cborattr_decode_obj_array)
+{
+    int rc;
+    char arr_data[4];
+    int arr_cnt;
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrNullType,
+            .addr.array.arr.objects.base = arr_data,
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = 4,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_obj_array();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 2);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_object.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_object.c
new file mode 100644
index 000000000..81e6f9231
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_object.c
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_data(void)
+{
+    CborEncoder test_data;
+    CborEncoder sub_obj;
+
+    test_cbor_len = 0;
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+    cbor_encoder_create_map(&test_encoder, &test_data, CborIndefiniteLength);
+
+    /*
+     * p: { bm : 7 }
+     */
+    cbor_encode_text_stringz(&test_data, "p");
+
+    cbor_encoder_create_map(&test_data, &sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&test_data, "bm");
+    cbor_encode_int(&test_data, 7);
+    cbor_encoder_close_container(&test_data, &sub_obj);
+
+    cbor_encoder_close_container(&test_encoder, &test_data);
+}
+
+static void
+test_encode_data_complex(void)
+{
+    CborEncoder test_data;
+    CborEncoder sub_obj;
+    CborEncoder sub_sub_obj;
+
+    test_cbor_len = 0;
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+    cbor_encoder_create_map(&test_encoder, &test_data, CborIndefiniteLength);
+
+    /*
+     * p: { bm : 7 }, c: { d : { i : 1 } }, a: 3
+     */
+    cbor_encode_text_stringz(&test_data, "p");
+
+    cbor_encoder_create_map(&test_data, &sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&test_data, "bm");
+    cbor_encode_int(&test_data, 7);
+    cbor_encoder_close_container(&test_data, &sub_obj);
+
+    cbor_encode_text_stringz(&test_data, "c");
+    cbor_encoder_create_map(&test_data, &sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&sub_obj, "d");
+
+    cbor_encoder_create_map(&sub_obj, &sub_sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&sub_sub_obj, "i");
+    cbor_encode_int(&sub_sub_obj, 1);
+    cbor_encoder_close_container(&sub_obj, &sub_sub_obj);
+    cbor_encoder_close_container(&test_data, &sub_obj);
+
+    cbor_encode_text_stringz(&test_data, "a");
+    cbor_encode_int(&test_data, 3);
+
+    cbor_encoder_close_container(&test_encoder, &test_data);
+}
+
+/*
+ * Simple decoding.
+ */
+TEST_CASE(test_cborattr_decode_object)
+{
+    int rc;
+    long long int bm_val = 0;
+    struct cbor_attr_t test_sub_attr_bm[] = {
+        [0] = {
+            .attribute = "bm",
+            .type = CborAttrIntegerType,
+            .addr.integer = &bm_val,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "p",
+            .type = CborAttrObjectType,
+            .addr.obj = test_sub_attr_bm,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+    long long int a_val = 0;
+    long long int i_val = 0;
+    struct cbor_attr_t test_sub_sub_attr[] = {
+        [0] = {
+            .attribute = "i",
+            .type = CborAttrIntegerType,
+            .addr.integer = &i_val,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+    struct cbor_attr_t test_sub_attr_d[] = {
+        [0] = {
+            .attribute = "d",
+            .type = CborAttrObjectType,
+            .addr.obj = test_sub_sub_attr,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+    struct cbor_attr_t test_attr_complex[] = {
+        [0] = {
+            .attribute = "c",
+            .type = CborAttrObjectType,
+            .addr.obj = test_sub_attr_d,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = "a",
+            .type = CborAttrIntegerType,
+            .addr.integer = &a_val,
+            .nodefault = true
+        },
+        [2] = {
+            .attribute = "p",
+            .type = CborAttrObjectType,
+            .addr.obj = test_sub_attr_bm,
+            .nodefault = true
+        },
+        [3] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_data();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(bm_val == 7);
+
+    test_encode_data_complex();
+
+    bm_val = 0;
+    i_val = 0;
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attr_complex);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(bm_val == 7);
+    TEST_ASSERT(i_val == 1);
+}
diff --git 
a/encoding/cborattr/test/src/testcases/cborattr_decode_object_array.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_object_array.c
new file mode 100644
index 000000000..82cc34f17
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_object_array.c
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_object_array(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+    CborEncoder sub_obj;
+
+    test_cbor_len = 0;
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * a: [ { h:"str1"}, {h:"2str"}, {h:"str3"}]
+     */
+    cbor_encode_text_stringz(&data, "a");
+
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+
+    cbor_encoder_create_map(&array, &sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&sub_obj, "h");
+    cbor_encode_text_stringz(&sub_obj, "str1");
+    cbor_encoder_close_container(&array, &sub_obj);
+
+    cbor_encoder_create_map(&array, &sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&sub_obj, "h");
+    cbor_encode_text_stringz(&sub_obj, "2str");
+    cbor_encoder_close_container(&array, &sub_obj);
+
+    cbor_encoder_create_map(&array, &sub_obj, CborIndefiniteLength);
+    cbor_encode_text_stringz(&sub_obj, "h");
+    cbor_encode_text_stringz(&sub_obj, "str3");
+    cbor_encoder_close_container(&array, &sub_obj);
+
+    cbor_encoder_close_container(&data, &array);
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * object array
+ */
+TEST_CASE(test_cborattr_decode_object_array)
+{
+    int rc;
+    struct h_obj {
+        char h_data[32];
+    } arr_objs[5];
+    int arr_cnt = 0;
+    struct cbor_attr_t sub_attr[] = {
+        [0] = {
+            .attribute = "h",
+            .type = CborAttrTextStringType,
+            CBORATTR_STRUCT_OBJECT(struct h_obj, h_data),
+            .len = sizeof(arr_objs[0].h_data)
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            CBORATTR_STRUCT_ARRAY(arr_objs, sub_attr, &arr_cnt),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_object_array();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 3);
+    TEST_ASSERT(!strcmp(arr_objs[0].h_data, "str1"));
+    TEST_ASSERT(!strcmp(arr_objs[1].h_data, "2str"));
+    TEST_ASSERT(!strcmp(arr_objs[2].h_data, "str3"));
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_partial.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_partial.c
new file mode 100644
index 000000000..df0b8d4f5
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_partial.c
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Simple decoding. Only have key for one of the key/value pairs.
+ */
+TEST_CASE(test_cborattr_decode_partial)
+{
+    const uint8_t *data;
+    int len;
+    int rc;
+    char test_str_b[4] = { '\0' };
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "b",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_b,
+            .len = sizeof(test_str_b),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+
+    data = test_str1(&len);
+    rc = cbor_read_flat_attrs(data, len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(!strcmp(test_str_b, "B"));
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_decode_simple.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_simple.c
new file mode 100644
index 000000000..aef3a8078
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_simple.c
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_data(void)
+{
+    CborEncoder test_data;
+    uint8_t data[4] = { 0, 1, 2 };
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &test_data, CborIndefiniteLength);
+    /*
+     * a:22
+     */
+    cbor_encode_text_stringz(&test_data, "a");
+    cbor_encode_uint(&test_data, 22);
+
+    /*
+     * b:-13
+     */
+    cbor_encode_text_stringz(&test_data, "b");
+    cbor_encode_int(&test_data, -13);
+
+    /*
+     * c:0x000102
+     */
+    cbor_encode_text_stringz(&test_data, "c");
+    cbor_encode_byte_string(&test_data, data, 3);
+    cbor_encoder_close_container(&test_encoder, &test_data);
+
+    /*
+     * XXX add other data types to encode here.
+     */
+
+}
+
+/*
+ * Simple decoding.
+ */
+TEST_CASE(test_cborattr_decode_simple)
+{
+    int rc;
+    long long unsigned int a_val = 0;
+    long long int b_val = 0;
+    uint8_t c_data[4];
+    size_t c_len;
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrIntegerType,
+            .addr.uinteger = &a_val,
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = "b",
+            .type = CborAttrIntegerType,
+            .addr.integer = &b_val,
+            .nodefault = true
+        },
+        [2] = {
+            .attribute = "c",
+            .type = CborAttrByteStringType,
+            .addr.bytestring.data = c_data,
+            .addr.bytestring.len = &c_len,
+            .len = sizeof(c_data)
+        },
+        [3] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_data();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(a_val == 22);
+    TEST_ASSERT(b_val == -13);
+    TEST_ASSERT(c_len == 3);
+    TEST_ASSERT(c_data[0] == 0 && c_data[1] == 1 && c_data[2] == 2);
+}
diff --git 
a/encoding/cborattr/test/src/testcases/cborattr_decode_string_array.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_string_array.c
new file mode 100644
index 000000000..d68e8dfaf
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_string_array.c
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_string_array_one(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+
+    test_cbor_len = 0;
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * a: ["asdf"]
+     */
+    cbor_encode_text_stringz(&data, "a");
+
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+    cbor_encode_text_stringz(&array, "asdf");
+    cbor_encoder_close_container(&data, &array);
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+static void
+test_encode_string_array_three(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+
+    test_cbor_len = 0;
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * a: ["asdf", "k", "blurb"]
+     */
+    cbor_encode_text_stringz(&data, "a");
+
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+    cbor_encode_text_stringz(&array, "asdf");
+    cbor_encode_text_stringz(&array, "k");
+    cbor_encode_text_stringz(&array, "blurb");
+    cbor_encoder_close_container(&data, &array);
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * string array
+ */
+TEST_CASE(test_cborattr_decode_string_array)
+{
+    int rc;
+    char *str_ptrs[5];
+    char arr_data[256];
+    int arr_cnt = 0;
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrTextStringType,
+            .addr.array.arr.strings.ptrs = str_ptrs,
+            .addr.array.arr.strings.store = arr_data,
+            .addr.array.arr.strings.storelen = sizeof(arr_data),
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = sizeof(arr_data) / sizeof(arr_data[0]),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_string_array_one();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 1);
+    TEST_ASSERT(!strcmp(str_ptrs[0], "asdf"));
+
+    test_encode_string_array_three();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 3);
+    TEST_ASSERT(!strcmp(str_ptrs[0], "asdf"));
+    TEST_ASSERT(!strcmp(str_ptrs[1], "k"));
+    TEST_ASSERT(!strcmp(str_ptrs[2], "blurb"));
+}
diff --git 
a/encoding/cborattr/test/src/testcases/cborattr_decode_string_array_bounds.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_string_array_bounds.c
new file mode 100644
index 000000000..b0a2ce432
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_string_array_bounds.c
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "test_cborattr.h"
+
+static uint8_t test_arr_buf[64];
+static int test_arr_len;
+
+static int
+test_arr_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_arr_buf + test_arr_len, data, len);
+    test_arr_len += len;
+    assert(test_arr_len < sizeof(test_arr_buf));
+    return 0;
+}
+
+/* Encodes {"a": [strs...]} */
+static void
+test_arr_encode(const char **strs, int cnt)
+{
+    struct cbor_encoder_writer writer = { .write = test_arr_wr };
+    CborEncoder enc;
+    CborEncoder map;
+    CborEncoder arr;
+    int i;
+
+    test_arr_len = 0;
+    cbor_encoder_init(&enc, &writer, 0);
+    cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
+    cbor_encode_text_stringz(&map, "a");
+    cbor_encoder_create_array(&map, &arr, CborIndefiniteLength);
+    for (i = 0; i < cnt; i++) {
+        cbor_encode_text_stringz(&arr, strs[i]);
+    }
+    cbor_encoder_close_container(&map, &arr);
+    cbor_encoder_close_container(&enc, &map);
+}
+
+TEST_CASE_SELF(test_cborattr_decode_string_array_bounds)
+{
+    const char *fit[] = { "ab", "cd" };
+    const char *no_fit[] = { "abcdefgh", "zzzzzzzzzzzz", "zzzzzzzzzzzz" };
+    struct {
+        char store[8];
+        char guard[24];
+    } buf;
+    char *ptrs[3];
+    int cnt;
+    int rc;
+    int i;
+
+    const struct cbor_attr_t attrs[] = {
+        {
+            .attribute = "a",
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrTextStringType,
+            .addr.array.arr.strings.ptrs = ptrs,
+            .addr.array.arr.strings.store = buf.store,
+            .addr.array.arr.strings.storelen = sizeof(buf.store),
+            .addr.array.count = &cnt,
+            .addr.array.maxlen = 3,
+        },
+        { 0 },
+    };
+
+    /* Strings fitting in store are decoded. */
+    test_arr_encode(fit, 2);
+    rc = cbor_read_flat_attrs(test_arr_buf, test_arr_len, attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(cnt == 2);
+    TEST_ASSERT(strcmp(ptrs[0], "ab") == 0);
+    TEST_ASSERT(strcmp(ptrs[1], "cd") == 0);
+
+    /* String not fitting in store is rejected and nothing is written past
+     * the store.
+     */
+    memset(buf.guard, 0x5a, sizeof(buf.guard));
+    test_arr_encode(no_fit, 3);
+    rc = cbor_read_flat_attrs(test_arr_buf, test_arr_len, attrs);
+    TEST_ASSERT(rc != 0);
+    TEST_ASSERT(cnt == 0);
+    for (i = 0; i < sizeof(buf.guard); i++) {
+        TEST_ASSERT_FATAL(buf.guard[i] == 0x5a);
+    }
+}
diff --git 
a/encoding/cborattr/test/src/testcases/cborattr_decode_string_bounds.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_string_bounds.c
new file mode 100644
index 000000000..8e399e0b2
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_string_bounds.c
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "test_cborattr.h"
+
+static uint8_t test_str_buf[64];
+static int test_str_len;
+
+static int
+test_str_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_str_buf + test_str_len, data, len);
+    test_str_len += len;
+    assert(test_str_len < sizeof(test_str_buf));
+    return 0;
+}
+
+/* Encodes {"s": <text>, "b": <bytes>, "i": 1}, omitting NULL members. */
+static void
+test_str_encode(const char *s, const uint8_t *b, size_t b_len)
+{
+    struct cbor_encoder_writer writer = { .write = test_str_wr };
+    CborEncoder enc;
+    CborEncoder map;
+
+    test_str_len = 0;
+    cbor_encoder_init(&enc, &writer, 0);
+    cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
+    if (s) {
+        cbor_encode_text_stringz(&map, "s");
+        cbor_encode_text_stringz(&map, s);
+    }
+    if (b) {
+        cbor_encode_text_stringz(&map, "b");
+        cbor_encode_byte_string(&map, b, b_len);
+    }
+    cbor_encode_text_stringz(&map, "i");
+    cbor_encode_uint(&map, 1);
+    cbor_encoder_close_container(&enc, &map);
+}
+
+TEST_CASE_SELF(test_cborattr_decode_string_bounds)
+{
+    const uint8_t bytes[8] = { 0 };
+    uint8_t b[4];
+    size_t b_len;
+    char s[4];
+    long long int i;
+    int rc;
+
+    const struct cbor_attr_t attrs[] = {
+        {
+            .attribute = "s",
+            .type = CborAttrTextStringType,
+            .addr.string = s,
+            .len = sizeof(s),
+        },
+        {
+            .attribute = "b",
+            .type = CborAttrByteStringType,
+            .addr.bytestring.data = b,
+            .addr.bytestring.len = &b_len,
+            .len = sizeof(b),
+        },
+        {
+            .attribute = "i",
+            .type = CborAttrIntegerType,
+            .addr.integer = &i,
+        },
+        { 0 },
+    };
+
+    /* String shorter than buffer is decoded and terminated. */
+    memset(s, 'x', sizeof(s));
+    test_str_encode("abc", bytes, 4);
+    rc = cbor_read_flat_attrs(test_str_buf, test_str_len, attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(strcmp(s, "abc") == 0);
+    TEST_ASSERT(b_len == 4);
+    TEST_ASSERT(i == 1);
+
+    /* String filling whole buffer is rejected, buffer is left terminated. */
+    memset(s, 'x', sizeof(s));
+    test_str_encode("abcd", NULL, 0);
+    rc = cbor_read_flat_attrs(test_str_buf, test_str_len, attrs);
+    TEST_ASSERT(rc != 0);
+    TEST_ASSERT(s[0] == '\0');
+
+    /* Too long byte string is rejected and reports zero length. */
+    test_str_encode(NULL, bytes, sizeof(bytes));
+    rc = cbor_read_flat_attrs(test_str_buf, test_str_len, attrs);
+    TEST_ASSERT(rc != 0);
+    TEST_ASSERT(b_len == 0);
+
+    /* Omitted string and byte string are set to empty. */
+    memset(s, 'x', sizeof(s));
+    b_len = 1234;
+    test_str_encode(NULL, NULL, 0);
+    rc = cbor_read_flat_attrs(test_str_buf, test_str_len, attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(s[0] == '\0');
+    TEST_ASSERT(b_len == 0);
+}
diff --git 
a/encoding/cborattr/test/src/testcases/cborattr_decode_substring_key.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_substring_key.c
new file mode 100644
index 000000000..6eee7740c
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_substring_key.c
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_substring_key(void)
+{
+    CborEncoder data;
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    /*
+     * { "a": "A", "aa": "AA", "aaa" : "AAA" }
+     */
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    cbor_encode_text_stringz(&data, "a");
+    cbor_encode_text_stringz(&data, "A");
+    cbor_encode_text_stringz(&data, "aa");
+    cbor_encode_text_stringz(&data, "AA");
+    cbor_encode_text_stringz(&data, "aaa");
+    cbor_encode_text_stringz(&data, "AAA");
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * substring key
+ */
+TEST_CASE(test_cborattr_decode_substring_key)
+{
+    int rc;
+    char test_str_1a[4] = { '\0' };
+    char test_str_2a[4] = { '\0' };
+    char test_str_3a[4] = { '\0' };
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = "aaa",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_3a,
+            .len = sizeof(test_str_3a),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = "aa",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_2a,
+            .len = sizeof(test_str_2a),
+            .nodefault = true
+            },
+        [2] = {
+            .attribute = "a",
+            .type = CborAttrTextStringType,
+            .addr.string = test_str_1a,
+            .len = sizeof(test_str_1a),
+            .nodefault = true
+            },
+        [3] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_substring_key();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(!strcmp(test_str_1a, "A"));
+    TEST_ASSERT(!strcmp(test_str_2a, "AA"));
+    TEST_ASSERT(!strcmp(test_str_3a, "AAA"));
+}
diff --git 
a/encoding/cborattr/test/src/testcases/cborattr_decode_unnamed_array.c 
b/encoding/cborattr/test/src/testcases/cborattr_decode_unnamed_array.c
new file mode 100644
index 000000000..0baf4ccb8
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_decode_unnamed_array.c
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+/*
+ * Where we collect cbor data.
+ */
+static uint8_t test_cbor_buf[1024];
+static int test_cbor_len;
+
+/*
+ * CBOR encoder data structures.
+ */
+static int test_cbor_wr(struct cbor_encoder_writer *, const char *, int);
+static CborEncoder test_encoder;
+static struct cbor_encoder_writer test_writer = {
+    .write = test_cbor_wr
+};
+
+static int
+test_cbor_wr(struct cbor_encoder_writer *cew, const char *data, int len)
+{
+    memcpy(test_cbor_buf + test_cbor_len, data, len);
+    test_cbor_len += len;
+
+    assert(test_cbor_len < sizeof(test_cbor_buf));
+    return 0;
+}
+
+static void
+test_encode_unnamed_array(void)
+{
+    CborEncoder data;
+    CborEncoder array;
+
+    cbor_encoder_init(&test_encoder, &test_writer, 0);
+
+    cbor_encoder_create_map(&test_encoder, &data, CborIndefiniteLength);
+
+    /*
+     * [1,2,33]
+     */
+    cbor_encoder_create_array(&data, &array, CborIndefiniteLength);
+    cbor_encode_int(&array, 1);
+    cbor_encode_int(&array, 2);
+    cbor_encode_int(&array, 33);
+    cbor_encoder_close_container(&data, &array);
+
+    cbor_encoder_close_container(&test_encoder, &data);
+}
+
+/*
+ * integer array
+ */
+TEST_CASE(test_cborattr_decode_unnamed_array)
+{
+    int rc;
+    long long int arr_data[5];
+    int arr_cnt = 0;
+    struct cbor_attr_t test_attrs[] = {
+        [0] = {
+            .attribute = CBORATTR_ATTR_UNNAMED,
+            .type = CborAttrArrayType,
+            .addr.array.element_type = CborAttrIntegerType,
+            .addr.array.arr.integers.store = arr_data,
+            .addr.array.count = &arr_cnt,
+            .addr.array.maxlen = sizeof(arr_data) / sizeof(arr_data[0]),
+            .nodefault = true
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+
+    test_encode_unnamed_array();
+
+    rc = cbor_read_flat_attrs(test_cbor_buf, test_cbor_len, test_attrs);
+    TEST_ASSERT(rc == 0);
+    TEST_ASSERT(arr_cnt == 3);
+    TEST_ASSERT(arr_data[0] == 1);
+    TEST_ASSERT(arr_data[1] == 2);
+    TEST_ASSERT(arr_data[2] == 33);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_encode_omit.c 
b/encoding/cborattr/test/src/testcases/cborattr_encode_omit.c
new file mode 100644
index 000000000..7d6b91fc8
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_encode_omit.c
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+TEST_CASE_SELF(test_cborattr_encode_omit)
+{
+    /* Omit everything except the "str" value. */
+    const struct cbor_out_attr_t attrs[11] = {
+        {
+            .attribute = "null",
+            .val = {
+                .type = CborAttrNullType,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "bool",
+            .val = {
+                .type = CborAttrBooleanType,
+                .boolean = true,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "int",
+            .val = {
+                .type = CborAttrIntegerType,
+                .integer = -99,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "uint",
+            .val = {
+                .type = CborAttrUnsignedIntegerType,
+                .integer = 8442,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "float",
+            .val = {
+                .type = CborAttrFloatType,
+                .fval = 8.0,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "double",
+            .val = {
+                .type = CborAttrDoubleType,
+                .real = 16.0,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "bytes",
+            .val = {
+                .type = CborAttrByteStringType,
+                .bytestring.data = (uint8_t[]) {1, 2, 3},
+                .bytestring.len = 3,
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "str",
+            .val = {
+                .type = CborAttrTextStringType,
+                .string = "mystr",
+            },
+        },
+        {
+            .attribute = "arr",
+            .val = {
+                .type = CborAttrArrayType,
+                .array = {
+                    .elems = (struct cbor_out_val_t[]) {
+                        [0] = {
+                            .type = CborAttrUnsignedIntegerType,
+                            .integer = 4355,
+                        },
+                        [1] = {
+                            .type = CborAttrBooleanType,
+                            .integer = false,
+                        },
+                    },
+                    .len = 2,
+                },
+            },
+            .omit = true,
+        },
+        {
+            .attribute = "obj",
+            .val = {
+                .type = CborAttrObjectType,
+                .obj = (struct cbor_out_attr_t[]) {
+                    {
+                        .attribute = "inner_str",
+                        .val = {
+                            .type = CborAttrTextStringType,
+                            .string = "mystr2",
+                        },
+                    },
+                    {
+                        .attribute = "inner_int",
+                        .val = {
+                            .type = CborAttrIntegerType,
+                            .integer = 123,
+                        },
+                    },
+                    { 0 }
+                },
+            },
+            .omit = true,
+        },
+        { 0 }
+    };
+    const uint8_t exp[] = {
+        0xbf, 0x63, 0x73, 0x74, 0x72, 0x65, 0x6d, 0x79,
+        0x73, 0x74, 0x72, 0xff,
+    };
+
+    cborattr_test_util_encode(attrs, exp, sizeof exp);
+}
diff --git a/encoding/cborattr/test/src/testcases/cborattr_encode_simple.c 
b/encoding/cborattr/test/src/testcases/cborattr_encode_simple.c
new file mode 100644
index 000000000..1ddcf3cac
--- /dev/null
+++ b/encoding/cborattr/test/src/testcases/cborattr_encode_simple.c
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include "test_cborattr.h"
+
+TEST_CASE_SELF(test_cborattr_encode_simple)
+{
+    const struct cbor_out_attr_t attrs[] = {
+        {
+            .attribute = "null",
+            .val = {
+                .type = CborAttrNullType,
+            },
+        },
+        {
+            .attribute = "bool",
+            .val = {
+                .type = CborAttrBooleanType,
+                .boolean = true,
+            },
+        },
+        {
+            .attribute = "int",
+            .val = {
+                .type = CborAttrIntegerType,
+                .integer = -99,
+            },
+        },
+        {
+            .attribute = "uint",
+            .val = {
+                .type = CborAttrUnsignedIntegerType,
+                .integer = 8442,
+            },
+        },
+        {
+            .attribute = "float",
+            .val = {
+                .type = CborAttrFloatType,
+                .fval = 8.0,
+            },
+        },
+        {
+            .attribute = "double",
+            .val = {
+                .type = CborAttrDoubleType,
+                .real = 16.0,
+            },
+        },
+        {
+            .attribute = "bytes",
+            .val = {
+                .type = CborAttrByteStringType,
+                .bytestring.data = (uint8_t[]) {1, 2, 3},
+                .bytestring.len = 3,
+            },
+        },
+        {
+            .attribute = "str",
+            .val = {
+                .type = CborAttrTextStringType,
+                .string = "mystr",
+            },
+        },
+        {
+            .attribute = "arr",
+            .val = {
+                .type = CborAttrArrayType,
+                .array = {
+                    .elems = (struct cbor_out_val_t[]) {
+                        [0] = {
+                            .type = CborAttrUnsignedIntegerType,
+                            .integer = 4355,
+                        },
+                        [1] = {
+                            .type = CborAttrBooleanType,
+                            .integer = false,
+                        },
+                    },
+                    .len = 2,
+                },
+            },
+        },
+        {
+            .attribute = "obj",
+            .val = {
+                .type = CborAttrObjectType,
+                .obj = (struct cbor_out_attr_t[]) {
+                    {
+                        .attribute = "inner_str",
+                        .val = {
+                            .type = CborAttrTextStringType,
+                            .string = "mystr2",
+                        },
+                    },
+                    {
+                        .attribute = "inner_int",
+                        .val = {
+                            .type = CborAttrIntegerType,
+                            .integer = 123,
+                        },
+                    },
+                    { 0 }
+                },
+            },
+        },
+        { 0 }
+    };
+    const uint8_t exp[] = {
+        0xbf, 0x64, 0x6e, 0x75, 0x6c, 0x6c, 0xf6, 0x64,
+        0x62, 0x6f, 0x6f, 0x6c, 0xf5, 0x63, 0x69, 0x6e,
+        0x74, 0x38, 0x62, 0x64, 0x75, 0x69, 0x6e, 0x74,
+        0x19, 0x20, 0xfa, 0x65, 0x66, 0x6c, 0x6f, 0x61,
+        0x74, 0xfa, 0x41, 0x00, 0x00, 0x00, 0x66, 0x64,
+        0x6f, 0x75, 0x62, 0x6c, 0x65, 0xfb, 0x40, 0x30,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x62,
+        0x79, 0x74, 0x65, 0x73, 0x43, 0x01, 0x02, 0x03,
+        0x63, 0x73, 0x74, 0x72, 0x65, 0x6d, 0x79, 0x73,
+        0x74, 0x72, 0x63, 0x61, 0x72, 0x72, 0x82, 0x19,
+        0x11, 0x03, 0xf4, 0x63, 0x6f, 0x62, 0x6a, 0xbf,
+        0x69, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x73,
+        0x74, 0x72, 0x66, 0x6d, 0x79, 0x73, 0x74, 0x72,
+        0x32, 0x69, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x5f,
+        0x69, 0x6e, 0x74, 0x18, 0x7b, 0xff, 0xff,
+    };
+
+    cborattr_test_util_encode(attrs, exp, sizeof exp);
+}

Reply via email to