blob: 026d4b1028429c6f40219130c6a4fbb332c6ba77 [file] [log] [blame]
/*
* Copyright (c) 2018-2021 mcumgr authors
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "cborattr/cborattr.h"
#include "tinycbor/cbor.h"
#include "tinycbor/cbor_buf_reader.h"
#include <zephyr.h>
#ifdef CONFIG_MGMT_CBORATTR_MAX_SIZE
#define CBORATTR_MAX_SIZE CONFIG_MGMT_CBORATTR_MAX_SIZE
#else
#define CBORATTR_MAX_SIZE 512
#endif
#ifdef CONFIG_MGMT_CBORATTR_FLOAT_SUPPORT
#define CBORATTR_FLOAT_SUPPORT 0
#else
#define CBORATTR_FLOAT_SUPPORT 1
#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, enum 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 CBORATTR_FLOAT_SUPPORT != 0
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;
}
/* 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 CBORATTR_FLOAT_SUPPORT != 0
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));
break;
case CborAttrUnsignedIntegerType:
memcpy(lptr, &cursor->dflt.integer,
sizeof(unsigned long long));
break;
case CborAttrBooleanType:
memcpy(lptr, &cursor->dflt.boolean, sizeof(bool));
break;
#if CBORATTR_FLOAT_SUPPORT != 0
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)) {
if (cbor_value_calculate_string_length(&cur_value, &len) == 0) {
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 |= cbor_value_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 CBORATTR_FLOAT_SUPPORT != 0
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;
err |= cbor_value_copy_byte_string(&cur_value, lptr, &len, NULL);
*cursor->addr.bytestring.len = len;
break;
}
case CborAttrTextStringType: {
size_t len = cursor->len;
err |= cbor_value_copy_text_string(&cur_value, lptr, &len, NULL);
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 |= cbor_value_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;
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 CBORATTR_FLOAT_SUPPORT != 0
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);
err |= cbor_value_copy_text_string(&elem, tp, &len, NULL);
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;
}
arrcount++;
if (arr->element_type != CborAttrStructObjectType) {
err |= cbor_value_advance(&elem);
}
if (!cbor_value_is_valid(&elem)) {
break;
}
}
if (arr->count) {
*arr->count = arrcount;
}
while (!cbor_value_at_end(&elem)) {
err |= CborErrorDataTooLarge;
cbor_value_advance(&elem);
}
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);
}