#include "serialization.h"
#include <string.h>
#include "cbor/arrays.h"
#include "cbor/bytestrings.h"
#include "cbor/floats_ctrls.h"
#include "cbor/ints.h"
#include "cbor/maps.h"
#include "cbor/strings.h"
#include "cbor/tags.h"
#include "encoding.h"
#include "internal/memory_utils.h"
size_t cbor_serialize(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT:
return cbor_serialize_uint(item, buffer, buffer_size);
case CBOR_TYPE_NEGINT:
return cbor_serialize_negint(item, buffer, buffer_size);
case CBOR_TYPE_BYTESTRING:
return cbor_serialize_bytestring(item, buffer, buffer_size);
case CBOR_TYPE_STRING:
return cbor_serialize_string(item, buffer, buffer_size);
case CBOR_TYPE_ARRAY:
return cbor_serialize_array(item, buffer, buffer_size);
case CBOR_TYPE_MAP:
return cbor_serialize_map(item, buffer, buffer_size);
case CBOR_TYPE_TAG:
return cbor_serialize_tag(item, buffer, buffer_size);
case CBOR_TYPE_FLOAT_CTRL:
return cbor_serialize_float_ctrl(item, buffer, buffer_size);
}
}
const uint64_t kMaxEmbeddedInt = 23;
size_t _cbor_encoded_header_size(uint64_t size) {
if (size <= kMaxEmbeddedInt)
return 1;
else if (size <= UINT8_MAX)
return 2;
else if (size <= UINT16_MAX)
return 3;
else if (size <= UINT32_MAX)
return 5;
else
return 9;
}
size_t cbor_serialized_size(const cbor_item_t *item) {
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT:
case CBOR_TYPE_NEGINT:
switch (cbor_int_get_width(item)) {
case CBOR_INT_8:
if (cbor_get_uint8(item) <= kMaxEmbeddedInt) return 1;
return 2;
case CBOR_INT_16:
return 3;
case CBOR_INT_32:
return 5;
case CBOR_INT_64:
return 9;
}
case CBOR_TYPE_BYTESTRING: {
if (cbor_bytestring_is_definite(item)) {
size_t header_size =
_cbor_encoded_header_size(cbor_bytestring_length(item));
if (cbor_bytestring_length(item) == 0) return header_size;
return _cbor_safe_signaling_add(header_size,
cbor_bytestring_length(item));
}
size_t indef_bytestring_size = 2;
cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++) {
indef_bytestring_size = _cbor_safe_signaling_add(
indef_bytestring_size, cbor_serialized_size(chunks[i]));
}
return indef_bytestring_size;
}
case CBOR_TYPE_STRING: {
if (cbor_string_is_definite(item)) {
size_t header_size =
_cbor_encoded_header_size(cbor_string_length(item));
if (cbor_string_length(item) == 0) return header_size;
return _cbor_safe_signaling_add(header_size, cbor_string_length(item));
}
size_t indef_string_size = 2;
cbor_item_t **chunks = cbor_string_chunks_handle(item);
for (size_t i = 0; i < cbor_string_chunk_count(item); i++) {
indef_string_size = _cbor_safe_signaling_add(
indef_string_size, cbor_serialized_size(chunks[i]));
}
return indef_string_size;
}
case CBOR_TYPE_ARRAY: {
size_t array_size = cbor_array_is_definite(item)
? _cbor_encoded_header_size(cbor_array_size(item))
: 2;
cbor_item_t **items = cbor_array_handle(item);
for (size_t i = 0; i < cbor_array_size(item); i++) {
array_size = _cbor_safe_signaling_add(array_size,
cbor_serialized_size(items[i]));
}
return array_size;
}
case CBOR_TYPE_MAP: {
size_t map_size = cbor_map_is_definite(item)
? _cbor_encoded_header_size(cbor_map_size(item))
: 2;
struct cbor_pair *items = cbor_map_handle(item);
for (size_t i = 0; i < cbor_map_size(item); i++) {
map_size = _cbor_safe_signaling_add(
map_size,
_cbor_safe_signaling_add(cbor_serialized_size(items[i].key),
cbor_serialized_size(items[i].value)));
}
return map_size;
}
case CBOR_TYPE_TAG: {
return _cbor_safe_signaling_add(
_cbor_encoded_header_size(cbor_tag_value(item)),
cbor_serialized_size(cbor_move(cbor_tag_item(item))));
}
case CBOR_TYPE_FLOAT_CTRL:
switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0:
return _cbor_encoded_header_size(cbor_ctrl_value(item));
case CBOR_FLOAT_16:
return 3;
case CBOR_FLOAT_32:
return 5;
case CBOR_FLOAT_64:
return 9;
}
}
}
size_t cbor_serialize_alloc(const cbor_item_t *item, unsigned char **buffer,
size_t *buffer_size) {
*buffer = NULL;
size_t serialized_size = cbor_serialized_size(item);
if (serialized_size == 0) {
if (buffer_size != NULL) *buffer_size = 0;
return 0;
}
*buffer = _cbor_malloc(serialized_size);
if (*buffer == NULL) {
if (buffer_size != NULL) *buffer_size = 0;
return 0;
}
size_t written = cbor_serialize(item, *buffer, serialized_size);
CBOR_ASSERT(written == serialized_size);
if (buffer_size != NULL) *buffer_size = serialized_size;
return written;
}
size_t cbor_serialize_uint(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_uint(item));
switch (cbor_int_get_width(item)) {
case CBOR_INT_8:
return cbor_encode_uint8(cbor_get_uint8(item), buffer, buffer_size);
case CBOR_INT_16:
return cbor_encode_uint16(cbor_get_uint16(item), buffer, buffer_size);
case CBOR_INT_32:
return cbor_encode_uint32(cbor_get_uint32(item), buffer, buffer_size);
case CBOR_INT_64:
return cbor_encode_uint64(cbor_get_uint64(item), buffer, buffer_size);
}
}
size_t cbor_serialize_negint(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_negint(item));
switch (cbor_int_get_width(item)) {
case CBOR_INT_8:
return cbor_encode_negint8(cbor_get_uint8(item), buffer, buffer_size);
case CBOR_INT_16:
return cbor_encode_negint16(cbor_get_uint16(item), buffer, buffer_size);
case CBOR_INT_32:
return cbor_encode_negint32(cbor_get_uint32(item), buffer, buffer_size);
case CBOR_INT_64:
return cbor_encode_negint64(cbor_get_uint64(item), buffer, buffer_size);
}
}
size_t cbor_serialize_bytestring(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_bytestring(item));
if (cbor_bytestring_is_definite(item)) {
size_t length = cbor_bytestring_length(item);
size_t written = cbor_encode_bytestring_start(length, buffer, buffer_size);
if (written > 0 && (buffer_size - written >= length)) {
memcpy(buffer + written, cbor_bytestring_handle(item), length);
return written + length;
}
return 0;
} else {
CBOR_ASSERT(cbor_bytestring_is_indefinite(item));
size_t chunk_count = cbor_bytestring_chunk_count(item);
size_t written = cbor_encode_indef_bytestring_start(buffer, buffer_size);
if (written == 0) return 0;
cbor_item_t **chunks = cbor_bytestring_chunks_handle(item);
for (size_t i = 0; i < chunk_count; i++) {
size_t chunk_written = cbor_serialize_bytestring(
chunks[i], buffer + written, buffer_size - written);
if (chunk_written == 0) return 0;
written += chunk_written;
}
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
if (break_written == 0) return 0;
return written + break_written;
}
}
size_t cbor_serialize_string(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_string(item));
if (cbor_string_is_definite(item)) {
size_t length = cbor_string_length(item);
size_t written = cbor_encode_string_start(length, buffer, buffer_size);
if (written && (buffer_size - written >= length)) {
memcpy(buffer + written, cbor_string_handle(item), length);
return written + length;
}
return 0;
} else {
CBOR_ASSERT(cbor_string_is_indefinite(item));
size_t chunk_count = cbor_string_chunk_count(item);
size_t written = cbor_encode_indef_string_start(buffer, buffer_size);
if (written == 0) return 0;
cbor_item_t **chunks = cbor_string_chunks_handle(item);
for (size_t i = 0; i < chunk_count; i++) {
size_t chunk_written = cbor_serialize_string(chunks[i], buffer + written,
buffer_size - written);
if (chunk_written == 0) return 0;
written += chunk_written;
}
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
if (break_written == 0) return 0;
return written + break_written;
}
}
size_t cbor_serialize_array(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_array(item));
size_t size = cbor_array_size(item), written = 0;
cbor_item_t **handle = cbor_array_handle(item);
if (cbor_array_is_definite(item)) {
written = cbor_encode_array_start(size, buffer, buffer_size);
} else {
CBOR_ASSERT(cbor_array_is_indefinite(item));
written = cbor_encode_indef_array_start(buffer, buffer_size);
}
if (written == 0) return 0;
for (size_t i = 0; i < size; i++) {
size_t item_written =
cbor_serialize(*(handle++), buffer + written, buffer_size - written);
if (item_written == 0) return 0;
written += item_written;
}
if (cbor_array_is_definite(item)) {
return written;
} else {
CBOR_ASSERT(cbor_array_is_indefinite(item));
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
if (break_written == 0) return 0;
return written + break_written;
}
}
size_t cbor_serialize_map(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_map(item));
size_t size = cbor_map_size(item), written = 0;
struct cbor_pair *handle = cbor_map_handle(item);
if (cbor_map_is_definite(item)) {
written = cbor_encode_map_start(size, buffer, buffer_size);
} else {
CBOR_ASSERT(cbor_map_is_indefinite(item));
written = cbor_encode_indef_map_start(buffer, buffer_size);
}
if (written == 0) return 0;
for (size_t i = 0; i < size; i++) {
size_t item_written =
cbor_serialize(handle->key, buffer + written, buffer_size - written);
if (item_written == 0) {
return 0;
}
written += item_written;
item_written = cbor_serialize((handle++)->value, buffer + written,
buffer_size - written);
if (item_written == 0) return 0;
written += item_written;
}
if (cbor_map_is_definite(item)) {
return written;
} else {
CBOR_ASSERT(cbor_map_is_indefinite(item));
size_t break_written =
cbor_encode_break(buffer + written, buffer_size - written);
if (break_written == 0) return 0;
return written + break_written;
}
}
size_t cbor_serialize_tag(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_tag(item));
size_t written = cbor_encode_tag(cbor_tag_value(item), buffer, buffer_size);
if (written == 0) return 0;
size_t item_written = cbor_serialize(cbor_move(cbor_tag_item(item)),
buffer + written, buffer_size - written);
if (item_written == 0) return 0;
return written + item_written;
}
size_t cbor_serialize_float_ctrl(const cbor_item_t *item, unsigned char *buffer,
size_t buffer_size) {
CBOR_ASSERT(cbor_isa_float_ctrl(item));
switch (cbor_float_get_width(item)) {
case CBOR_FLOAT_0:
return cbor_encode_ctrl(cbor_ctrl_value(item), buffer, buffer_size);
case CBOR_FLOAT_16:
return cbor_encode_half(cbor_float_get_float2(item), buffer, buffer_size);
case CBOR_FLOAT_32:
return cbor_encode_single(cbor_float_get_float4(item), buffer,
buffer_size);
case CBOR_FLOAT_64:
return cbor_encode_double(cbor_float_get_float8(item), buffer,
buffer_size);
}
}