From 08546d73b4410d0a776123792b442b144fd87740 Mon Sep 17 00:00:00 2001 From: Kalyan Sriram Date: Sat, 9 Mar 2024 11:57:40 -0600 Subject: [PATCH 1/3] kocherga_serial: update header format --- kocherga/kocherga.hpp | 106 +++++++++++++++++++++++- kocherga/kocherga_can.hpp | 47 +---------- kocherga/kocherga_serial.hpp | 132 ++++++++++++------------------ tests/unit/can/test_misc.cpp | 14 ---- tests/unit/serial/test_misc.cpp | 2 +- tests/unit/serial/test_node.cpp | 45 +++++----- tests/unit/serial/test_stream.cpp | 125 ++++++++++++++-------------- tests/unit/test_misc.cpp | 26 +++++- 8 files changed, 268 insertions(+), 229 deletions(-) diff --git a/kocherga/kocherga.hpp b/kocherga/kocherga.hpp index abe8106..b3df838 100644 --- a/kocherga/kocherga.hpp +++ b/kocherga/kocherga.hpp @@ -238,6 +238,110 @@ auto getRandomByte() -> std::uint8_t; // -------------------------------------------------------------------------------------------------------------------- +namespace detail +{ + +static constexpr uint8_t BitsPerByte = 8U; + +/// Size-optimized implementation of CRC16-CCITT +class CRC16CCITT +{ +public: + static constexpr std::size_t Size = 2; + + void update(const std::uint8_t b) noexcept + { + value_ ^= static_cast(b << BitsPerByte); + // Manually unrolled because the difference in performance is drastic. Can't use table because size limitations. + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + value_ = + static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); + } + + [[nodiscard]] auto get() const noexcept { return value_; } + + [[nodiscard]] auto getBytes() const noexcept -> std::array + { + const auto x = get(); + return { + static_cast(x >> (BitsPerByte * 1U)), + static_cast(x >> (BitsPerByte * 0U)), + }; + } + + void update(const std::size_t size, const std::uint8_t* const ptr) noexcept + { + const auto* p = ptr; + for (std::size_t s = 0; s < size; s++) + { + update(*p); + p++; + } + } + + [[nodiscard]] auto isResidueCorrect() const noexcept { return value_ == Residue; } + +private: + static constexpr std::uint16_t Initial = 0xFFFFU; + static constexpr std::uint16_t Poly = 0x1021U; + static constexpr std::uint16_t Top = 0x8000U; + static constexpr std::uint16_t Residue = 0x0000U; + + std::uint16_t value_ = Initial; +}; + +/// Size-optimized implementation of CRC32-C (Castagnoli). +class CRC32C +{ +public: + static constexpr std::size_t Size = 4; + + void update(const std::uint8_t b) noexcept + { + value_ ^= static_cast(b); + for (auto i = 0U; i < BitsPerByte; i++) + { + value_ = ((value_ & 1U) != 0) ? ((value_ >> 1U) ^ ReflectedPoly) : (value_ >> 1U); // NOLINT + } + } + + [[nodiscard]] auto get() const noexcept { return value_ ^ Xor; } + + [[nodiscard]] auto getBytes() const noexcept -> std::array + { + const auto x = get(); + return { + static_cast(x >> (BitsPerByte * 0U)), + static_cast(x >> (BitsPerByte * 1U)), + static_cast(x >> (BitsPerByte * 2U)), + static_cast(x >> (BitsPerByte * 3U)), + }; + } + + [[nodiscard]] auto isResidueCorrect() const noexcept { return value_ == Residue; } + +private: + static constexpr std::uint32_t Xor = 0xFFFF'FFFFUL; + static constexpr std::uint32_t ReflectedPoly = 0x82F6'3B78UL; + static constexpr std::uint32_t Residue = 0xB798'B438UL; + + std::uint32_t value_ = Xor; +}; +} // namespace detail + /// This is used to verify integrity of the application and other data. /// Note that the firmware CRC verification is a computationally expensive process that needs to be completed /// in a limited time interval, which should be minimized. This class has been carefully manually optimized to @@ -307,8 +411,6 @@ class CRC64 /// Internal use only. namespace detail { -static constexpr auto BitsPerByte = 8U; - static constexpr std::chrono::microseconds DefaultTransferIDTimeout{2'000'000}; ///< Default taken from Specification. /// Detects the application in the ROM, verifies its integrity, and retrieves the information about it. diff --git a/kocherga/kocherga_can.hpp b/kocherga/kocherga_can.hpp index 6a6528e..b5c4cd2 100644 --- a/kocherga/kocherga_can.hpp +++ b/kocherga/kocherga_can.hpp @@ -234,6 +234,7 @@ class TxQueue final namespace detail { using kocherga::detail::BitsPerByte; // NOSONAR +using kocherga::detail::CRC16CCITT; // NOSONAR static constexpr std::uint8_t TailByteStartOfTransfer = 0b1000'0000; static constexpr std::uint8_t TailByteEndOfTransfer = 0b0100'0000; @@ -241,52 +242,6 @@ static constexpr std::uint8_t TailByteToggleBit = 0b0010'0000; static constexpr std::uint8_t MaxTransferID = 31; -class CRC16CCITT -{ -public: - static constexpr std::size_t Size = 2; - - void update(const std::uint8_t b) noexcept - { - value_ ^= static_cast(static_cast(b) << 8U); - // Manually unrolled because the difference in performance is drastic. Can't use table because size limitations. - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - value_ = static_cast(static_cast(value_ << 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U)); - } - - void update(const std::size_t size, const std::uint8_t* const ptr) noexcept - { - const auto* p = ptr; - for (std::size_t s = 0; s < size; s++) - { - update(*p); - p++; - } - } - - [[nodiscard]] auto get() const noexcept { return value_; } - - [[nodiscard]] auto getBytes() const noexcept -> std::array - { - const auto x = get(); - return {static_cast(x >> BitsPerByte), static_cast(x)}; - } - - [[nodiscard]] auto isResidueCorrect() const noexcept { return value_ == 0; } - -private: - static constexpr std::uint16_t Top = 0x8000U; - static constexpr std::uint16_t Poly = 0x1021U; - - std::uint16_t value_ = std::numeric_limits::max(); -}; - inline auto makePseudoUniqueID(const SystemInfo::UniqueID& uid) -> std::uint64_t { CRC64 crc; diff --git a/kocherga/kocherga_serial.hpp b/kocherga/kocherga_serial.hpp index 013a100..5cb454e 100644 --- a/kocherga/kocherga_serial.hpp +++ b/kocherga/kocherga_serial.hpp @@ -5,6 +5,7 @@ #pragma once #include "kocherga.hpp" +#include #include namespace kocherga::serial @@ -12,50 +13,15 @@ namespace kocherga::serial namespace detail { using kocherga::detail::BitsPerByte; // NOSONAR +using kocherga::detail::CRC16CCITT; // NOSONAR +using kocherga::detail::CRC32C; // NOSONAR constexpr std::uint8_t FrameDelimiter = 0x00; ///< Zeros cannot occur inside frames thanks to COBS encoding. /// Reference values to check the header against. -static constexpr std::uint8_t FrameFormatVersion = 0; +static constexpr std::uint8_t FrameFormatVersion = 1; static constexpr std::array FrameIndexEOTReference{0, 0, 0, 0x80}; - -/// Size-optimized implementation of CRC32-C (Castagnoli). -class CRC32C -{ -public: - static constexpr std::size_t Size = 4; - - void update(const std::uint8_t b) noexcept - { - value_ ^= static_cast(b); - for (auto i = 0U; i < BitsPerByte; i++) - { - value_ = ((value_ & 1U) != 0) ? ((value_ >> 1U) ^ ReflectedPoly) : (value_ >> 1U); // NOLINT - } - } - - [[nodiscard]] auto get() const noexcept { return value_ ^ Xor; } - - [[nodiscard]] auto getBytes() const noexcept -> std::array - { - const auto x = get(); - return { - static_cast(x >> (BitsPerByte * 0U)), - static_cast(x >> (BitsPerByte * 1U)), - static_cast(x >> (BitsPerByte * 2U)), - static_cast(x >> (BitsPerByte * 3U)), - }; - } - - [[nodiscard]] auto isResidueCorrect() const noexcept { return value_ == Residue; } - -private: - static constexpr std::uint32_t Xor = 0xFFFF'FFFFUL; - static constexpr std::uint32_t ReflectedPoly = 0x82F6'3B78UL; - static constexpr std::uint32_t Residue = 0xB798'B438UL; - - std::uint32_t value_ = Xor; -}; +static constexpr std::array UserData{0, 0}; /// New instance shall be created per encoded frame. /// ByteWriter is of type (std::uint8_t) -> bool, returns true on success. @@ -205,12 +171,12 @@ struct Transfer { struct Metadata { - static constexpr std::uint8_t DefaultPriority = 6U; // Second to lowest. - static constexpr NodeID AnonymousNodeID = 0xFFFFU; - static constexpr PortID DataSpecServiceFlag = 0x8000U; - static constexpr PortID DataSpecResponseFlag = 0x4000U; + static constexpr std::uint8_t DefaultPriority = 6U; // Second to lowest. + static constexpr NodeID AnonymousNodeID = 0xFFFFU; + static constexpr PortID DataSpecServiceFlag = 0x8000U; + static constexpr PortID DataSpecRequestFlag = 0x4000U; static constexpr auto DataSpecServiceIDMask = - static_cast(~static_cast(DataSpecServiceFlag | DataSpecResponseFlag)); + static_cast(~static_cast(DataSpecServiceFlag | DataSpecRequestFlag)); std::uint8_t priority = DefaultPriority; NodeID source = AnonymousNodeID; @@ -220,7 +186,7 @@ struct Transfer [[nodiscard]] auto isRequest() const noexcept -> std::optional { - if (((data_spec & DataSpecServiceFlag) != 0) && ((data_spec & DataSpecResponseFlag) == 0)) + if (((data_spec & DataSpecServiceFlag) != 0) && ((data_spec & DataSpecRequestFlag) != 0)) { return data_spec & DataSpecServiceIDMask; } @@ -229,7 +195,7 @@ struct Transfer [[nodiscard]] auto isResponse() const noexcept -> std::optional { - if (((data_spec & DataSpecServiceFlag) != 0) && ((data_spec & DataSpecResponseFlag) != 0)) + if (((data_spec & DataSpecServiceFlag) != 0) && ((data_spec & DataSpecRequestFlag) == 0)) { return data_spec & DataSpecServiceIDMask; } @@ -256,7 +222,7 @@ class StreamParser const auto dec = decoder_.feed(stream_byte); if (std::holds_alternative(dec)) { - if (inside_ && (offset_ >= TotalOverheadSize) && crc_.isResidueCorrect() && isMetaValid()) + if (inside_ && (offset_ >= TotalOverheadSize) && transfer_crc_.isResidueCorrect() && isMetaValid()) { out = Transfer{ meta_, @@ -269,13 +235,14 @@ class StreamParser } else if (const std::uint8_t* const decoded_byte = std::get_if(&dec)) { - crc_.update(*decoded_byte); if (offset_ < HeaderSize) { + header_crc_.update(*decoded_byte); acceptHeader(*decoded_byte); } else { + transfer_crc_.update(*decoded_byte); const auto buf_offset = offset_ - HeaderSize; if (buf_offset < buf_.size()) { @@ -299,10 +266,11 @@ class StreamParser void reset() noexcept { decoder_.reset(); - offset_ = 0; - inside_ = false; - crc_ = {}; - meta_ = {}; + offset_ = 0; + inside_ = false; + header_crc_ = {}; + transfer_crc_ = {}; + meta_ = {}; } private: @@ -327,7 +295,7 @@ class StreamParser } if (offset_ == (HeaderSize - 1U)) { - if (!crc_.isResidueCorrect()) + if (!header_crc_.isResidueCorrect()) { reset(); // Header CRC error. } @@ -336,7 +304,6 @@ class StreamParser // also, the amount of dynamic memory that needs to be allocated for the payload would also be determined // at this moment. The main purpose of the header CRC is to permit such early-stage frame processing. // This specialized implementation requires none of that. - crc_ = {}; } } @@ -366,7 +333,7 @@ class StreamParser return meta_.destination == Transfer::Metadata::AnonymousNodeID; } - static constexpr std::size_t HeaderSize = 32; + static constexpr std::size_t HeaderSize = 24; static constexpr std::size_t TotalOverheadSize = HeaderSize + CRC32C::Size; // Header field offsets. static constexpr std::size_t OffsetVersion = 0; @@ -374,13 +341,14 @@ class StreamParser static constexpr std::pair OffsetSource{2, 3}; static constexpr std::pair OffsetDestination{4, 5}; static constexpr std::pair OffsetDataSpec{6, 7}; - static constexpr std::pair OffsetTransferID{16, 23}; - static constexpr std::pair OffsetFrameIndexEOT{24, 27}; + static constexpr std::pair OffsetTransferID{8, 15}; + static constexpr std::pair OffsetFrameIndexEOT{16, 19}; COBSDecoder decoder_; std::size_t offset_ = 0; bool inside_ = false; - CRC32C crc_; + CRC16CCITT header_crc_; + CRC32C transfer_crc_; Transfer::Metadata meta_; std::array buf_{}; }; @@ -392,40 +360,43 @@ template [[nodiscard]] inline auto transmit(const Callback& send_byte, const Transfer& tr) -> bool { COBSEncoder encoder(send_byte); - CRC32C crc; - const auto out = [&crc, &encoder](const std::uint8_t b) { - crc.update(b); + CRC16CCITT header_crc; + const auto header_out = [&header_crc, &encoder](const std::uint8_t b) { + header_crc.update(b); return encoder.push(b); }; - const auto out2 = [&out](const std::uint16_t bb) { - return out(static_cast(bb)) && out(static_cast(bb >> BitsPerByte)); + const auto header_out2 = [&header_out](const std::uint16_t bb) { + return header_out(static_cast(bb)) && header_out(static_cast(bb >> BitsPerByte)); }; - bool ok = out(FrameFormatVersion) && out(tr.meta.priority) && // - out2(tr.meta.source) && out2(tr.meta.destination) && out2(tr.meta.data_spec); - for (auto i = 0U; i < sizeof(std::uint64_t); i++) - { - ok = ok && out(0); - } + + bool ok = header_out(FrameFormatVersion) && header_out(tr.meta.priority) && // + header_out2(tr.meta.source) && header_out2(tr.meta.destination) && header_out2(tr.meta.data_spec); auto tmp_transfer_id = tr.meta.transfer_id; for (auto i = 0U; i < sizeof(std::uint64_t); i++) { - ok = ok && out(static_cast(tmp_transfer_id)); + ok = ok && header_out(static_cast(tmp_transfer_id)); tmp_transfer_id >>= BitsPerByte; } for (const auto x : FrameIndexEOTReference) { - ok = ok && out(x); + ok = ok && header_out(x); } - for (const auto x : crc.getBytes()) + for (const auto x : UserData) { - ok = ok && out(x); + ok = ok && header_out(x); } - crc = {}; // Now it's the payload CRC. + for (const auto x : header_crc.getBytes()) + { + ok = ok && header_out(x); + } + + CRC32C transfer_crc; { const auto* ptr = tr.payload; for (std::size_t i = 0U; i < tr.payload_len; i++) { - ok = ok && out(*ptr); + transfer_crc.update(*ptr); + ok = ok && encoder.push(*ptr); ++ptr; if (!ok) { @@ -433,9 +404,9 @@ template } } } - for (const auto x : crc.getBytes()) + for (const auto x : transfer_crc.getBytes()) { - ok = ok && out(x); + ok = ok && encoder.push(x); } return ok && encoder.end(); } @@ -572,8 +543,7 @@ class SerialNode : public kocherga::INode meta.source = *local_node_id_; meta.destination = tr.meta.source; meta.data_spec = static_cast(*req_id) | - static_cast(detail::Transfer::Metadata::DataSpecServiceFlag | - detail::Transfer::Metadata::DataSpecResponseFlag); + static_cast(detail::Transfer::Metadata::DataSpecServiceFlag); meta.transfer_id = tr.meta.transfer_id; for (auto i = 0U; i < service_multiplication_factor_; i++) { @@ -615,7 +585,9 @@ class SerialNode : public kocherga::INode detail::Transfer::Metadata meta{}; meta.source = *local_node_id_; meta.destination = server_node_id; - meta.data_spec = static_cast(service_id) | detail::Transfer::Metadata::DataSpecServiceFlag; + meta.data_spec = + static_cast(service_id) | static_cast(detail::Transfer::Metadata::DataSpecServiceFlag | + detail::Transfer::Metadata::DataSpecRequestFlag); meta.transfer_id = transfer_id; bool transmit_ok = false; // Optimistic aggregation: one successful transmission is considered a success. for (auto i = 0U; i < service_multiplication_factor_; i++) diff --git a/tests/unit/can/test_misc.cpp b/tests/unit/can/test_misc.cpp index 6ebd49c..7cb3713 100644 --- a/tests/unit/can/test_misc.cpp +++ b/tests/unit/can/test_misc.cpp @@ -9,20 +9,6 @@ #include #include -TEST_CASE("can::CRC") -{ - kocherga::can::detail::CRC16CCITT crc; - crc.update(3, reinterpret_cast("123")); - REQUIRE(0x5BCEU == crc.get()); - REQUIRE(crc.getBytes().at(0) == 0x5BU); - REQUIRE(crc.getBytes().at(1) == 0xCEU); - REQUIRE(!crc.isResidueCorrect()); - crc.update(0x5BU); - crc.update(0xCEU); - REQUIRE(crc.isResidueCorrect()); - REQUIRE(0 == crc.get()); -} - TEST_CASE("can::BlockAllocator") { kocherga::can::detail::BlockAllocator<8, 2> ba; diff --git a/tests/unit/serial/test_misc.cpp b/tests/unit/serial/test_misc.cpp index 76f5410..e3f4557 100644 --- a/tests/unit/serial/test_misc.cpp +++ b/tests/unit/serial/test_misc.cpp @@ -6,7 +6,7 @@ #include "catch.hpp" #include -TEST_CASE("serial::CRC") +TEST_CASE("serial::CRC32C") { kocherga::serial::detail::CRC32C crc; crc.update(static_cast('1')); diff --git a/tests/unit/serial/test_node.cpp b/tests/unit/serial/test_node.cpp index cb0bee4..131bb9c 100644 --- a/tests/unit/serial/test_node.cpp +++ b/tests/unit/serial/test_node.cpp @@ -135,7 +135,8 @@ TEST_CASE("kocherga_serial::SerialNode service") REQUIRE(request->meta.source == 2222); REQUIRE(request->meta.destination == 1111); REQUIRE(request->meta.data_spec == (static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag)); + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | + kocherga::serial::detail::Transfer::Metadata::DataSpecRequestFlag)); REQUIRE(request->meta.transfer_id == 0xCAFE'CAFE); REQUIRE(request->payload_len == 3); REQUIRE(0 == std::memcmp(request->payload, "\x03\x03\x03", 3)); @@ -147,8 +148,7 @@ TEST_CASE("kocherga_serial::SerialNode service") response.meta.source = 1110; response.meta.destination = 2222; response.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag; + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; response.meta.transfer_id = 0xCAFE'CAFE; port.pushRx(response); static_cast(node).poll(reactor, std::chrono::microseconds(1'000)); @@ -157,8 +157,7 @@ TEST_CASE("kocherga_serial::SerialNode service") response.meta.source = 1111; response.meta.destination = 2221; response.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag; + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; response.meta.transfer_id = 0xCAFE'CAFE; port.pushRx(response); static_cast(node).poll(reactor, std::chrono::microseconds(1'000)); @@ -167,8 +166,7 @@ TEST_CASE("kocherga_serial::SerialNode service") response.meta.source = 1111; response.meta.destination = 2222; response.meta.data_spec = static_cast(kocherga::ServiceID::NodeGetInfo) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag; + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; response.meta.transfer_id = 0xCAFE'CAFE; port.pushRx(response); static_cast(node).poll(reactor, std::chrono::microseconds(1'000)); @@ -177,8 +175,7 @@ TEST_CASE("kocherga_serial::SerialNode service") response.meta.source = 1111; response.meta.destination = 2222; response.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag; + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; response.meta.transfer_id = 0xCAFE'CAFA; port.pushRx(response); static_cast(node).poll(reactor, std::chrono::microseconds(1'000)); @@ -189,8 +186,7 @@ TEST_CASE("kocherga_serial::SerialNode service") response.meta.source = 1111; response.meta.destination = 2222; response.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag; + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; response.meta.transfer_id = 0xCAFE'CAFE; response.payload_len = 6; response.payload = reinterpret_cast("\x05\x04\x03\x02\x01\x00"); @@ -209,8 +205,10 @@ TEST_CASE("kocherga_serial::SerialNode service") kocherga::serial::detail::Transfer request; request.meta.source = 1111; request.meta.destination = 2222; - request.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; + request.meta.data_spec = + static_cast(kocherga::ServiceID::NodeExecuteCommand) | + static_cast(kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | + kocherga::serial::detail::Transfer::Metadata::DataSpecRequestFlag); request.meta.transfer_id = 0xCAFE'BABE; request.payload_len = 6; request.payload = reinterpret_cast("\x05\x04\x03\x02\x01\x00"); @@ -233,8 +231,7 @@ TEST_CASE("kocherga_serial::SerialNode service") REQUIRE(response->meta.source == 2222); REQUIRE(response->meta.destination == 1111); REQUIRE(response->meta.data_spec == (static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag)); + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag)); REQUIRE(response->meta.transfer_id == 0xCAFE'BABE); REQUIRE(response->payload_len == 5); REQUIRE(0 == std::memcmp(response->payload, "\x01\x02\x03\x04\x05", 5)); @@ -246,8 +243,10 @@ TEST_CASE("kocherga_serial::SerialNode service") kocherga::serial::detail::Transfer request; request.meta.source = 3210; request.meta.destination = 2222; - request.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; + request.meta.data_spec = + static_cast(kocherga::ServiceID::NodeExecuteCommand) | + static_cast(kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | + kocherga::serial::detail::Transfer::Metadata::DataSpecRequestFlag); request.meta.transfer_id = 0xBABA'CACA; request.payload_len = 6; request.payload = reinterpret_cast("\x05\x04\x03\x02\x01\x00"); @@ -270,8 +269,7 @@ TEST_CASE("kocherga_serial::SerialNode service") REQUIRE(response->meta.source == 2222); REQUIRE(response->meta.destination == 3210); REQUIRE(response->meta.data_spec == (static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag)); + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag)); REQUIRE(response->meta.transfer_id == 0xBABA'CACA); REQUIRE(response->payload_len == 5); REQUIRE(0 == std::memcmp(response->payload, "\x01\x02\x03\x04\x05", 5)); @@ -286,8 +284,7 @@ TEST_CASE("kocherga_serial::SerialNode service") REQUIRE(response->meta.source == 2222); REQUIRE(response->meta.destination == 3210); REQUIRE(response->meta.data_spec == (static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | - kocherga::serial::detail::Transfer::Metadata::DataSpecResponseFlag)); + kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag)); REQUIRE(response->meta.transfer_id == 0xBABA'CACA); REQUIRE(response->payload_len == 5); REQUIRE(0 == std::memcmp(response->payload, "\x01\x02\x03\x04\x05", 5)); @@ -299,8 +296,10 @@ TEST_CASE("kocherga_serial::SerialNode service") kocherga::serial::detail::Transfer request; request.meta.source = 3333; request.meta.destination = 2222; - request.meta.data_spec = static_cast(kocherga::ServiceID::NodeExecuteCommand) | - kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag; + request.meta.data_spec = + static_cast(kocherga::ServiceID::NodeExecuteCommand) | + static_cast(kocherga::serial::detail::Transfer::Metadata::DataSpecServiceFlag | + kocherga::serial::detail::Transfer::Metadata::DataSpecRequestFlag); request.meta.transfer_id = 0xBABA'BABA; request.payload_len = 6; request.payload = reinterpret_cast("\x05\x04\x03\x02\x01\x00"); diff --git a/tests/unit/serial/test_stream.cpp b/tests/unit/serial/test_stream.cpp index 0fdda11..806fd24 100644 --- a/tests/unit/serial/test_stream.cpp +++ b/tests/unit/serial/test_stream.cpp @@ -97,7 +97,7 @@ TEST_CASE("serial::transmit") using Buf = std::vector; // The reference dump has been obtained as follows: - // import pycyphal.serial + // import pycyphal.transport.serial // tr = pycyphal.transport.serial.SerialTransport('loop://', local_node_id=1234, baudrate=115200) // pm = pycyphal.transport.PayloadMetadata(1024) // ds = pycyphal.transport.MessageDataSpecifier(2345) @@ -111,16 +111,16 @@ TEST_CASE("serial::transmit") { const Buf reference = { 0x00, // starting delimiter - 0x01, // COBS starting stuff byte, next byte zero - 0x08, // version 0, next zero 8 bytes later + 0x0b, // COBS starting stuff byte, next zero 11 bytes later + 0x01, // version 1 0x05, // priority low 0xd2, 0x04, // src node-ID 1234 0xff, 0xff, // dst node-ID broadcast 0x29, 0x09, // subject-ID 2345 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, // reserved zeros 0x57, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // transfer-ID 1111 - 0x01, 0x01, 0x06, 0x80, // frame index EOT 0x80000000 (single-frame transfer) - 0x02, 0xf4, 0x6f, 0x2a, // header CRC 0x2a6ff402 + 0x01, 0x01, 0x02, 0x80, // frame index EOT 0x80000000 (single-frame transfer) + 0x01, 0x03, // user data 0x0000, next zero 3 bytes later + 0x4f, 0x83, // header CRC 0x4f83 0x01, 0x01, 0x01, 0x01, // payload CRC 0x00000000 0x00 // final delimiter }; @@ -148,16 +148,16 @@ TEST_CASE("serial::transmit") { const Buf reference = { 0x00, // starting delimiter - 0x01, // COBS starting stuff byte, next byte zero - 0x08, // version 0, next zero 8 bytes later + 0x0b, // COBS starting stuff byte, next zero 11 bytes later + 0x01, // version 1 0x05, // priority low 0xd2, 0x04, // src node-ID 1234 0xff, 0xff, // dst node-ID broadcast 0x29, 0x09, // subject-ID 2345 - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, // reserved zeros 0x57, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // transfer-ID 1111 - 0x01, 0x01, 0x06, 0x80, // frame index EOT 0x80000000 (single-frame transfer) - 0x02, 0xf4, 0x6f, 0x2a, // header CRC 0x2a6ff402 + 0x01, 0x01, 0x02, 0x80, // frame index EOT 0x80000000 (single-frame transfer) + 0x01, 0x03, // user data 0x0000, next zero 3 bytes later + 0x4f, 0x83, // header CRC 0x4f83 0x07, 0x01, 0x02, // payload [0, 1, 2] 0xfa, 0x4b, 0xfd, 0x92, // payload CRC 0x00, // final delimiter @@ -382,16 +382,16 @@ TEST_CASE("serial::StreamParser basic") // ', '.join(f'0x{x:02x}' for x in cobs.cobs.encode(bytes(hdr) + crc(hdr) + bytes(payload) + crc(bytes(payload)))) { const Buf chunk{ - 0x01, // COBS starting overhead byte - 0x03, // Version 0 + 0x04, // COBS starting overhead byte + 0x01, // Version 1 0x05, // Priority 5 - 0x7b, 0x15, // Source NID 123 - 0xff, 0xff, // Destination NID 456 + 0x7b, 0x0d, // Source NID 123 + 0xff, 0xff, // Destination NID broadcast 0xe1, 0x10, // Data specifier 4321 - 0x0d, 0xf0, 0xdd, 0xe0, 0xfe, 0x0f, 0xdc, 0xba, // 0xd2, 0x0a, 0x1f, 0xeb, 0x8c, 0xa9, 0x54, 0xab, // Transfer ID 12345678901234567890 - 0x01, 0x01, 0x0f, 0x80, // Frame index, EOT 0 with EOT flag set - 0xad, 0x13, 0xce, 0xc6, // Header CRC computed with the help of PyCyphal + 0x01, 0x01, 0x02, 0x80, // Frame index, EOT 0 with EOT flag set + 0x01, 0x0c, // user data 0x0000 + 0x3e, 0x9c, // Header CRC computed with the help of PyCyphal 0x01, 0x02, 0x03, 0x04, 0x05, // Payload 1 2 3 4 5 0xab, 0x8f, 0x51, 0x53, // Payload CRC }; @@ -413,16 +413,16 @@ TEST_CASE("serial::StreamParser basic") // VALID RESPONSE { const Buf chunk = { - 0x01, - 0x18, // Version 0 - 0x02, // Priority 1 + 0x11, + 0x01, // Version 1 + 0x02, // Priority 2 0x8e, 0x01, // Source NID 398 0x11, 0x01, // Destination NID 273 - 0x9e, 0xc0, // Data specifier response 158 - 0x0d, 0xf0, 0xdd, 0xe0, 0xfe, 0x0f, 0xdc, 0xba, // + 0x9e, 0x80, // Data specifier response 158 0xd2, 0x0a, 0x1f, 0xeb, 0x8c, 0xa9, 0x54, 0xab, // Transfer ID 12345678901234567890 - 0x01, 0x01, 0x0f, 0x80, // Frame index, EOT 0 with EOT flag set - 0x50, 0x42, 0x9b, 0x1f, // Header CRC + 0x01, 0x01, 0x02, 0x80, // Frame index, EOT 0 with EOT flag set + 0x01, 0x0c, // user data 0x0000 + 0xbd, 0x2c, // Header CRC 0x01, 0x02, 0x03, 0x04, 0x05, // Payload 1 2 3 4 5 0xab, 0x8f, 0x51, 0x53 // Payload CRC }; @@ -437,7 +437,7 @@ TEST_CASE("serial::StreamParser basic") REQUIRE(tr->meta.priority == 2); REQUIRE(tr->meta.source == 398); REQUIRE(tr->meta.destination == 273); - REQUIRE(tr->meta.data_spec == 0xC09E); + REQUIRE(tr->meta.data_spec == 0x809E); REQUIRE(tr->meta.transfer_id == 12'345'678'901'234'567'890ULL); REQUIRE(!tr->meta.isRequest()); REQUIRE(tr->meta.isResponse() == 158); @@ -446,16 +446,16 @@ TEST_CASE("serial::StreamParser basic") // BAD HEADER CRC { const Buf chunk = { - 0x01, - 0x18, // Version 0 - 0x02, // Priority 1 + 0x11, + 0x01, // Version 1 + 0x02, // Priority 2 0x8e, 0x01, // Source NID 398 0x11, 0x01, // Destination NID 273 0x9e, 0xc0, // Data specifier response 158 - 0x0d, 0xf0, 0xdd, 0xe0, 0xfe, 0x0f, 0xdc, 0xba, // 0xd2, 0x0a, 0x1f, 0xeb, 0x8c, 0xa9, 0x54, 0xab, // Transfer ID 12345678901234567890 - 0x01, 0x01, 0x0f, 0x80, // Frame index, EOT 0 with EOT flag set - 0x50, 0x42, 0x9b, 0x0f, // Header CRC MSB FLIP ERROR + 0x01, 0x01, 0x02, 0x80, // Frame index, EOT 0 with EOT flag set + 0x01, 0x0c, // user data 0x0000 + 0x79, 0x1e, // Header CRC MSB FLIP ERROR 0x01, 0x02, 0x03, 0x04, 0x05, // Payload 1 2 3 4 5 0xab, 0x8f, 0x51, 0x53 // Payload CRC }; @@ -501,8 +501,8 @@ TEST_CASE("serial::StreamParser error") out = sp.update(x); return true; }); - const auto inject = [&enc](const auto& data) { - kocherga::serial::detail::CRC32C crc_computer; + const auto inject = [&enc](const auto& data, const auto crc_type) { + typename decltype(crc_type)::type crc_computer; for (const std::uint8_t x : data) { crc_computer.update(x); @@ -513,8 +513,8 @@ TEST_CASE("serial::StreamParser error") REQUIRE(enc.push(x)); } }; - inject(header); - inject(payload); + inject(header, std::common_type{}); + inject(payload, std::common_type{}); REQUIRE(enc.end()); return out; }; @@ -522,21 +522,23 @@ TEST_CASE("serial::StreamParser error") // Self-test with max length payload { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0x8a, 0x0c, // Destination NID - 0x4d, 0x81, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0xc1, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00 // User data }; const auto tr = feed(header, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); REQUIRE(tr); REQUIRE(tr->meta.priority == 7); REQUIRE(tr->meta.source == 1234); REQUIRE(tr->meta.destination == 3210); - REQUIRE(tr->meta.data_spec == (Transfer::Metadata::DataSpecServiceFlag | 333U)); + REQUIRE(tr->meta.data_spec == (static_cast(Transfer::Metadata::DataSpecServiceFlag | + Transfer::Metadata::DataSpecRequestFlag) | + 333U)); REQUIRE(tr->meta.transfer_id == 0x1234'5678'9012'3456ULL); REQUIRE(tr->payload_len == 10); REQUIRE(0 == std::memcmp(tr->payload, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09", 10)); @@ -547,22 +549,21 @@ TEST_CASE("serial::StreamParser error") // Self-test with zero length payload { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0x8a, 0x0c, // Destination NID - 0x4d, 0xc1, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0x81, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00 // User data }; const auto tr = feed(header, {}); REQUIRE(tr); REQUIRE(tr->meta.priority == 7); REQUIRE(tr->meta.source == 1234); REQUIRE(tr->meta.destination == 3210); - REQUIRE(tr->meta.data_spec == - (333U | Transfer::Metadata::DataSpecServiceFlag | Transfer::Metadata::DataSpecResponseFlag)); + REQUIRE(tr->meta.data_spec == (333U | Transfer::Metadata::DataSpecServiceFlag)); REQUIRE(tr->meta.transfer_id == 0x1234'5678'9012'3456ULL); REQUIRE(tr->payload_len == 0); REQUIRE(!tr->meta.isRequest()); @@ -572,14 +573,14 @@ TEST_CASE("serial::StreamParser error") // Payload one byte too long { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0x8a, 0x0c, // Destination NID - 0x4d, 0x81, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0xc1, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00 // User data }; const auto tr = feed(header, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); REQUIRE(!tr); @@ -588,14 +589,14 @@ TEST_CASE("serial::StreamParser error") // Bad version { const Buf header{ - 0x01, // Version + 0x00, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0x8a, 0x0c, // Destination NID - 0x4d, 0x81, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0xc1, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00 // User data }; const auto tr = feed(header, {}); REQUIRE(!tr); @@ -604,14 +605,14 @@ TEST_CASE("serial::StreamParser error") // Cyphal/serial transfers cannot be multi-frame { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0x8a, 0x0c, // Destination NID - 0x4d, 0x81, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0xc1, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x00, // Frame index, EOT (cleared) + 0x00, 0x00, // User data }; const auto tr = feed(header, {}); REQUIRE(!tr); @@ -620,14 +621,14 @@ TEST_CASE("serial::StreamParser error") // Service transfers cannot be broadcast { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0xff, 0xff, // Destination NID - 0x4d, 0xc1, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0x81, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00, // User data }; const auto tr = feed(header, {}); REQUIRE(!tr); @@ -636,14 +637,14 @@ TEST_CASE("serial::StreamParser error") // Service transfers cannot be anonymous { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xff, 0xff, // Source NID 0x01, 0x01, // Destination NID - 0x4d, 0xc1, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x4d, 0x81, // Data specifier 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00, // User data }; const auto tr = feed(header, {}); REQUIRE(!tr); @@ -652,14 +653,14 @@ TEST_CASE("serial::StreamParser error") // Message transfers cannot be unicast { const Buf header{ - 0x00, // Version + 0x01, // Version 0x07, // Priority 0xd2, 0x04, // Source NID 0x8a, 0x0c, // Destination NID 0x4d, 0x01, // Data specifier - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, // Transfer ID 0x00, 0x00, 0x00, 0x80, // Frame index, EOT + 0x00, 0x00, // User data }; const auto tr = feed(header, {}); REQUIRE(!tr); diff --git a/tests/unit/test_misc.cpp b/tests/unit/test_misc.cpp index ad8d3e1..b6fced8 100644 --- a/tests/unit/test_misc.cpp +++ b/tests/unit/test_misc.cpp @@ -9,7 +9,7 @@ #include #include -TEST_CASE("CRC") +TEST_CASE("CRC64") { kocherga::CRC64 crc; const char* val = "12345"; @@ -34,6 +34,30 @@ TEST_CASE("CRC") REQUIRE(0xFCAC'BEBD'5931'A992ULL == (~crc.get())); } +TEST_CASE("CRC16-CCITT") +{ + kocherga::detail::CRC16CCITT crc; + crc.update(static_cast('1')); + crc.update(static_cast('2')); + crc.update(static_cast('3')); + crc.update(static_cast('4')); + crc.update(static_cast('5')); + crc.update(static_cast('6')); + crc.update(static_cast('7')); + crc.update(static_cast('8')); + crc.update(static_cast('9')); + + REQUIRE(0x29B1U == crc.get()); + REQUIRE(crc.getBytes().at(0) == 0x29U); + REQUIRE(crc.getBytes().at(1) == 0xB1U); + + REQUIRE(!crc.isResidueCorrect()); + crc.update(0x29U); + crc.update(0xB1U); + REQUIRE(crc.isResidueCorrect()); + REQUIRE(0x0000U == crc.get()); +} + TEST_CASE("VolatileStorage") { struct Data From 0ffc86de33d9fb29cfb07c9b8b93d9e29931d5b8 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Wed, 3 Apr 2024 12:37:26 +0300 Subject: [PATCH 2/3] Do not attempt to run sonar scanner if SONAR_TOKEN is not exported Secrets are not exported on builds originating from forks --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 395c042..3a1fa9c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -120,6 +120,7 @@ jobs: SONAR_SCANNER_VERSION: 4.6.1.2450 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + if: env.SONAR_TOKEN != '' steps: - uses: actions/checkout@v2 with: From 332ba5a668deb0e00c2b36326b67ea5588bc61df Mon Sep 17 00:00:00 2001 From: Kalyan Sriram Date: Wed, 3 Apr 2024 14:53:04 -0500 Subject: [PATCH 3/3] tests: move crc32c from serial to shared --- tests/unit/serial/test_misc.cpp | 35 --------------------------------- tests/unit/test_misc.cpp | 28 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 35 deletions(-) delete mode 100644 tests/unit/serial/test_misc.cpp diff --git a/tests/unit/serial/test_misc.cpp b/tests/unit/serial/test_misc.cpp deleted file mode 100644 index e3f4557..0000000 --- a/tests/unit/serial/test_misc.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// This software is distributed under the terms of the MIT License. -// Copyright (c) 2020 Zubax Robotics. -// Author: Pavel Kirienko - -#include "kocherga_serial.hpp" // NOLINT include order: include Kocherga first to ensure no headers are missed. -#include "catch.hpp" -#include - -TEST_CASE("serial::CRC32C") -{ - kocherga::serial::detail::CRC32C crc; - crc.update(static_cast('1')); - crc.update(static_cast('2')); - crc.update(static_cast('3')); - crc.update(static_cast('4')); - crc.update(static_cast('5')); - crc.update(static_cast('6')); - crc.update(static_cast('7')); - crc.update(static_cast('8')); - crc.update(static_cast('9')); - - REQUIRE(0xE306'9283UL == crc.get()); - REQUIRE(crc.getBytes().at(0) == 0x83U); - REQUIRE(crc.getBytes().at(1) == 0x92U); - REQUIRE(crc.getBytes().at(2) == 0x06U); - REQUIRE(crc.getBytes().at(3) == 0xE3U); - - REQUIRE(!crc.isResidueCorrect()); - crc.update(0x83U); - crc.update(0x92U); - crc.update(0x06U); - crc.update(0xE3U); - REQUIRE(crc.isResidueCorrect()); - REQUIRE(0xB798'B438UL == (~crc.get())); -} diff --git a/tests/unit/test_misc.cpp b/tests/unit/test_misc.cpp index b6fced8..4aed7ff 100644 --- a/tests/unit/test_misc.cpp +++ b/tests/unit/test_misc.cpp @@ -58,6 +58,34 @@ TEST_CASE("CRC16-CCITT") REQUIRE(0x0000U == crc.get()); } +TEST_CASE("serial::CRC32C") +{ + kocherga::detail::CRC32C crc; + crc.update(static_cast('1')); + crc.update(static_cast('2')); + crc.update(static_cast('3')); + crc.update(static_cast('4')); + crc.update(static_cast('5')); + crc.update(static_cast('6')); + crc.update(static_cast('7')); + crc.update(static_cast('8')); + crc.update(static_cast('9')); + + REQUIRE(0xE306'9283UL == crc.get()); + REQUIRE(crc.getBytes().at(0) == 0x83U); + REQUIRE(crc.getBytes().at(1) == 0x92U); + REQUIRE(crc.getBytes().at(2) == 0x06U); + REQUIRE(crc.getBytes().at(3) == 0xE3U); + + REQUIRE(!crc.isResidueCorrect()); + crc.update(0x83U); + crc.update(0x92U); + crc.update(0x06U); + crc.update(0xE3U); + REQUIRE(crc.isResidueCorrect()); + REQUIRE(0xB798'B438UL == (~crc.get())); +} + TEST_CASE("VolatileStorage") { struct Data