/* * Copyright (C) 2014 Freie Universität Berlin * Copyright (C) 2014 Kevin Funk * Copyright (C) 2014 Jana Cavojska * * This file is subject to the terms and conditions of the GNU Lesser General * Public License v2.1. See the file LICENSE in the top level directory for more * details. */ /** * @author Kevin Funk * @author Jana Cavojska */ #include "cbor.h" #include "byteorder.h" #include #include #include #include #include #include /* Automatically enable/disable ENABLE_DEBUG based on CBOR_NO_PRINT */ #ifndef CBOR_NO_PRINT #define ENABLE_DEBUG (1) #include "debug.h" #endif #define CBOR_TYPE_MASK 0xE0 /* top 3 bits */ #define CBOR_INFO_MASK 0x1F /* low 5 bits */ #define CBOR_BYTE_FOLLOWS 24 /* indicator that the next byte is part of this item */ /* Jump Table for Initial Byte (cf. table 5) */ #define CBOR_UINT 0x00 /* type 0 */ #define CBOR_NEGINT 0x20 /* type 1 */ #define CBOR_BYTES 0x40 /* type 2 */ #define CBOR_TEXT 0x60 /* type 3 */ #define CBOR_ARRAY 0x80 /* type 4 */ #define CBOR_MAP 0xA0 /* type 5 */ #define CBOR_TAG 0xC0 /* type 6 */ #define CBOR_7 0xE0 /* type 7 (float and other types) */ /* Major types (cf. section 2.1) */ /* Major type 0: Unsigned integers */ #define CBOR_UINT8_FOLLOWS 24 /* 0x18 */ #define CBOR_UINT16_FOLLOWS 25 /* 0x19 */ #define CBOR_UINT32_FOLLOWS 26 /* 0x1a */ #define CBOR_UINT64_FOLLOWS 27 /* 0x1b */ /* Indefinite Lengths for Some Major types (cf. section 2.2) */ #define CBOR_VAR_FOLLOWS 31 /* 0x1f */ /* Major type 6: Semantic tagging */ #define CBOR_DATETIME_STRING_FOLLOWS 0 #define CBOR_DATETIME_EPOCH_FOLLOWS 1 /* Major type 7: Float and other types */ #define CBOR_FALSE (CBOR_7 | 20) #define CBOR_TRUE (CBOR_7 | 21) #define CBOR_NULL (CBOR_7 | 22) #define CBOR_UNDEFINED (CBOR_7 | 23) /* CBOR_BYTE_FOLLOWS == 24 */ #define CBOR_FLOAT16 (CBOR_7 | 25) #define CBOR_FLOAT32 (CBOR_7 | 26) #define CBOR_FLOAT64 (CBOR_7 | 27) #define CBOR_BREAK (CBOR_7 | 31) #define CBOR_TYPE(stream, offset) (stream->data[offset] & CBOR_TYPE_MASK) #define CBOR_ADDITIONAL_INFO(stream, offset) (stream->data[offset] & CBOR_INFO_MASK) /* Ensure that @p stream is big enough to fit @p bytes bytes, otherwise return 0 */ #define CBOR_ENSURE_SIZE(stream, bytes) do { \ if (stream->pos + bytes >= stream->size) { return 0; } \ } while(0) /* Extra defines not related to the protocol itself */ #define CBOR_STREAM_PRINT_BUFFERSIZE 1024 /* bytes */ /* Array size */ #define MAX_TIMESTRING_LENGTH (21) #ifndef INFINITY #define INFINITY (1.0/0.0) #endif #ifndef NAN #define NAN (0.0/0.0) #endif #ifndef CBOR_NO_FLOAT /** * Convert float @p x to network format */ static uint32_t htonf(float x) { union u { float f; uint32_t i; } u = { .f = x }; return HTONL(u.i); } /** * Convert float @p x to host format */ static float ntohf(uint32_t x) { union u { float f; uint32_t i; } u = { .i = NTOHL(x) }; return u.f; } /** * Convert double @p x to network format */ static uint64_t htond(double x) { union u { double d; uint64_t i; } u = { .d = x }; return HTONLL(u.i); } /** * Convert double @p x to host format */ static double ntohd(uint64_t x) { union u { double d; uint64_t i; } u = { .i = HTONLL(x) }; return u.d; } /** * Source: CBOR RFC reference implementation */ double decode_float_half(unsigned char *halfp) { int half = (halfp[0] << 8) + halfp[1]; int exp = (half >> 10) & 0x1f; int mant = half & 0x3ff; double val; if (exp == 0) { val = ldexp(mant, -24); } else if (exp != 31) { val = ldexp(mant + 1024, exp - 25); } else { val = mant == 0 ? INFINITY : NAN; } return (half & 0x8000) ? -val : val; } /** * Source: According to http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a */ static uint16_t encode_float_half(float x) { union u { float f; uint32_t i; } u = { .f = x }; uint16_t bits = (u.i >> 16) & 0x8000; /* Get the sign */ uint16_t m = (u.i >> 12) & 0x07ff; /* Keep one extra bit for rounding */ unsigned int e = (u.i >> 23) & 0xff; /* Using int is faster here */ /* If zero, or denormal, or exponent underflows too much for a denormal * half, return signed zero. */ if (e < 103) { return bits; } /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */ if (e > 142) { bits |= 0x7c00u; /* If exponent was 0xff and one mantissa bit was set, it means NaN, * not Inf, so make sure we set one mantissa bit too. */ bits |= (e == 255) && (u.i & 0x007fffffu); return bits; } /* If exponent underflows but not too much, return a denormal */ if (e < 113) { m |= 0x0800u; /* Extra rounding may overflow and set mantissa to 0 and exponent * to 1, which is OK. */ bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1); return bits; } bits |= ((e - 112) << 10) | (m >> 1); /* Extra rounding. An overflow will set mantissa to 0 and increment * the exponent, which is OK. */ bits += m & 1; return bits; } #endif /* CBOR_NO_FLOAT */ #ifndef CBOR_NO_PRINT /** * Print @p size bytes at @p data in hexadecimal display format */ void dump_memory(const unsigned char *data, size_t size) { if (!data || !size) { return; } DEBUG("0x"); for (size_t i = 0; i < size; ++i) { DEBUG("%02X", data[i]); } DEBUG("\n"); } #endif /* CBOR_NO_PRINT */ void cbor_init(cbor_stream_t *stream, unsigned char *buffer, size_t size) { if (!stream) { return; } stream->data = buffer; stream->size = size; stream->pos = 0; } void cbor_clear(cbor_stream_t *stream) { if (!stream) { return; } stream->pos = 0; } void cbor_destroy(cbor_stream_t *stream) { if (!stream) { return; } stream->data = 0; stream->size = 0; stream->pos = 0; } /** * Return additional info field value for input value @p val * * @return Byte with the additional info bits set */ static unsigned char uint_additional_info(uint64_t val) { if (val < CBOR_UINT8_FOLLOWS) { return val; } else if (val <= 0xff) { return CBOR_UINT8_FOLLOWS; } else if (val <= 0xffff) { return CBOR_UINT16_FOLLOWS; } else if (val <= 0xffffffffL) { return CBOR_UINT32_FOLLOWS; } return CBOR_UINT64_FOLLOWS; } /** * Return the number of bytes that would follow the additional info field @p additional_info * * @param additional_info Must be in the range [CBOR_UINT8_FOLLOWS, CBOR_UINT64_FOLLOWS] */ static unsigned char uint_bytes_follow(unsigned char additional_info) { if (additional_info < CBOR_UINT8_FOLLOWS || additional_info > CBOR_UINT64_FOLLOWS) { return 0; } static const unsigned char BYTES_FOLLOW[] = {1, 2, 4, 8}; return BYTES_FOLLOW[additional_info - CBOR_UINT8_FOLLOWS]; } static size_t encode_int(unsigned char major_type, cbor_stream_t *s, uint64_t val) { if (!s) { return 0; } unsigned char additional_info = uint_additional_info(val); unsigned char bytes_follow = uint_bytes_follow(additional_info); CBOR_ENSURE_SIZE(s, bytes_follow + 1); s->data[s->pos++] = major_type | additional_info; for (int i = bytes_follow - 1; i >= 0; --i) { s->data[s->pos++] = (val >> (8 * i)) & 0xff; } return bytes_follow + 1; } static size_t decode_int(const cbor_stream_t *s, size_t offset, uint64_t *val) { if (!s) { return 0; } *val = 0; /* clear val first */ unsigned char *in = &s->data[offset]; unsigned char additional_info = CBOR_ADDITIONAL_INFO(s, offset); unsigned char bytes_follow = uint_bytes_follow(additional_info); switch (bytes_follow) { case 0: *val = (in[0] & CBOR_INFO_MASK); break; case 1: *val = in[1]; break; case 2: *val = HTONS(*((uint16_t *)&in[1])); break; case 4: *val = HTONL(*((uint32_t *)&in[1])); break; default: *val = HTONLL(*((uint64_t *)&in[1])); break; } return bytes_follow + 1; } static size_t encode_bytes(unsigned char major_type, cbor_stream_t *s, const char *data, size_t length) { size_t length_field_size = uint_bytes_follow(uint_additional_info(length)) + 1; CBOR_ENSURE_SIZE(s, length_field_size + length); size_t bytes_start = encode_int(major_type, s, (uint64_t) length); if (!bytes_start) { return 0; } memcpy(&(s->data[s->pos]), data, length); /* copy byte string into our cbor struct */ s->pos += length; return (bytes_start + length); } static size_t decode_bytes(const cbor_stream_t *s, size_t offset, char *out, size_t length) { if ((CBOR_TYPE(s, offset) != CBOR_BYTES && CBOR_TYPE(s, offset) != CBOR_TEXT) || !out) { return 0; } uint64_t bytes_length; size_t bytes_start = decode_int(s, offset, &bytes_length); if (!bytes_start) { return 0; } if (length + 1 < bytes_length) { return 0; } memcpy(out, &s->data[offset + bytes_start], bytes_length); out[bytes_length] = '\0'; return (bytes_start + bytes_length); } size_t cbor_deserialize_int(const cbor_stream_t *stream, size_t offset, int *val) { if ((CBOR_TYPE(stream, offset) != CBOR_UINT && CBOR_TYPE(stream, offset) != CBOR_NEGINT) || !val) { return 0; } uint64_t buf; size_t read_bytes = decode_int(stream, offset, &buf); if (CBOR_TYPE(stream, offset) == CBOR_UINT) { *val = buf; /* resolve as CBOR_UINT */ } else { *val = -1 - buf; /* resolve as CBOR_NEGINT */ } return read_bytes; } size_t cbor_serialize_int(cbor_stream_t *s, int val) { if (val >= 0) { /* Major type 0: an unsigned integer */ return encode_int(CBOR_UINT, s, val); } else { /* Major type 1: an negative integer */ return encode_int(CBOR_NEGINT, s, -1 - val); } } size_t cbor_deserialize_uint64_t(const cbor_stream_t *stream, size_t offset, uint64_t *val) { if (CBOR_TYPE(stream, offset) != CBOR_UINT || !val) { return 0; } return decode_int(stream, offset, val); } size_t cbor_serialize_uint64_t(cbor_stream_t *s, uint64_t val) { return encode_int(CBOR_UINT, s, val); } size_t cbor_deserialize_int64_t(const cbor_stream_t *stream, size_t offset, int64_t *val) { if ((CBOR_TYPE(stream, offset) != CBOR_UINT && CBOR_TYPE(stream, offset) != CBOR_NEGINT) || !val) { return 0; } uint64_t buf; size_t read_bytes = decode_int(stream, offset, &buf); if (CBOR_TYPE(stream, offset) == CBOR_UINT) { *val = buf; /* resolve as CBOR_UINT */ } else { *val = -1 - buf; /* resolve as CBOR_NEGINT */ } return read_bytes; } size_t cbor_serialize_int64_t(cbor_stream_t *s, int64_t val) { if (val >= 0) { /* Major type 0: an unsigned integer */ return encode_int(CBOR_UINT, s, val); } else { /* Major type 1: an negative integer */ return encode_int(CBOR_NEGINT, s, -1 - val); } } size_t cbor_deserialize_bool(const cbor_stream_t *stream, size_t offset, bool *val) { if (CBOR_TYPE(stream, offset) != CBOR_7 || !val) { return 0; } unsigned char byte = stream->data[offset]; *val = (byte == CBOR_TRUE); return 1; } size_t cbor_serialize_bool(cbor_stream_t *s, bool val) { CBOR_ENSURE_SIZE(s, 1); s->data[s->pos++] = val ? CBOR_TRUE : CBOR_FALSE; return 1; } #ifndef CBOR_NO_FLOAT size_t cbor_deserialize_float_half(const cbor_stream_t *stream, size_t offset, float *val) { if (CBOR_TYPE(stream, offset) != CBOR_7 || !val) { return 0; } unsigned char *data = &stream->data[offset]; if (*data == CBOR_FLOAT16) { *val = (float)decode_float_half(data + 1); return 3; } return 0; } size_t cbor_serialize_float_half(cbor_stream_t *s, float val) { CBOR_ENSURE_SIZE(s, 3); s->data[s->pos++] = CBOR_FLOAT16; uint16_t encoded_val = HTONS(encode_float_half(val)); memcpy(s->data + s->pos, &encoded_val, 2); s->pos += 2; return 3; } size_t cbor_deserialize_float(const cbor_stream_t *stream, size_t offset, float *val) { if (CBOR_TYPE(stream, offset) != CBOR_7 || !val) { return 0; } unsigned char *data = &stream->data[offset]; if (*data == CBOR_FLOAT32) { *val = ntohf(*(uint32_t *)(data + 1)); return 5; } return 0; } size_t cbor_serialize_float(cbor_stream_t *s, float val) { CBOR_ENSURE_SIZE(s, 5); s->data[s->pos++] = CBOR_FLOAT32; uint32_t encoded_val = htonf(val); memcpy(s->data + s->pos, &encoded_val, 4); s->pos += 4; return 5; } size_t cbor_deserialize_double(const cbor_stream_t *stream, size_t offset, double *val) { if (CBOR_TYPE(stream, offset) != CBOR_7 || !val) { return 0; } unsigned char *data = &stream->data[offset]; if (*data == CBOR_FLOAT64) { *val = ntohd(*(uint64_t *)(data + 1)); return 9; } return 0; } size_t cbor_serialize_double(cbor_stream_t *s, double val) { CBOR_ENSURE_SIZE(s, 9); s->data[s->pos++] = CBOR_FLOAT64; uint64_t encoded_val = htond(val); memcpy(s->data + s->pos, &encoded_val, 8); s->pos += 8; return 9; } #endif /* CBOR_NO_FLOAT */ size_t cbor_deserialize_byte_string(const cbor_stream_t *stream, size_t offset, char *val, size_t length) { if (CBOR_TYPE(stream, offset) != CBOR_BYTES) { return 0; } return decode_bytes(stream, offset, val, length); } size_t cbor_serialize_byte_string(cbor_stream_t *stream, const char *val) { return encode_bytes(CBOR_BYTES, stream, val, strlen(val)); } size_t cbor_serialize_byte_stringl(cbor_stream_t *stream, const char *val, size_t length) { return encode_bytes(CBOR_BYTES, stream, val, length); } size_t cbor_deserialize_unicode_string(const cbor_stream_t *stream, size_t offset, char *val, size_t length) { if (CBOR_TYPE(stream, offset) != CBOR_TEXT) { return 0; } return decode_bytes(stream, offset, val, length); } size_t cbor_serialize_unicode_string(cbor_stream_t *stream, const char *val) { return encode_bytes(CBOR_TEXT, stream, val, strlen(val)); } size_t cbor_deserialize_array(const cbor_stream_t *s, size_t offset, size_t *array_length) { if (CBOR_TYPE(s, offset) != CBOR_ARRAY || !array_length) { return 0; } uint64_t val; size_t read_bytes = decode_int(s, offset, &val); *array_length = (size_t)val; return read_bytes; } size_t cbor_serialize_array(cbor_stream_t *s, size_t array_length) { /* serialize number of array items */ return encode_int(CBOR_ARRAY, s, array_length); } size_t cbor_serialize_array_indefinite(cbor_stream_t *s) { CBOR_ENSURE_SIZE(s, 1); s->data[s->pos++] = CBOR_ARRAY | CBOR_VAR_FOLLOWS; return 1; } size_t cbor_deserialize_array_indefinite(const cbor_stream_t *s, size_t offset) { if (s->data[offset] != (CBOR_ARRAY | CBOR_VAR_FOLLOWS)) { return 0; } return 1; } size_t cbor_serialize_map_indefinite(cbor_stream_t *s) { CBOR_ENSURE_SIZE(s, 1); s->data[s->pos++] = CBOR_MAP | CBOR_VAR_FOLLOWS; return 1; } size_t cbor_deserialize_map_indefinite(const cbor_stream_t *s, size_t offset) { if (s->data[offset] != (CBOR_MAP | CBOR_VAR_FOLLOWS)) { return 0; } return 1; } size_t cbor_deserialize_map(const cbor_stream_t *s, size_t offset, size_t *map_length) { if (CBOR_TYPE(s, offset) != CBOR_MAP || !map_length) { return 0; } uint64_t val; size_t read_bytes = decode_int(s, offset, &val); *map_length = (size_t)val; return read_bytes; } size_t cbor_serialize_map(cbor_stream_t *s, size_t map_length) { /* serialize number of item key-value pairs */ return encode_int(CBOR_MAP, s, map_length); } #ifndef CBOR_NO_SEMANTIC_TAGGING #ifndef CBOR_NO_CTIME size_t cbor_deserialize_date_time(const cbor_stream_t *stream, size_t offset, struct tm *val) { if ((CBOR_TYPE(stream, offset) != CBOR_TAG) || (CBOR_ADDITIONAL_INFO(stream, offset) != CBOR_DATETIME_STRING_FOLLOWS)) { return 0; } char buffer[21]; offset++; /* skip tag byte to decode date_time */ size_t read_bytes = cbor_deserialize_unicode_string(stream, offset, buffer, sizeof(buffer)); const char *format = "%Y-%m-%dT%H:%M:%SZ"; if (strptime(buffer, format, val) == 0) { return 0; } val->tm_isdst = -1; /* not set by strptime(), undefined in CBOR */ if (mktime(val) == -1) { return 0; } return read_bytes + 1; /* + 1 tag byte */ } size_t cbor_serialize_date_time(cbor_stream_t *stream, struct tm *val) { CBOR_ENSURE_SIZE(stream, MAX_TIMESTRING_LENGTH + 1); /* + 1 tag byte */ char time_str[MAX_TIMESTRING_LENGTH]; const char *format = "%Y-%m-%dT%H:%M:%SZ"; if (strftime(time_str, sizeof(time_str), format, val) == 0) { /* struct tm to string */ return 0; } if (!cbor_write_tag(stream, CBOR_DATETIME_STRING_FOLLOWS)) { return 0; } size_t written_bytes = cbor_serialize_unicode_string(stream, time_str); return written_bytes + 1; /* utf8 time string length + tag length */ } size_t cbor_deserialize_date_time_epoch(const cbor_stream_t *stream, size_t offset, time_t *val) { if ((CBOR_TYPE(stream, offset) != CBOR_TAG) || (CBOR_ADDITIONAL_INFO(stream, offset) != CBOR_DATETIME_EPOCH_FOLLOWS)) { return 0; } offset++; /* skip tag byte */ uint64_t epoch; size_t read_bytes = cbor_deserialize_uint64_t(stream, offset, &epoch); if (!read_bytes) { return 0; } *val = (time_t)epoch; return read_bytes + 1; /* + 1 tag byte */ } size_t cbor_serialize_date_time_epoch(cbor_stream_t *stream, time_t val) { /* we need at least 2 bytes (tag byte + at least 1 byte for the integer) */ CBOR_ENSURE_SIZE(stream, 2); if (val < 0) { return 0; /* we currently don't support negative values for the time_t object */ } if (!cbor_write_tag(stream, CBOR_DATETIME_EPOCH_FOLLOWS)) { return 0; } uint64_t time = (uint64_t)val; size_t written_bytes = encode_int(CBOR_UINT, stream, time); return written_bytes + 1; /* + 1 tag byte */ } #endif /* CBOR_NO_CTIME */ size_t cbor_write_tag(cbor_stream_t *s, unsigned char tag) { CBOR_ENSURE_SIZE(s, 1); s->data[s->pos++] = CBOR_TAG | tag; return 1; } bool cbor_at_tag(const cbor_stream_t *s, size_t offset) { return cbor_at_end(s, offset) || CBOR_TYPE(s, offset) == CBOR_TAG; } #endif /* CBOR_NO_SEMANTIC_TAGGING */ size_t cbor_write_break(cbor_stream_t *s) { CBOR_ENSURE_SIZE(s, 1); s->data[s->pos++] = CBOR_BREAK; return 1; } bool cbor_at_break(const cbor_stream_t *s, size_t offset) { return cbor_at_end(s, offset) || s->data[offset] == CBOR_BREAK; } bool cbor_at_end(const cbor_stream_t *s, size_t offset) { /* cbor_stream_t::pos points at the next *free* byte, hence the -1 */ return s ? offset >= s->pos - 1 : true; } #ifndef CBOR_NO_PRINT /* BEGIN: Printers */ void cbor_stream_print(const cbor_stream_t *stream) { dump_memory(stream->data, stream->pos); } /** * Skip byte(s) at offset @p offset in stream @p stream * * This function can be used as fallback, in case we cannot deserialize the * current byte */ static size_t cbor_stream_decode_skip(cbor_stream_t *stream, size_t offset) { size_t consume_bytes = 0; switch (CBOR_ADDITIONAL_INFO(stream, offset)) { case CBOR_BYTE_FOLLOWS: consume_bytes = 2; break; default: consume_bytes = 1; break; } DEBUG("(unsupported, "); dump_memory(stream->data + offset, consume_bytes); DEBUG(")\n"); return consume_bytes; } /** * Decode CBOR data item from @p stream at position @p offset * * @return Amount of bytes consumed */ static size_t cbor_stream_decode_at(cbor_stream_t *stream, size_t offset, int indent) { #define DESERIALIZE_AND_PRINT(type, suffix, format_string) { \ type val; \ size_t read_bytes = cbor_deserialize_##suffix(stream, offset, &val); \ DEBUG("("#type", "format_string")\n", val); \ return read_bytes; \ } DEBUG("%*s", indent, ""); switch (CBOR_TYPE(stream, offset)) { case CBOR_UINT: DESERIALIZE_AND_PRINT(uint64_t, uint64_t, "%" PRIu64) case CBOR_NEGINT: DESERIALIZE_AND_PRINT(int64_t, int64_t, "%" PRId64) case CBOR_BYTES: { char buffer[CBOR_STREAM_PRINT_BUFFERSIZE]; size_t read_bytes = cbor_deserialize_byte_string(stream, offset, buffer, sizeof(buffer)); DEBUG("(byte string, \"%s\")\n", buffer); return read_bytes; } case CBOR_TEXT: { char buffer[CBOR_STREAM_PRINT_BUFFERSIZE]; size_t read_bytes = cbor_deserialize_unicode_string(stream, offset, buffer, sizeof(buffer)); DEBUG("(unicode string, \"%s\")\n", buffer); return read_bytes; } case CBOR_ARRAY: { const bool is_indefinite = (stream->data[offset] == (CBOR_ARRAY | CBOR_VAR_FOLLOWS)); uint64_t array_length; size_t read_bytes; if (is_indefinite) { offset += read_bytes = cbor_deserialize_array_indefinite(stream, offset); DEBUG("(array, length: [indefinite])\n"); } else { offset += read_bytes = decode_int(stream, offset, &array_length); DEBUG("(array, length: %"PRIu64")\n", array_length); } size_t i = 0; while (is_indefinite ? !cbor_at_break(stream, offset) : i < array_length) { size_t inner_read_bytes; offset += inner_read_bytes = cbor_stream_decode_at(stream, offset, indent + 2); if (inner_read_bytes == 0) { DEBUG("Failed to read array item at position %d\n", i); break; } read_bytes += inner_read_bytes; ++i; } read_bytes += cbor_at_break(stream, offset); return read_bytes; } case CBOR_MAP: { const bool is_indefinite = (stream->data[offset] == (CBOR_MAP | CBOR_VAR_FOLLOWS)); uint64_t map_length; size_t read_bytes; if (is_indefinite) { offset += read_bytes = cbor_deserialize_map_indefinite(stream, offset); DEBUG("(map, length: [indefinite])\n"); } else { offset += read_bytes = decode_int(stream, offset, &map_length); DEBUG("(map, length: %"PRIu64")\n", map_length); } size_t i = 0; while (is_indefinite ? !cbor_at_break(stream, offset) : i < map_length) { size_t key_read_bytes, value_read_bytes; offset += key_read_bytes = cbor_stream_decode_at(stream, offset, indent + 1); /* key */ offset += value_read_bytes = cbor_stream_decode_at(stream, offset, indent + 2); /* value */ if (key_read_bytes == 0 || value_read_bytes == 0) { DEBUG("Failed to read key-value pair at position %d\n", i); break; } read_bytes += key_read_bytes + value_read_bytes; ++i; } read_bytes += cbor_at_break(stream, offset); return read_bytes; } case CBOR_TAG: { unsigned char tag = CBOR_ADDITIONAL_INFO(stream, offset); switch (tag) { /* Non-native builds likely don't have support for ctime (hence disable it there) * TODO: Better check for availability of ctime functions? */ #ifndef CBOR_NO_CTIME case CBOR_DATETIME_STRING_FOLLOWS: { char buf[64]; struct tm timeinfo; size_t read_bytes = cbor_deserialize_date_time(stream, offset, &timeinfo); strftime(buf, sizeof(buf), "%c", &timeinfo); DEBUG("(tag: %u, date/time string: \"%s\")\n", tag, buf); return read_bytes; } case CBOR_DATETIME_EPOCH_FOLLOWS: { time_t time; size_t read_bytes = cbor_deserialize_date_time_epoch(stream, offset, &time); DEBUG("(tag: %u, date/time epoch: %d)\n", tag, (int)time); return read_bytes; } #endif /* CBOR_NO_CTIME */ default: break; } } case CBOR_7: { switch (stream->data[offset]) { case CBOR_FALSE: case CBOR_TRUE: DESERIALIZE_AND_PRINT(bool, bool, "%d") #ifndef CBOR_NO_FLOAT case CBOR_FLOAT16: DESERIALIZE_AND_PRINT(float, float_half, "%f") case CBOR_FLOAT32: DESERIALIZE_AND_PRINT(float, float, "%f") case CBOR_FLOAT64: DESERIALIZE_AND_PRINT(double, double, "%lf") #endif /* CBOR_NO_FLOAT */ default: break; } } } /* if we end up here, we weren't able to parse the current byte * let's just skip this (and the next one as well if required) */ return cbor_stream_decode_skip(stream, offset); #undef DESERIALIZE_AND_PRINT } void cbor_stream_decode(cbor_stream_t *stream) { DEBUG("Data:\n"); size_t offset = 0; while (offset < stream->pos) { size_t read_bytes = cbor_stream_decode_at(stream, offset, 0); if (read_bytes == 0) { DEBUG("Failed to read from stream at offset %d, start byte 0x%02X\n", offset, stream->data[offset]); cbor_stream_print(stream); return; } offset += read_bytes; } DEBUG("\n"); } #endif /* CBOR_NO_PRINT */ /* END: Printers */