template-ify the cbor stuff

custom
jacqueline 2 years ago
parent 9176ef1872
commit dfa9ab6e04
  1. 43
      src/audio/chunk.cpp
  2. 63
      src/cbor/cbor_decoder.cpp
  3. 92
      src/cbor/include/cbor_decoder.hpp

@ -10,18 +10,22 @@
namespace audio { namespace audio {
/* /*
* The maximum size that we expect a header to take up. * The amount of space to allocate for the first chunk's header. After the first
* chunk, we have a more concrete idea of the header's size and can allocate
* space for future headers more compactly.
*/ */
// TODO: tune this. // TODO: measure how big headers tend to be to pick a better value.
static const size_t kMaxHeaderSize = 64; static const size_t kInitialHeaderSize = 32;
auto WriteChunksToStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeWriteResult { auto WriteChunksToStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeWriteResult {
size_t header_size = kInitialHeaderSize;
while (1) { while (1) {
// First, ask the callback for some data to write. // First, ask the callback for some data to write.
size_t chunk_size = size_t chunk_size =
callback( callback(
working_buffer + kMaxHeaderSize, working_buffer + header_size,
working_buffer_length - kMaxHeaderSize); working_buffer_length - header_size);
if (chunk_size == 0) { if (chunk_size == 0) {
// They had nothing for us, so bail out. // They had nothing for us, so bail out.
@ -31,22 +35,28 @@ auto WriteChunksToStream(MessageBufferHandle_t *stream, uint8_t *working_buffer,
// Put together a header. // Put together a header.
cbor::Encoder encoder(cbor::CONTAINER_ARRAY, 3, working_buffer, working_buffer_length); cbor::Encoder encoder(cbor::CONTAINER_ARRAY, 3, working_buffer, working_buffer_length);
encoder.WriteUnsigned(TYPE_CHUNK_HEADER); encoder.WriteUnsigned(TYPE_CHUNK_HEADER);
// Note here that we need to write the offset of the chunk into the header. encoder.WriteUnsigned(header_size);
// We could be smarter here and write the actual header size, allowing us to
// pack slightly more data into each message, but this is hard so I haven't
// done it. Please make my code better for me.
encoder.WriteUnsigned(kMaxHeaderSize);
encoder.WriteUnsigned(chunk_size); encoder.WriteUnsigned(chunk_size);
if (encoder.Finish().has_error()) {
size_t new_header_size = header_size;
cpp::result<size_t, CborError> encoder_res = encoder.Finish();
if (encoder_res.has_error()) {
return CHUNK_ENCODING_ERROR; return CHUNK_ENCODING_ERROR;
}; } else {
// We can now tune the space to allocate for the header to be closer to
// its actual size. We pad this by 2 bytes to allow extra space for the
// chunk size and header size fields to each spill over into another byte
// each.
new_header_size = encoder_res.value() + 2;
}
// Try to write to the buffer. Note the return type here will be either 0 or // Try to write to the buffer. Note the return type here will be either 0 or
// kMaxHeaderSize + chunk_size, as MessageBuffer doesn't allow partial // header_size + chunk_size, as MessageBuffer doesn't allow partial writes.
// writes.
size_t actual_write_size = size_t actual_write_size =
xMessageBufferSend( xMessageBufferSend(
*stream, working_buffer, kMaxHeaderSize + chunk_size, max_wait); *stream, working_buffer, header_size + chunk_size, max_wait);
header_size = new_header_size;
if (actual_write_size == 0) { if (actual_write_size == 0) {
// We failed to write in time, so bail out. This is techinically data loss // We failed to write in time, so bail out. This is techinically data loss
@ -84,8 +94,7 @@ auto ReadChunksFromStream(MessageBufferHandle_t *stream, uint8_t *working_buffer
return CHUNK_STREAM_ENDED; return CHUNK_STREAM_ENDED;
} }
// Work the size and position of the chunk (don't assume it's at // Work the size and position of the chunk.
// kMaxHeaderSize offset for future-proofing).
header_length = decoder.ParseUnsigned().value_or(0); header_length = decoder.ParseUnsigned().value_or(0);
chunk_length = decoder.ParseUnsigned().value_or(0); chunk_length = decoder.ParseUnsigned().value_or(0);
if (decoder.Failed()) { if (decoder.Failed()) {

@ -18,68 +18,6 @@ static auto ArrayDecoder::Create(uint8_t *buffer, size_t buffer_len) -> cpp::res
return std::move(decoder); return std::move(decoder);
} }
auto ArrayDecoder::ParseString() -> cpp::result<std::string, CborError> {
if (error_ != CborNoError) {
return cpp::fail(error_);
}
if (!cbor_value_is_byte_string(&it_)) {
error_ = CborErrorIllegalType;
return cpp::fail(error_);
}
uint8_t *buf; size_t len; CborValue new_val;
error_ = cbor_value_dup_byte_string(&it_, &buf, &len, &new_val);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
std::string ret(buf, len);
free(buf);
val_ = new_val;
return ret;
}
auto ArrayDecoder::ParseUnsigned() -> cpp::result<uint32_t, CborError> {
if (error_ != CborNoError) {
return cpp::fail(error_);
}
if (!cbor_value_is_unsigned_integer(&it_)) {
error_ = CborErrorIllegalType;
return cpp::fail(error_);
}
uint64_t ret;
error_ = cbor_value_get_uint64(&it_, &ret);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
error_ = cbor_value_advance(&it_);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
return ret;
}
auto ArrayDecoder::ParseSigned() -> cpp::result<int32_t, CborError> {
if (error_ != CborNoError) {
return cpp::fail(error_);
}
if (!cbor_value_is_unsigned_integer(&it_)) {
error_ = CborErrorIllegalType;
return cpp::fail(error_);
}
uint64_t ret;
error_ = cbor_value_get_uint64(&it_, &ret);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
error_ = cbor_value_advance(&it_);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
return ret;
}
static auto MapDecoder::Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<MapDecoder>, CborError> { static auto MapDecoder::Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<MapDecoder>, CborError> {
auto decoder = std::make_unique<MapDecoder>(); auto decoder = std::make_unique<MapDecoder>();
cbor_parser_init(buffer, buffer_len, &decoder->parser_, &decoder->root_); cbor_parser_init(buffer, buffer_len, &decoder->parser_, &decoder->root_);
@ -141,6 +79,7 @@ auto MapDecoder::FindSigned(const std::string &key) -> std::optional<int32_t> {
return {}; return {};
} }
if (cbor_value_map_find_value(&it_, key.c_str(), &val) != CborNoError) { if (cbor_value_map_find_value(&it_, key.c_str(), &val) != CborNoError) {
// Don't store this as an error; missing keys are fine.
return {}; return {};
} }
if (!cbor_value_is_integer(&val)) { if (!cbor_value_is_integer(&val)) {

@ -1,15 +1,61 @@
#pragma once #pragma once
#include <stdint.h>
#include <cstdint> #include <cstdint>
namespace cbor { namespace cbor {
static auto parse_stdstring(CborValue *val, std::string *out) -> CborError {
uint8_t *buf; size_t len;
CborError err = cbor_value_dup_byte_string(val, &buf, &len, NULL);
if (err != CborNoError) {
return err;
}
*out = std::move(std::string(buf, len));
free(buf);
return err
}
class ArrayDecoder { class ArrayDecoder {
public: public:
static auto Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<ArrayDecoder>, CborError>; static auto Create(uint8_t *buffer, size_t buffer_len)
-> cpp::result<std::unique_ptr<ArrayDecoder>, CborError>;
template<typename T>
auto NextValue() -> cpp::result<T, CborError>;
auto ParseString() -> cpp::result<std::string, CborError>; template<> auto NextValue() -> cpp::result<int64_t, CborError> {
auto ParseUnsigned() -> cpp::result<uint32_t, CborError>; return NextValue(&cbor_value_is_integer, &cbor_value_get_int);
auto ParseSigned() -> cpp::result<int32_t, CborError>; }
template<> auto NextValue() -> cpp::result<uint64_t, CborError> {
return NextValue(&cbor_value_is_unsigned_integer, &cbor_value_get_uint64);
}
template<> auto NextValue() -> cpp::result<std::string, CborError> {
return NextValue(&cbor_value_is_byte_string, &parse_stdstring);
}
template <typename T>
auto NextValue(
bool(*is_valid)(CborValue*),
CborError(*parse)(CborValue*, T*)) -> cpp::result<T, CborError> {
if (error_ != CborNoError) {
return cpp::fail(error_);
}
if (!is_valid(&it_)) {
error_ = CborErrorIllegalType;
return cpp::fail(error_);
}
T ret;
error_ = parse(&it_, &ret);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
error_ = cbor_value_advance(&it_);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
return ret;
}
auto Failed() -> CborError { return error_; } auto Failed() -> CborError { return error_; }
@ -27,9 +73,41 @@ namespace cbor {
public: public:
static auto Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<MapDecoder>, CborError>; static auto Create(uint8_t *buffer, size_t buffer_len) -> cpp::result<std::unique_ptr<MapDecoder>, CborError>;
auto FindString(const std::string &key) -> std::optional<std::string>; template<typename T>
auto FindUnsigned(const std::string &key) -> std::optional<uint32_t>; auto FindValue(const std::string &key) -> std::optional<T>;
auto FindSigned(const std::string &key) -> std::optional<int32_t>;
template<> auto FindValue(const std::string &key) -> std::optional<int64_t> {
return FindValue(key, &cbor_value_is_integer, &cbor_value_get_int);
}
template<> auto FindValue(const std::string &key) -> std::optional<uint64_t> {
return FindValue(key, &cbor_value_is_unsigned_integer, &cbor_value_get_uint64);
}
template<> auto FindValue(const std::string &key) -> std::optional<std::string> {
return FindValue(key, &cbor_value_is_byte_string, &parse_stdstring);
}
template <typename T>
auto FindValue(
const std::string &key,
bool(*is_valid)(CborValue*),
CborError(*parse)(CborValue*, T*)) -> std::optional<T> {
if (error_ != CborNoError) {
return {};
}
if (cbor_value_map_find_value(&it_, key.c_str(), &val) != CborNoError) {
return {};
}
if (!is_valid(&val)) {
error_ = CborErrorIllegalType;
return {};
}
T ret;
error_ = parse(&val, &ret);
if (error_ != CborNoError) {
return cpp::fail(error_);
}
return ret;
}
auto Failed() -> CborError { return error_; } auto Failed() -> CborError { return error_; }

Loading…
Cancel
Save