| /* |
| * 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); |
| } |