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); +}
