diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index 32ca1a56..d5564f13 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -1,7 +1,7 @@ idf_component_register( SRCS "audio_decoder.cpp" "audio_task.cpp" "chunk.cpp" "fatfs_audio_input.cpp" - "stream_info.cpp" + "stream_info.cpp" "stream_message.cpp" INCLUDE_DIRS "include" - REQUIRES "codecs" "drivers" "cbor_wrapper" "result" "tasks") + REQUIRES "codecs" "drivers" "cbor" "result" "tasks") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp index a19fd5bf..48637f08 100644 --- a/src/audio/audio_decoder.cpp +++ b/src/audio/audio_decoder.cpp @@ -39,8 +39,8 @@ auto AudioDecoder::SetOutputBuffer(MessageBufferHandle_t* buffer) -> void { output_buffer_ = buffer; } -auto AudioDecoder::ProcessStreamInfo(StreamInfo&& info) - -> cpp::result { +auto AudioDecoder::ProcessStreamInfo(StreamInfo& info) + -> cpp::result { stream_info_ = info; // Reuse the existing codec if we can. This will help with gapless playback, @@ -62,7 +62,7 @@ auto AudioDecoder::ProcessStreamInfo(StreamInfo&& info) } auto AudioDecoder::ProcessChunk(uint8_t* data, std::size_t length) - -> cpp::result { + -> cpp::result { if (current_codec_ == nullptr) { // Should never happen, but fail explicitly anyway. return cpp::fail(UNSUPPORTED_STREAM); @@ -111,7 +111,7 @@ auto AudioDecoder::ProcessChunk(uint8_t* data, std::size_t length) return current_codec_->GetInputPosition(); } -auto AudioDecoder::ProcessIdle() -> cpp::result { +auto AudioDecoder::ProcessIdle() -> cpp::result { // Not used; we delay forever when waiting on IO. return {}; } diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index f3362897..9ffcab74 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -4,9 +4,7 @@ #include -#include "cbor_decoder.hpp" -#include "chunk.hpp" -#include "esp-idf/components/cbor/tinycbor/src/cbor.h" +#include "cbor.h" #include "esp_heap_caps.h" #include "freertos/portmacro.h" #include "freertos/queue.h" @@ -14,6 +12,7 @@ #include "audio_element.hpp" #include "chunk.hpp" +#include "stream_info.hpp" #include "stream_message.hpp" #include "tasks.hpp" @@ -39,7 +38,7 @@ void AudioTaskMain(void* args) { ChunkReader chunk_reader = ChunkReader(element->InputBuffer()); while (1) { - cpp::result process_res; + cpp::result process_res; // If this element has an input stream, then our top priority is // processing any chunks from it. Try doing this first, then fall back to @@ -67,26 +66,15 @@ void AudioTaskMain(void* args) { if (has_received_message) { auto [buffer, length] = chunk_reader.GetLastMessage(); - auto decoder_res = cbor::ArrayDecoder::Create(buffer, length); - if (decoder_res.has_error()) { - // TODO. - break; - } - auto decoder = std::move(decoder_res.value()); - // TODO: this can be more elegant i think - cpp::result message_type = - decoder->NextValue(); - if (message_type.has_error()) { - break; // TODO. - } else if (message_type.value() == TYPE_STREAM_INFO) { - auto info_decoder = cbor::MapDecoder::Create(decoder->Iterator()); - if (info_decoder.has_value()) { - auto process_error = element->ProcessStreamInfo( - StreamInfo(info_decoder.value().get())); - if (process_error.has_error()) { - break; // TODO. - } - } else { + MessageType msg = ReadMessageType(buffer, length); + if (msg == TYPE_STREAM_INFO) { + auto parse_res = + ReadMessage(&StreamInfo::Parse, buffer, length); + if (parse_res.has_error()) { + break; // TODO. + } + auto info_res = element->ProcessStreamInfo(parse_res.value()); + if (info_res.has_error()) { break; // TODO. } } diff --git a/src/audio/chunk.cpp b/src/audio/chunk.cpp index 718b2649..94618ab1 100644 --- a/src/audio/chunk.cpp +++ b/src/audio/chunk.cpp @@ -1,14 +1,12 @@ #include "chunk.hpp" -#include - #include #include +#include +#include #include "cbor.h" -#include "cbor_decoder.hpp" -#include "cbor_encoder.hpp" #include "stream_message.hpp" namespace audio { @@ -25,7 +23,7 @@ static const std::size_t kWorkingBufferSize = kMaxChunkSize * 1.5; * space for future headers more compactly. */ // TODO: measure how big headers tend to be to pick a better value. -static const size_t kInitialHeaderSize = 32; +static const std::size_t kInitialHeaderSize = 32; auto WriteChunksToStream(MessageBufferHandle_t* stream, uint8_t* working_buffer, @@ -44,14 +42,20 @@ auto WriteChunksToStream(MessageBufferHandle_t* stream, } // Put together a header. - cbor::Encoder encoder(cbor::CONTAINER_ARRAY, 3, working_buffer, - working_buffer_length); - encoder.WriteValue(TYPE_CHUNK_HEADER); - encoder.WriteValue(header_size); - encoder.WriteValue(chunk_size); + cpp::result encoder_res; + CborEncoder arr; + WriteMessage( + TYPE_CHUNK_HEADER, + [&](CborEncoder& container) { + cbor_encoder_create_array(&container, &arr, 2); + cbor_encode_uint(&arr, header_size); + cbor_encode_uint(&arr, chunk_size); + cbor_encoder_close_container(&container, &arr); + return std::nullopt; + }, + working_buffer, working_buffer_length); size_t new_header_size = header_size; - cpp::result encoder_res = encoder.Finish(); if (encoder_res.has_error()) { return CHUNK_ENCODING_ERROR; } else { @@ -108,15 +112,9 @@ auto ChunkReader::ReadChunkFromStream( return CHUNK_READ_TIMEOUT; } - auto decoder = cbor::ArrayDecoder::Create(working_buffer_ + leftover_bytes_, - last_message_size_); - if (decoder.has_error()) { - // Weird; this implies someone is shoving invalid data into the buffer. - return CHUNK_DECODING_ERROR; - } + MessageType type = + ReadMessageType(working_buffer_ + leftover_bytes_, last_message_size_); - MessageType type = static_cast( - decoder.value()->NextValue().value_or(TYPE_UNKNOWN)); if (type != TYPE_CHUNK_HEADER) { // This message wasn't for us, so let the caller handle it. Reset(); @@ -124,11 +122,9 @@ auto ChunkReader::ReadChunkFromStream( } // Work the size and position of the chunk. - size_t header_length = decoder.value()->NextValue().value_or(0); - size_t chunk_length = decoder.value()->NextValue().value_or(0); - if (decoder.value()->Failed()) { - return CHUNK_DECODING_ERROR; - } + size_t header_length = 0, chunk_length = 0; + + // TODO: chunker header type to encapsulate this? // Now we need to stick the end of the last chunk (if it exists) onto the // front of the new chunk. Do it this way around bc we assume the old chunk diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp index dee090d0..d4cbf6db 100644 --- a/src/audio/fatfs_audio_input.cpp +++ b/src/audio/fatfs_audio_input.cpp @@ -10,6 +10,9 @@ #include "audio_element.hpp" #include "chunk.hpp" +#include "stream_message.hpp" + +static const char* kTag = "SRC"; namespace audio { @@ -43,14 +46,17 @@ FatfsAudioInput::~FatfsAudioInput() { free(output_buffer_); } -auto FatfsAudioInput::ProcessStreamInfo(StreamInfo&& info) - -> cpp::result { +auto FatfsAudioInput::ProcessStreamInfo(StreamInfo& info) + -> cpp::result { if (is_file_open_) { f_close(¤t_file_); is_file_open_ = false; } - std::string path = info.Path().value_or(""); + if (!info.Path()) { + return cpp::fail(UNSUPPORTED_STREAM); + } + std::string path = info.Path().value(); FRESULT res = f_open(¤t_file_, path.c_str(), FA_READ); if (res != FR_OK) { return cpp::fail(IO_ERROR); @@ -58,15 +64,24 @@ auto FatfsAudioInput::ProcessStreamInfo(StreamInfo&& info) is_file_open_ = true; - // TODO: pass on stream info. + auto write_res = + WriteMessage(TYPE_STREAM_INFO, + std::bind(&StreamInfo::Encode, info, std::placeholders::_1), + chunk_buffer_, kMaxChunkSize); + + if (write_res.has_error()) { + return cpp::fail(IO_ERROR); + } else { + xMessageBufferSend(output_buffer_, chunk_buffer_, write_res.value(), + portMAX_DELAY); + } return {}; } auto FatfsAudioInput::ProcessChunk(uint8_t* data, std::size_t length) - -> cpp::result { - // TODO. - return 0; + -> cpp::result { + return cpp::fail(UNSUPPORTED_STREAM); } auto FatfsAudioInput::GetRingBufferDistance() -> size_t { @@ -83,7 +98,7 @@ auto FatfsAudioInput::GetRingBufferDistance() -> size_t { + (file_buffer_write_pos_ - file_buffer_); } -auto FatfsAudioInput::ProcessIdle() -> cpp::result { +auto FatfsAudioInput::ProcessIdle() -> cpp::result { // First, see if we're able to fill up the input buffer with any more of the // file's contents. if (is_file_open_) { @@ -102,7 +117,8 @@ auto FatfsAudioInput::ProcessIdle() -> cpp::result { FRESULT result = f_read(¤t_file_, file_buffer_write_pos_, read_size, &bytes_read); if (result != FR_OK) { - return cpp::fail(IO_ERROR); // TODO; + ESP_LOGE(kTag, "file I/O error %d", result); + return cpp::fail(IO_ERROR); } if (f_eof(¤t_file_)) { @@ -128,9 +144,11 @@ auto FatfsAudioInput::ProcessIdle() -> cpp::result { switch (result) { case CHUNK_WRITE_TIMEOUT: case CHUNK_OUT_OF_DATA: - return {}; // TODO. + // Both of these are fine; SendChunk keeps track of where it's up to + // internally, so we will pick back up where we left off. + return {}; default: - return {}; // TODO. + return cpp::fail(IO_ERROR); } } diff --git a/src/audio/include/audio_decoder.hpp b/src/audio/include/audio_decoder.hpp index a4508c3e..4d2fd5f3 100644 --- a/src/audio/include/audio_decoder.hpp +++ b/src/audio/include/audio_decoder.hpp @@ -21,10 +21,11 @@ class AudioDecoder : public IAudioElement { auto SetInputBuffer(MessageBufferHandle_t*) -> void; auto SetOutputBuffer(MessageBufferHandle_t*) -> void; - auto ProcessStreamInfo(StreamInfo&& info) -> cpp::result; + auto ProcessStreamInfo(StreamInfo& info) + -> cpp::result; auto ProcessChunk(uint8_t* data, std::size_t length) - -> cpp::result; - auto ProcessIdle() -> cpp::result; + -> cpp::result; + auto ProcessIdle() -> cpp::result; AudioDecoder(const AudioDecoder&) = delete; AudioDecoder& operator=(const AudioDecoder&) = delete; diff --git a/src/audio/include/audio_element.hpp b/src/audio/include/audio_element.hpp index 13a48590..5b697784 100644 --- a/src/audio/include/audio_element.hpp +++ b/src/audio/include/audio_element.hpp @@ -15,8 +15,11 @@ namespace audio { -/* Errors that may be returned by any of the Process* methods. */ -enum StreamError { +/* + * Errors that may be returned by any of the Process* methods of an audio + * element. + */ +enum AudioProcessingError { // Indicates that this element is unable to handle the upcoming chunks. UNSUPPORTED_STREAM, // Indicates an error with reading or writing stream data. @@ -65,8 +68,8 @@ class IAudioElement { * Called when a StreamInfo message is received. Used to configure this * element in preperation for incoming chunks. */ - virtual auto ProcessStreamInfo(StreamInfo&& info) - -> cpp::result = 0; + virtual auto ProcessStreamInfo(StreamInfo& info) + -> cpp::result = 0; /* * Called when a ChunkHeader message is received. Includes the data associated @@ -75,14 +78,14 @@ class IAudioElement { * prepended to the next call. */ virtual auto ProcessChunk(uint8_t* data, std::size_t length) - -> cpp::result = 0; + -> cpp::result = 0; /* * Called when there has been no data received over the input buffer for some * time. This could be used to synthesize output, or to save memory by * releasing unused resources. */ - virtual auto ProcessIdle() -> cpp::result = 0; + virtual auto ProcessIdle() -> cpp::result = 0; protected: MessageBufferHandle_t* input_buffer_; diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index 6fe178ab..c54b32bd 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -19,10 +19,11 @@ class FatfsAudioInput : public IAudioElement { FatfsAudioInput(std::shared_ptr storage); ~FatfsAudioInput(); - auto ProcessStreamInfo(StreamInfo&& info) -> cpp::result; + auto ProcessStreamInfo(StreamInfo& info) + -> cpp::result; auto ProcessChunk(uint8_t* data, std::size_t length) - -> cpp::result; - auto ProcessIdle() -> cpp::result; + -> cpp::result; + auto ProcessIdle() -> cpp::result; auto SendChunk(uint8_t* buffer, size_t size) -> size_t; diff --git a/src/audio/include/stream_info.hpp b/src/audio/include/stream_info.hpp index ca28dd4e..47bcaa45 100644 --- a/src/audio/include/stream_info.hpp +++ b/src/audio/include/stream_info.hpp @@ -7,22 +7,11 @@ #include "cbor.h" #include "result.hpp" -#include "cbor_decoder.hpp" -#include "cbor_encoder.hpp" - namespace audio { class StreamInfo { public: - enum ParseError { - WRONG_TYPE, - MISSING_MAP, - CBOR_ERROR, - }; - - static auto Create(const uint8_t* buffer, size_t length) - -> cpp::result; - StreamInfo(cbor::MapDecoder*); + static auto Parse(CborValue& container) -> cpp::result; StreamInfo() = default; StreamInfo(const StreamInfo&) = default; @@ -38,7 +27,7 @@ class StreamInfo { return sample_rate_; } - auto WriteToMap(cbor::Encoder& encoder) -> cpp::result; + auto Encode(CborEncoder& enc) -> std::optional; private: std::optional path_; diff --git a/src/audio/include/stream_message.hpp b/src/audio/include/stream_message.hpp index 2791dcd8..cbd7c733 100644 --- a/src/audio/include/stream_message.hpp +++ b/src/audio/include/stream_message.hpp @@ -1,11 +1,60 @@ #pragma once +#include + +#include +#include + +#include "cbor.h" +#include "result.hpp" + namespace audio { +extern const int kEncoderFlags; +extern const int kDecoderFlags; + enum MessageType { TYPE_UNKNOWN, TYPE_CHUNK_HEADER, TYPE_STREAM_INFO, }; +template +auto WriteMessage(MessageType type, + Writer&& writer, + uint8_t* buffer, + size_t length) -> cpp::result { + CborEncoder root; + CborEncoder container; + + cbor_encoder_init(&root, buffer, length, kEncoderFlags); + cbor_encoder_create_array(&root, &container, 2); + cbor_encode_uint(&container, type); + + std::optional inner_err = std::invoke(writer, container); + if (inner_err) { + return cpp::fail(inner_err.value()); + } + + cbor_encoder_close_container(&root, &container); + return cbor_encoder_get_buffer_size(&root, buffer); +} + +template +auto ReadMessage(Reader&& reader, uint8_t* buffer, size_t length) + -> cpp::result { + CborParser parser; + CborValue root; + CborValue container; + + cbor_parser_init(buffer, length, kDecoderFlags, &parser, &root); + cbor_value_enter_container(&root, &container); + // Skip the type header + cbor_value_advance_fixed(&container); + + return std::invoke(reader, container); +} + +auto ReadMessageType(uint8_t* buffer, size_t length) -> MessageType; + } // namespace audio diff --git a/src/audio/stream_info.cpp b/src/audio/stream_info.cpp index a5f7bebf..8f217b79 100644 --- a/src/audio/stream_info.cpp +++ b/src/audio/stream_info.cpp @@ -5,8 +5,6 @@ #include "cbor.h" -#include "cbor_decoder.hpp" -#include "cbor_encoder.hpp" #include "stream_message.hpp" namespace audio { @@ -16,48 +14,72 @@ static const std::string kKeyChannels = "c"; static const std::string kKeyBitsPerSample = "b"; static const std::string kKeySampleRate = "r"; -auto StreamInfo::Create(const uint8_t* buffer, size_t length) - -> cpp::result { - CborParser parser; - CborValue value; +auto StreamInfo::Parse(CborValue& container) + -> cpp::result { + CborValue map; + cbor_value_enter_container(&container, &map); - cbor_parser_init(buffer, length, 0, &parser, &value); + CborValue entry; + StreamInfo ret; - int type = 0; - if (!cbor_value_is_integer(&value) || !cbor_value_get_int(&value, &type) || - type != TYPE_STREAM_INFO) { - return cpp::fail(WRONG_TYPE); + cbor_value_map_find_value(&map, kKeyPath.c_str(), &entry); + if (cbor_value_get_type(&entry) != CborInvalidType) { + char* val; + size_t len; + cbor_value_dup_text_string(&entry, &val, &len, NULL); + ret.path_ = std::string(val, len); + free(val); + } + cbor_value_map_find_value(&map, kKeyChannels.c_str(), &entry); + if (cbor_value_get_type(&entry) != CborInvalidType) { + uint64_t val; + cbor_value_get_uint64(&entry, &val); + ret.channels_ = val; + } + cbor_value_map_find_value(&map, kKeyBitsPerSample.c_str(), &entry); + if (cbor_value_get_type(&entry) != CborInvalidType) { + uint64_t val; + cbor_value_get_uint64(&entry, &val); + ret.bits_per_sample_ = val; + } + cbor_value_map_find_value(&map, kKeySampleRate.c_str(), &entry); + if (cbor_value_get_type(&entry) != CborInvalidType) { + uint64_t val; + cbor_value_get_uint64(&entry, &val); + ret.sample_rate_ = val; } - cbor_value_advance_fixed(&value); + return ret; +} - if (!cbor_value_is_map(&value)) { - return cpp::fail(MISSING_MAP); - } +auto StreamInfo::Encode(CborEncoder& enc) -> std::optional { + CborEncoder map; + size_t num_items = 0 + channels_.has_value() + bits_per_sample_.has_value() + + sample_rate_.has_value() + path_.has_value(); + cbor_encoder_create_map(&enc, &map, num_items); - auto map_decoder = cbor::MapDecoder::Create(value); - if (map_decoder.has_value()) { - return StreamInfo(map_decoder.value().get()); + if (channels_) { + cbor_encode_text_string(&map, kKeyChannels.c_str(), kKeyChannels.size()); + cbor_encode_uint(&map, channels_.value()); + } + if (bits_per_sample_) { + cbor_encode_text_string(&map, kKeyBitsPerSample.c_str(), + kKeyBitsPerSample.size()); + cbor_encode_uint(&map, bits_per_sample_.value()); + } + if (sample_rate_) { + cbor_encode_text_string(&map, kKeySampleRate.c_str(), + kKeySampleRate.size()); + cbor_encode_uint(&map, sample_rate_.value()); + } + if (path_) { + cbor_encode_text_string(&map, kKeyPath.c_str(), kKeyPath.size()); + cbor_encode_text_string(&map, path_.value().c_str(), path_.value().size()); } - return cpp::fail(CBOR_ERROR); -} -StreamInfo::StreamInfo(cbor::MapDecoder* decoder) { - // TODO: this method is n^2, which seems less than ideal. But you don't do it - // that frequently, so maybe it's okay? Needs investigation. - channels_ = decoder->FindValue(kKeyChannels); - bits_per_sample_ = decoder->FindValue(kKeyBitsPerSample); - sample_rate_ = decoder->FindValue(kKeySampleRate); - path_ = decoder->FindValue(kKeyPath); -} + cbor_encoder_close_container(&enc, &map); -auto StreamInfo::WriteToMap(cbor::Encoder& map_encoder) - -> cpp::result { - map_encoder.WriteKeyValue(kKeyChannels, channels_); - map_encoder.WriteKeyValue(kKeyBitsPerSample, bits_per_sample_); - map_encoder.WriteKeyValue(kKeySampleRate, sample_rate_); - map_encoder.WriteKeyValue(kKeyPath, path_); - return map_encoder.Finish(); + return std::nullopt; } } // namespace audio diff --git a/src/audio/stream_message.cpp b/src/audio/stream_message.cpp new file mode 100644 index 00000000..58868ce8 --- /dev/null +++ b/src/audio/stream_message.cpp @@ -0,0 +1,26 @@ +#include "stream_message.hpp" + +#include + +#include "cbor.h" + +namespace audio { + +const int kEncoderFlags = 0; +const int kDecoderFlags = 0; + +auto ReadMessageType(uint8_t* buffer, size_t length) -> MessageType { + CborParser parser; + CborValue root; + CborValue container; + + cbor_parser_init(buffer, length, kDecoderFlags, &parser, &root); + cbor_value_enter_container(&root, &container); + + uint64_t header = 0; + cbor_value_get_uint64(&container, &header); + + return static_cast(header); +} + +} // namespace audio diff --git a/src/cbor_wrapper/CMakeLists.txt b/src/cbor_wrapper/CMakeLists.txt deleted file mode 100644 index cd5186e0..00000000 --- a/src/cbor_wrapper/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -idf_component_register( - SRCS "cbor_decoder.cpp" "cbor_encoder.cpp" - INCLUDE_DIRS "include" - REQUIRES "cbor" "result") - -target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/cbor_wrapper/cbor_decoder.cpp b/src/cbor_wrapper/cbor_decoder.cpp deleted file mode 100644 index a99cca02..00000000 --- a/src/cbor_wrapper/cbor_decoder.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "cbor_decoder.hpp" - -#include -#include - -#include "cbor.h" -#include "result.hpp" - -static const int kDecoderFlags = 0; - -namespace cbor { - -auto parse_stdstring(const CborValue* val, std::string* out) -> CborError { - char* buf; - size_t len; - CborError err = cbor_value_dup_text_string(val, &buf, &len, NULL); - if (err != CborNoError) { - return err; - } - *out = std::string(buf, len); - free(buf); - return err; -} - -auto ArrayDecoder::Create(uint8_t* buffer, size_t buffer_len) - -> cpp::result, CborError> { - auto decoder = std::make_unique(); - cbor_parser_init(buffer, buffer_len, kDecoderFlags, &decoder->parser_, - &decoder->root_); - if (!cbor_value_is_array(&decoder->root_)) { - return cpp::fail(CborErrorIllegalType); - } - CborError err = cbor_value_enter_container(&decoder->root_, &decoder->it_); - if (err != CborNoError) { - return cpp::fail(err); - } - return std::move(decoder); -} - -auto ArrayDecoder::Create(CborValue& root) - -> cpp::result, CborError> { - auto decoder = std::make_unique(); - decoder->root_ = root; - if (!cbor_value_is_array(&decoder->root_)) { - return cpp::fail(CborErrorIllegalType); - } - - CborError err = cbor_value_enter_container(&decoder->root_, &decoder->it_); - if (err != CborNoError) { - return cpp::fail(err); - } - return std::move(decoder); -} - -template <> -auto ArrayDecoder::NextValue() -> cpp::result { - return NextValue(&cbor_value_is_integer, &cbor_value_get_int); -} -template <> -auto ArrayDecoder::NextValue() -> cpp::result { - return NextValue(&cbor_value_is_unsigned_integer, &cbor_value_get_uint64); -} -template <> -auto ArrayDecoder::NextValue() -> cpp::result { - return NextValue(&cbor_value_is_byte_string, &parse_stdstring); -} - -auto MapDecoder::Create(uint8_t* buffer, size_t buffer_len) - -> cpp::result, CborError> { - auto decoder = std::make_unique(); - cbor_parser_init(buffer, buffer_len, kDecoderFlags, &decoder->parser_, - &decoder->root_); - if (!cbor_value_is_map(&decoder->root_)) { - return cpp::fail(CborErrorIllegalType); - } - CborError err = cbor_value_enter_container(&decoder->root_, &decoder->it_); - if (err != CborNoError) { - return cpp::fail(err); - } - return std::move(decoder); -} - -auto MapDecoder::Create(CborValue& root) - -> cpp::result, CborError> { - auto decoder = std::make_unique(); - decoder->root_ = root; - if (!cbor_value_is_map(&decoder->root_)) { - return cpp::fail(CborErrorIllegalType); - } - CborError err = cbor_value_enter_container(&decoder->root_, &decoder->it_); - if (err != CborNoError) { - return cpp::fail(err); - } - return std::move(decoder); -} - -template <> -auto MapDecoder::FindValue(const std::string& key) -> std::optional { - return FindValue(key, &cbor_value_is_integer, &cbor_value_get_int); -} -template <> -auto MapDecoder::FindValue(const std::string& key) -> std::optional { - return FindValue(key, &cbor_value_is_unsigned_integer, - &cbor_value_get_uint64); -} -template <> -auto MapDecoder::FindValue(const std::string& key) - -> std::optional { - return FindValue(key, &cbor_value_is_byte_string, &parse_stdstring); -} - -} // namespace cbor diff --git a/src/cbor_wrapper/cbor_encoder.cpp b/src/cbor_wrapper/cbor_encoder.cpp deleted file mode 100644 index e4e8ee84..00000000 --- a/src/cbor_wrapper/cbor_encoder.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "cbor_encoder.hpp" - -#include -#include - -#include "cbor.h" -#include "cbor_decoder.hpp" -#include "result.hpp" - -namespace cbor { - -static const int kEncoderFlags = 0; - -Encoder::Encoder(ContainerType type, - uint32_t container_len, - uint8_t* buffer, - size_t buffer_len) { - cbor_encoder_init(&root_encoder_, buffer, buffer_len, kEncoderFlags); - switch (type) { - case CONTAINER_ARRAY: - error_ = cbor_encoder_create_array(&root_encoder_, &container_encoder_, - container_len); - break; - case CONTAINER_MAP: - error_ = cbor_encoder_create_map(&root_encoder_, &container_encoder_, - container_len); - break; - } -} - -auto Encoder::WriteValue(const std::string& val) -> void { - if (error_ != CborNoError) { - return; - } - error_ = - cbor_encode_text_string(&container_encoder_, val.c_str(), val.size()); -} - -auto Encoder::WriteValue(uint32_t val) -> void { - if (error_ != CborNoError) { - return; - } - error_ = cbor_encode_uint(&container_encoder_, val); -} - -auto Encoder::WriteValue(int32_t val) -> void { - if (error_ != CborNoError) { - return; - } - error_ = cbor_encode_int(&container_encoder_, val); -} - -auto Encoder::Finish() -> cpp::result { - if (error_ == CborNoError) { - error_ = cbor_encoder_close_container(&root_encoder_, &container_encoder_); - } - if (error_ != CborNoError) { - return cpp::fail(error_); - } - return cbor_encoder_get_buffer_size(&root_encoder_, buffer_); -} - -} // namespace cbor diff --git a/src/cbor_wrapper/include/cbor_decoder.hpp b/src/cbor_wrapper/include/cbor_decoder.hpp deleted file mode 100644 index 193e7843..00000000 --- a/src/cbor_wrapper/include/cbor_decoder.hpp +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include - -#include -#include - -#include "cbor.h" -#include "result.hpp" - -namespace cbor { - -class ArrayDecoder { - public: - static auto Create(uint8_t* buffer, size_t buffer_len) - -> cpp::result, CborError>; - - static auto Create(CborValue& root) - -> cpp::result, CborError>; - - ArrayDecoder() {} - - template - auto NextValue() -> cpp::result; - - template - auto NextValue(bool (*is_valid)(const CborValue*), - CborError (*parse)(const CborValue*, T*)) - -> cpp::result { - 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 Iterator() -> CborValue& { return it_; } - - ArrayDecoder(const ArrayDecoder&) = delete; - ArrayDecoder& operator=(const ArrayDecoder&) = delete; - - private: - CborParser parser_; - CborValue root_; - - CborValue it_; - CborError error_ = CborNoError; -}; - -class MapDecoder { - public: - static auto Create(uint8_t* buffer, size_t buffer_len) - -> cpp::result, CborError>; - - static auto Create(CborValue& root) - -> cpp::result, CborError>; - - MapDecoder() {} - - template - auto FindValue(const std::string& key) -> std::optional; - - template - auto FindValue(const std::string& key, - bool (*is_valid)(const CborValue*), - CborError (*parse)(const CborValue*, T*)) -> std::optional { - if (error_ != CborNoError) { - return {}; - } - CborValue val; - 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 {}; - } - return ret; - } - - auto Failed() -> CborError { return error_; } - - MapDecoder(const MapDecoder&) = delete; - MapDecoder& operator=(const MapDecoder&) = delete; - - private: - CborParser parser_; - CborValue root_; - - CborValue it_; - CborError error_ = CborNoError; -}; - -} // namespace cbor diff --git a/src/cbor_wrapper/include/cbor_encoder.hpp b/src/cbor_wrapper/include/cbor_encoder.hpp deleted file mode 100644 index cc57e8a4..00000000 --- a/src/cbor_wrapper/include/cbor_encoder.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "cbor.h" -#include "result.hpp" - -namespace cbor { - -enum ContainerType { CONTAINER_ARRAY, CONTAINER_MAP }; - -class Encoder { - public: - Encoder(ContainerType type, - uint32_t container_len, - uint8_t* buffer, - size_t buffer_len); - - auto WriteValue(const std::string& val) -> void; - auto WriteValue(uint32_t val) -> void; - auto WriteValue(int32_t val) -> void; - - template - auto WriteKeyValue(const std::string& key, const T&& val) -> void { - WriteValue(key); - WriteValue(val); - } - - template - auto WriteKeyValue(const std::string& key, const std::optional& val) - -> void { - if (val) { - WriteKeyValue(key, val.value()); - } - } - - auto Finish() -> cpp::result; - - Encoder(const Encoder&) = delete; - Encoder& operator=(const Encoder&) = delete; - - private: - uint8_t* buffer_; - CborEncoder root_encoder_; - CborEncoder container_encoder_; - - CborError error_ = CborNoError; -}; - -} // namespace cbor