blob: 93a85f15d8bc0ffa22e29d4944bad02a46b1c3ab [file] [log] [blame]
/*
* 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 "os/mynewt.h"
#include <cborattr/cborattr.h>
#include <tinycbor/cbor.h>
#include <tinycbor/cbor_buf_reader.h>
#include <tinycbor/cbor_mbuf_reader.h>
/* 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;
}
#if FLOAT_SUPPORT
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 FLOAT_SUPPORT
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[MYNEWT_VAL(CBORATTR_MAX_SIZE) + 1];
void *lptr;
CborValue cur_value;
CborError err = 0;
size_t len;
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;
#if FLOAT_SUPPORT
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 > MYNEWT_VAL(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 FLOAT_SUPPORT
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;
}
}
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 FLOAT_SUPPORT
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);
}
/*
* 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);
}