Skip to content

Commit

Permalink
kocherga_serial: update header format
Browse files Browse the repository at this point in the history
  • Loading branch information
coderkalyan committed Mar 9, 2024
1 parent 69e2131 commit 94c3902
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 25 deletions.
86 changes: 62 additions & 24 deletions kocherga/kocherga_serial.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@ constexpr std::uint8_t FrameDelimiter = 0x00; ///< Zeros cannot occur inside fr
static constexpr std::uint8_t FrameFormatVersion = 0;
static constexpr std::array<std::uint8_t, 4> FrameIndexEOTReference{0, 0, 0, 0x80};

/// Size-optimized implementation of CRC16-CCITT
class CRC16
{
public:
static constexpr std::size_t Size = 2;

void update(const std::uint8_t b) noexcept
{
value_ ^= static_cast<std::uint16_t>(b) << BitsPerByte;
for (auto i = 0U; i < BitsPerByte; i++)
{
value_ = (value_ >> 1U) ^ (((value_ & Top) != 0U) ? Poly : 0U); // NOLINT
}
}

[[nodiscard]] auto get() const noexcept { return value_ ^ Xor; }

[[nodiscard]] auto getBytes() const noexcept -> std::array<std::uint8_t, Size>
{
const auto x = get();
return {
static_cast<std::uint8_t>(x >> (BitsPerByte * 0U)),
static_cast<std::uint8_t>(x >> (BitsPerByte * 1U)),
};
}

[[nodiscard]] auto isResidueCorrect() const noexcept { return value_ == Residue; }

private:
static constexpr std::uint16_t Xor = 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_ = Xor;
};

/// Size-optimized implementation of CRC32-C (Castagnoli).
class CRC32C
{
Expand Down Expand Up @@ -256,7 +293,7 @@ class StreamParser
const auto dec = decoder_.feed(stream_byte);
if (std::holds_alternative<COBSDecoder::Delimiter>(dec))
{
if (inside_ && (offset_ >= TotalOverheadSize) && crc_.isResidueCorrect() && isMetaValid())
if (inside_ && (offset_ >= TotalOverheadSize) && transferCrc_.isResidueCorrect() && isMetaValid())
{
out = Transfer{
meta_,
Expand All @@ -269,13 +306,14 @@ class StreamParser
}
else if (const std::uint8_t* const decoded_byte = std::get_if<std::uint8_t>(&dec))
{
crc_.update(*decoded_byte);
if (offset_ < HeaderSize)
{
headerCrc_.update(*decoded_byte);
acceptHeader(*decoded_byte);
}
else
{
transferCrc_.update(*decoded_byte);
const auto buf_offset = offset_ - HeaderSize;
if (buf_offset < buf_.size())
{
Expand All @@ -299,10 +337,11 @@ class StreamParser
void reset() noexcept
{
decoder_.reset();
offset_ = 0;
inside_ = false;
crc_ = {};
meta_ = {};
offset_ = 0;
inside_ = false;
headerCrc_ = {};
transferCrc_ = {};
meta_ = {};
}

private:
Expand All @@ -327,7 +366,7 @@ class StreamParser
}
if (offset_ == (HeaderSize - 1U))
{
if (!crc_.isResidueCorrect())
if (!headerCrc_.isResidueCorrect())
{
reset(); // Header CRC error.
}
Expand All @@ -336,7 +375,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_ = {};
}
}

Expand Down Expand Up @@ -366,21 +404,22 @@ 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;
static constexpr std::size_t OffsetPriority = 1;
static constexpr std::pair<std::size_t, std::size_t> OffsetSource{2, 3};
static constexpr std::pair<std::size_t, std::size_t> OffsetDestination{4, 5};
static constexpr std::pair<std::size_t, std::size_t> OffsetDataSpec{6, 7};
static constexpr std::pair<std::size_t, std::size_t> OffsetTransferID{16, 23};
static constexpr std::pair<std::size_t, std::size_t> OffsetFrameIndexEOT{24, 27};
static constexpr std::pair<std::size_t, std::size_t> OffsetTransferID{8, 15};
static constexpr std::pair<std::size_t, std::size_t> OffsetFrameIndexEOT{16, 19};

COBSDecoder decoder_;
std::size_t offset_ = 0;
bool inside_ = false;
CRC32C crc_;
CRC16 headerCrc_;
CRC32C transferCrc_;
Transfer::Metadata meta_;
std::array<std::uint8_t, MaxPayloadSize + CRC32C::Size> buf_{};
};
Expand All @@ -392,20 +431,17 @@ template <typename Callback>
[[nodiscard]] inline auto transmit(const Callback& send_byte, const Transfer& tr) -> bool
{
COBSEncoder<const Callback&> encoder(send_byte);
CRC32C crc;
const auto out = [&crc, &encoder](const std::uint8_t b) {
crc.update(b);
CRC16 headerCrc;
const auto out = [&headerCrc, &encoder](const std::uint8_t b) {
headerCrc.update(b);
return encoder.push(b);
};
const auto out2 = [&out](const std::uint16_t bb) {
return out(static_cast<std::uint8_t>(bb)) && out(static_cast<std::uint8_t>(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);
}
auto tmp_transfer_id = tr.meta.transfer_id;
for (auto i = 0U; i < sizeof(std::uint64_t); i++)
{
Expand All @@ -416,26 +452,28 @@ template <typename Callback>
{
ok = ok && out(x);
}
for (const auto x : crc.getBytes())
for (const auto x : headerCrc.getBytes())
{
ok = ok && out(x);
}
crc = {}; // Now it's the payload CRC.

CRC32C transferCrc;
{
const auto* ptr = tr.payload;
for (std::size_t i = 0U; i < tr.payload_len; i++)
{
ok = ok && out(*ptr);
transferCrc.update(*ptr);
ok = ok && encoder.push(*ptr);
++ptr;
if (!ok)
{
break;
}
}
}
for (const auto x : crc.getBytes())
for (const auto x : transferCrc.getBytes())
{
ok = ok && out(x);
ok = ok && encoder.push(x);
}
return ok && encoder.end();
}
Expand Down
25 changes: 24 additions & 1 deletion tests/unit/serial/test_misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,30 @@
#include "catch.hpp"
#include <numeric>

TEST_CASE("serial::CRC")
TEST_CASE("serial::CRC3216-CCITT")
{
kocherga::serial::detail::CRC16 crc;
crc.update(static_cast<std::uint8_t>('1'));
crc.update(static_cast<std::uint8_t>('2'));
crc.update(static_cast<std::uint8_t>('3'));
crc.update(static_cast<std::uint8_t>('4'));
crc.update(static_cast<std::uint8_t>('5'));
crc.update(static_cast<std::uint8_t>('6'));
crc.update(static_cast<std::uint8_t>('7'));
crc.update(static_cast<std::uint8_t>('8'));
crc.update(static_cast<std::uint8_t>('9'));

REQUIRE(0x29B1U == crc.get());
REQUIRE(crc.getBytes().at(0) == 0xB1U);
REQUIRE(crc.getBytes().at(1) == 0x29U);

REQUIRE(!crc.isResidueCorrect());
crc.update(0xB1U);
crc.update(0x29U);
REQUIRE(crc.isResidueCorrect());
}

TEST_CASE("serial::CRC32C")
{
kocherga::serial::detail::CRC32C crc;
crc.update(static_cast<std::uint8_t>('1'));
Expand Down

0 comments on commit 94c3902

Please sign in to comment.