From 01c03c12dd574cf8c4bc3fd358ae8d16e8bfce75 Mon Sep 17 00:00:00 2001 From: Dmitriy Shilin Date: Tue, 27 Jan 2026 15:38:22 +0100 Subject: [PATCH] Add byte writer --- components/ocs_core/CMakeLists.txt | 1 + components/ocs_core/byte_writer.cpp | 47 ++++++++++++ components/ocs_core/byte_writer.h | 67 +++++++++++++++++ components/ocs_core/test/CMakeLists.txt | 1 + components/ocs_core/test/test_byte_writer.cpp | 71 +++++++++++++++++++ 5 files changed, 187 insertions(+) create mode 100644 components/ocs_core/byte_writer.cpp create mode 100644 components/ocs_core/byte_writer.h create mode 100644 components/ocs_core/test/test_byte_writer.cpp diff --git a/components/ocs_core/CMakeLists.txt b/components/ocs_core/CMakeLists.txt index 04e07eb5..1ca24ee8 100644 --- a/components/ocs_core/CMakeLists.txt +++ b/components/ocs_core/CMakeLists.txt @@ -15,6 +15,7 @@ idf_component_register( "operation_guard.cpp" "file_stream_reader.cpp" "stream_transceiver.cpp" + "byte_writer.cpp" REQUIRES "json" diff --git a/components/ocs_core/byte_writer.cpp b/components/ocs_core/byte_writer.cpp new file mode 100644 index 00000000..e731d912 --- /dev/null +++ b/components/ocs_core/byte_writer.cpp @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2026 Tendry Lab + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "ocs_core/byte_writer.h" + +namespace ocs { +namespace core { + +ByteWriter::ByteWriter(uint8_t* data, size_t size) + : size_(size) + , data_(data) { +} + +size_t ByteWriter::get_len() const { + return offset_; +} + +size_t ByteWriter::get_cap() const { + return size_; +} + +uint8_t* ByteWriter::get_data() { + return data_; +} + +size_t ByteWriter::write(const uint8_t* data, size_t size) { + const size_t ret = std::min(size, left_()); + + if (ret) { + memcpy(data_ + offset_, data, ret); + offset_ += ret; + } + + return ret; +} + +size_t ByteWriter::left_() const { + return size_ - offset_; +} + +} // namespace core +} // namespace ocs diff --git a/components/ocs_core/byte_writer.h b/components/ocs_core/byte_writer.h new file mode 100644 index 00000000..b39451e4 --- /dev/null +++ b/components/ocs_core/byte_writer.h @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2026 Tendry Lab + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "ocs_core/noncopyable.h" + +namespace ocs { +namespace core { + +class ByteWriter : private core::NonCopyable<> { +public: + //! Initialize. + //! + //! @remarks + //! - @p data should be valid until ByteWriter exists. + ByteWriter(uint8_t* data, size_t size); + + //! Return the number of written bytes. + size_t get_len() const; + + //! Return the size of the underlying array of bytes. + size_t get_cap() const; + + //! Return the underlying data. + uint8_t* get_data(); + + //! Write @p size bytes of @p data. + size_t write(const uint8_t* data, size_t size); + + //! Write any integer value. + template bool write(const T& t) { + static_assert(std::is_integral::value, "require integral type"); + + if (left_() < sizeof(t)) { + return false; + } + + T c = t; + + for (size_t n = offset_ + sizeof(t); n > offset_;) { + --n; + data_[n] = (c & 0xFF); + c >>= 8; + } + + offset_ += sizeof(t); + + return true; + } + +private: + size_t left_() const; + + const size_t size_ { 0 }; + + uint8_t* data_ { nullptr }; + size_t offset_ { 0 }; +}; + +} // namespace core +} // namespace ocs diff --git a/components/ocs_core/test/CMakeLists.txt b/components/ocs_core/test/CMakeLists.txt index 6059532d..9517b9b2 100644 --- a/components/ocs_core/test/CMakeLists.txt +++ b/components/ocs_core/test/CMakeLists.txt @@ -6,6 +6,7 @@ idf_component_register( "test_cond.cpp" "test_rate_limiter.cpp" "test_stream_transceiver.cpp" + "test_byte_writer.cpp" REQUIRES "unity" diff --git a/components/ocs_core/test/test_byte_writer.cpp b/components/ocs_core/test/test_byte_writer.cpp new file mode 100644 index 00000000..465c744c --- /dev/null +++ b/components/ocs_core/test/test_byte_writer.cpp @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2026 Tendry Lab + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "unity.h" + +#include "ocs_core/byte_writer.h" + +namespace ocs { +namespace core { + +TEST_CASE("Byte writer: write byte", "[ocs_core], [byte_writer]") { + uint8_t write_buf[42]; + memset(write_buf, 0, sizeof(write_buf)); + + ByteWriter writer(write_buf, sizeof(write_buf)); + TEST_ASSERT_EQUAL(0, writer.get_len()); + + for (uint8_t n = 0; n < sizeof(write_buf); ++n) { + TEST_ASSERT_TRUE(writer.write(n)); + } + + for (uint8_t n = 0; n < sizeof(write_buf); ++n) { + TEST_ASSERT_EQUAL(n, write_buf[n]); + } +} + +TEST_CASE("Byte writer: write array: whole at once", "[ocs_core], [byte_writer]") { + uint8_t write_buf[42]; + memset(write_buf, 0, sizeof(write_buf)); + + ByteWriter writer(write_buf, sizeof(write_buf)); + TEST_ASSERT_EQUAL(0, writer.get_len()); + + uint8_t buf[sizeof(write_buf)]; + memset(buf, 5, sizeof(buf)); + + TEST_ASSERT_EQUAL(sizeof(write_buf), writer.write(buf, sizeof(buf))); + TEST_ASSERT_TRUE(memcmp(write_buf, buf, sizeof(buf)) == 0); +} + +TEST_CASE("Byte writer: write array: by parts", "[ocs_core], [byte_writer]") { + uint8_t write_buf[6]; + memset(write_buf, 0, sizeof(write_buf)); + + ByteWriter writer(write_buf, sizeof(write_buf)); + + TEST_ASSERT_TRUE(writer.write(static_cast(5))); + TEST_ASSERT_EQUAL(1, writer.get_len()); + + uint8_t buf[2]; + memset(buf, 3, sizeof(buf)); + TEST_ASSERT_EQUAL(sizeof(buf), writer.write(buf, sizeof(buf))); + + memset(buf, 7, sizeof(buf)); + TEST_ASSERT_EQUAL(sizeof(buf), writer.write(buf, sizeof(buf))); + + // Write the remaining byte + TEST_ASSERT_TRUE(writer.write(static_cast(1))); + TEST_ASSERT_EQUAL(sizeof(write_buf), writer.get_len()); + + uint8_t want_buf[] = { 5, 3, 3, 7, 7, 1 }; + TEST_ASSERT_EQUAL(sizeof(want_buf), writer.get_len()); + TEST_ASSERT_TRUE(memcmp(want_buf, writer.get_data(), sizeof(want_buf)) == 0); +} + +} // namespace core +} // namespace ocs