Skip to content

Commit

Permalink
dirty
Browse files Browse the repository at this point in the history
  • Loading branch information
StarQTius committed Sep 22, 2024
1 parent 45262dc commit c3a230e
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 56 deletions.
69 changes: 57 additions & 12 deletions example/dynamixel-protocol-2.0.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <cinttypes>
#include <climits>
#include <iostream>

#include <upd/description.hpp>
Expand All @@ -11,14 +10,57 @@ ostream &operator<<(ostream &os, byte b) { return os << static_cast<int>(b); }

} // namespace std

using crc = upd::xuint<16>;

constexpr auto accumulate_crc(crc acc, upd::xuint<8> byte) noexcept -> crc {
constexpr auto crc_table = std::array {
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
};

auto i = ((acc >> 8) ^ byte) & 0xff;
return (acc << 8) ^ crc_table[i];
}

constexpr auto description = [] {
using namespace upd;
using namespace upd::literals;
using namespace upd::descriptor;

return constant<"header"_h>(0xfdffff, width<32>) | field<"id"_h>(unsigned_int, width<8>) |
field<"length"_h>(unsigned_int, width<16>) | field<"instruction"_h>(unsigned_int, width<8>) |
field<"checksum"_h>(unsigned_int, width<16>);
return
// constant<"header"_h>(0xfdffff, width<32>) | field<"id"_h>(unsigned_int, width<8>) |
// field<"length"_h>(unsigned_int, width<16>) | field<"instruction"_h>(unsigned_int, width<8>) |
checksum<"crc"_h>(accumulate_crc, width<16>, 0, all_fields);
}();

constexpr auto answer_description = [] {
Expand All @@ -29,16 +71,19 @@ constexpr auto answer_description = [] {
return constant<"header"_h>(0xfdffff, width<32>) | field<"id"_h>(unsigned_int, width<8>) |
field<"length"_h>(unsigned_int, width<16>) | field<"instruction"_h>(unsigned_int, width<8>) |
field<"error"_h>(unsigned_int, width<8>) | field<"model_number"_h>(unsigned_int, width<16>) |
field<"firmware_version"_h>(unsigned_int, width<8>) | field<"checksum"_h>(unsigned_int, width<16>);
field<"firmware_version"_h>(unsigned_int, width<8>) |
checksum<"crc"_h>(accumulate_crc, width<16>, 0, all_fields);
}();

struct serializer {
constexpr static auto bytewidth = 8;

template<typename XInteger, typename OutputIt>
void serialize_unsigned(XInteger value, OutputIt output) {
static_assert(upd::is_extended_integer_v<XInteger>, "`value` must be an instance of `extended_integer`");
static_assert(!upd::is_signed_v<XInteger>, "`value` must be unsigned");

constexpr auto byte_count = value.bitsize / CHAR_BIT;
constexpr auto byte_count = value.bitsize / bytewidth;

auto decomposition = value.decompose(upd::width<byte_count>);
std::copy(decomposition.begin(), decomposition.end(), output);
Expand All @@ -56,17 +101,17 @@ struct serializer {
abs = ~abs + 1;
}

constexpr auto byte_count = abs.bitsize / CHAR_BIT;
constexpr auto byte_count = abs.bitsize / bytewidth;

auto decomposition = abs.decompose(upd::width<byte_count>);
std::copy(decomposition.begin(), decomposition.end(), output);
}

template<typename InputIt, std::size_t Bitsize>
auto deserialize_unsigned(InputIt input, upd::width_t<Bitsize>) {
static_assert(Bitsize % CHAR_BIT == 0, "`Bitsize` must be a multiple of `CHAR_BIT`");
static_assert(Bitsize % bytewidth == 0, "`Bitsize` must be a multiple of `bytewidth`");

constexpr auto size = Bitsize / CHAR_BIT;
constexpr auto size = Bitsize / bytewidth;

auto byteseq = std::array<std::byte, size>{};
auto last_written = std::copy_n(input, size, byteseq.begin());
Expand All @@ -78,17 +123,17 @@ struct serializer {

template<typename InputIt, std::size_t Bitsize>
auto deserialize_signed(InputIt input, upd::width_t<Bitsize>) {
static_assert((Bitsize + 1) % CHAR_BIT == 0, "`Bitsize` must be a multiple of `CHAR_BIT`");
static_assert((Bitsize + 1) % bytewidth == 0, "`Bitsize` must be a multiple of `bytewidth`");

constexpr auto size = (Bitsize + 1) / CHAR_BIT;
constexpr auto size = (Bitsize + 1) / bytewidth;

auto byteseq = std::array<std::byte, size>{};
auto last_written = std::copy_n(input, size, byteseq.begin());

UPD_ASSERT(last_written == byteseq.end());

auto raw = upd::recompose_into_xuint(byteseq);
auto sign = (raw & upd::nth_bit<Bitsize> != 0);
auto sign = ((raw & upd::nth_bit<Bitsize>) != 0);
auto abs = (raw & upd::nth_bit<Bitsize>) ? ~raw + 1 : raw;

return sign ? -abs : abs;
Expand Down
120 changes: 110 additions & 10 deletions include/upd/description.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,43 @@ class iterator_reference {

namespace upd {

template<typename Iter, typename F>
class transformer_iterator {
public:
constexpr explicit transformer_iterator(Iter iter, F f) noexcept:
m_iter{iter},
m_f{std::move(f)}
{}

[[nodiscard]] constexpr auto operator*() -> decltype(auto) {
return m_f(*m_iter);
}

[[nodiscard]] constexpr auto operator++() -> transformer_iterator & {
++m_iter;
return *this;
}

private:
Iter m_iter;
F m_f;
};

enum class error {
none,
checksum_mismatch,
};

template<typename E>
template<typename E = error>
class unexpected {
public:
constexpr explicit unexpected(E e) noexcept : m_error{std::move(e)} {}
constexpr unexpected() noexcept = default;

constexpr unexpected(E e) noexcept : m_error{std::move(e)} {}

[[nodiscard]] constexpr operator bool() const noexcept { return static_cast<bool>(m_error); }

[[nodiscard]] constexpr auto error() noexcept -> E { return m_error; }
[[nodiscard]] constexpr auto error() const noexcept -> E { return m_error; }

private:
E m_error;
Expand Down Expand Up @@ -160,12 +187,19 @@ class expected {

namespace upd::descriptor {

struct all_fields_t {};

constexpr auto all_fields = all_fields_t{};

template<auto, bool Is_Signed, std::size_t Width>
constexpr auto field(signedness_t<Is_Signed>, width_t<Width>) noexcept(release);

template<auto, typename T, std::size_t Width>
constexpr auto constant(T, width_t<Width>) noexcept(release);

template<auto, typename BinaryOp, std::size_t Width, typename Init>
[[nodiscard]] constexpr auto checksum(BinaryOp, width_t<Width>, Init, all_fields_t) noexcept(release);

} // namespace upd::descriptor

namespace upd {
Expand All @@ -184,6 +218,7 @@ template<typename Range>
enum class field_tag {
pure_field,
constant,
checksum,
};

template<typename... Ts>
Expand All @@ -197,7 +232,13 @@ class description {
template<auto, typename T, std::size_t Width>
friend constexpr auto descriptor::constant(T, width_t<Width>) noexcept(release);

template<auto, typename BinaryOp, std::size_t Width, typename Init>
friend constexpr auto descriptor::checksum(BinaryOp, width_t<Width>, Init, descriptor::all_fields_t) noexcept(release);

public:
constexpr static auto identifiers = typelist<Ts...>{}
.transform_const([](auto field_type) { return field_type->identifier; });

template<typename... NamedValues>
[[nodiscard]] constexpr auto instantiate(NamedValues... nvs) const noexcept {
static_assert((detail::is_instance_of_v<NamedValues, named_value> && ...),
Expand All @@ -212,6 +253,8 @@ class description {
return kw<field.identifier> = representation{value};
} else if constexpr (field.tag == field_tag::constant) {
return kw<field.identifier> = field.value;
} else if constexpr (field.tag == field_tag::checksum) {
return kw<field.identifier> = field.value;
} else {
static_assert(UPD_ALWAYS_FALSE, "`field` is not a field object");
}
Expand All @@ -224,21 +267,49 @@ class description {

template<typename InputIt, typename Serializer>
[[nodiscard]] constexpr auto decode(InputIt src, Serializer &ser) const noexcept(release) {
auto impl = [&](const auto &field) {
auto err = unexpected{};
auto accumulators = m_fields
.filter([](auto field_type) { return field_type->tag == field_tag::checksum; })
.apply([](auto &&... checksum_fields) { return name_tuple((kw<checksum_fields.identifier> = checksum_fields.init)...); });

auto first_pass = [&](const auto &field) {
if constexpr (field.tag == field_tag::checksum) {
auto &[op, init, get_range] = field;
constexpr auto identifier = field.identifier;
constexpr auto range = get_range(identifiers);
auto accumulate_and_pass = [&acc = accumulators[kw<identifier>], op = op](auto identifier, const auto &value) mutable {
if constexpr (range.contains(identifier)) {
acc = op(acc, value);
}
return value;
};

return [f = std::move(accumulate_and_pass)](auto iter) { return transformer_iterator{iter, std::move(f)}; };
}
};

auto decorated_src = m_fields.transform(first_pass).fold_right(src, invoke);
auto second_pass = [&](const auto &field) {
constexpr auto identifier = field.identifier;
if constexpr (field.tag == field_tag::pure_field) {
constexpr auto width = field.width;
using representation = std::conditional_t<field.is_signed, xint<width>, xuint<width>>;
return kw<field.identifier> = deserialize_into_xinteger<representation>(detail::iterator_reference{src}, ser);
return kw<identifier> = deserialize_into_xinteger<representation>(detail::iterator_reference{src}, ser);
} else if constexpr (field.tag == field_tag::constant) {
return kw<field.identifier> = field.value;
return kw<identifier> = field.value;
} else if constexpr (field.tag == field_tag::checksum) {
constexpr auto width = field.width;
using representation = std::conditional_t<field.is_signed, xint<width>, xuint<width>>;
auto received = deserialize_into_xinteger<representation>(detail::iterator_reference{src}, ser);
if (err && accumulators[kw<identifier>] != received) {
err = error::checksum_mismatch;
}
} else {
static_assert(UPD_ALWAYS_FALSE, "`field` is not a field object");
static_assert(UPD_ALWAYS_FALSE, "`field.tag` is not a valid `field_tag` enumerator");
}
};

auto make_named_tuple = [](auto &&...nvs) { return (nvs, ...); };

auto retval = m_fields.transform(impl).apply(make_named_tuple);
auto retval = m_fields.transform(second_pass).apply(name_tuple);
return expected{std::move(retval)};
}

Expand Down Expand Up @@ -301,6 +372,35 @@ template<auto Identifier, typename T, std::size_t Width>
return description{tuple{retval}};
}

template<auto Identifier, typename BinaryOp, std::size_t Width, typename Init, typename GetRange>
struct checksum_t {
constexpr static auto tag = field_tag::checksum;
constexpr static auto identifier = Identifier;
constexpr static auto is_signed = false;
constexpr static auto width = Width;

BinaryOp op;
Init init;
GetRange get_range;
};

template<auto Identifier, typename BinaryOp, std::size_t Width, typename Init>
[[nodiscard]] constexpr auto checksum(BinaryOp op, width_t<Width>, Init init, all_fields_t) noexcept(release) {
constexpr auto get_identifier = [](auto field_type) { return field_type->identifier; };
constexpr auto is_not_checksum_identifier = [](auto identifier) { return identifier != Identifier; };

auto get_range = [](const auto &fields) {
return fields
.type_only()
.transform(get_identifier)
.filter_const(is_not_checksum_identifier);
};
auto retval = checksum_t<Identifier, BinaryOp, Width,
Init, decltype(get_range)>{std::move(op), init, get_range};

return description{tuple{retval}};
}

} // namespace upd::descriptor

namespace upd::literals {
Expand Down
57 changes: 54 additions & 3 deletions include/upd/integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,15 @@ class extended_integer {
constexpr extended_integer() noexcept = default;

template<typename T>
constexpr extended_integer(T n) noexcept(release) : m_value{(underlying)reduce_scalar(n, bitsize)} {
static_assert(std::is_integral_v<T> || std::is_enum_v<T>, "`n` must be an integer or an enumerator");
constexpr extended_integer(T n) noexcept(release) {

if constexpr (std::is_integral_v<T> || std::is_enum_v<T>) {
m_value = (underlying) reduce_scalar(n, bitsize);
} else if constexpr (is_extended_integer_v<T>) {
m_value = n.m_value;
} else {
static_assert(UPD_ALWAYS_FALSE, "`n` must be an integer, an enumerator, or an extended integer");
}
}

template<typename T, typename = std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>>
Expand Down Expand Up @@ -179,7 +186,7 @@ class extended_integer {

template<typename T>
[[nodiscard]] constexpr auto operator+(T rhs) const noexcept {
constexpr auto xrhs = to_extended_integer(rhs);
auto xrhs = to_extended_integer(rhs);
constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize);

auto retval = m_value + xrhs.m_value;
Expand All @@ -188,6 +195,50 @@ class extended_integer {
return xinteger<retval_bitsize, retval_type>{retval};
}

template<typename T>
[[nodiscard]] constexpr auto operator&(T rhs) const noexcept {
auto xrhs = to_extended_integer(rhs);
constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize);

auto retval = m_value & xrhs.m_value;
using retval_type = decltype(retval);

return xinteger<retval_bitsize, retval_type>{retval};
}

template<typename T>
[[nodiscard]] constexpr auto operator^(T rhs) const noexcept {
auto xrhs = to_extended_integer(rhs);
constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize);

auto retval = m_value ^ xrhs.m_value;
using retval_type = decltype(retval);

return xinteger<retval_bitsize, retval_type>{retval};
}

template<typename T>
[[nodiscard]] constexpr auto operator<<(T rhs) const noexcept {
auto xrhs = to_extended_integer(rhs);
constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize);

auto retval = m_value << xrhs.m_value;
using retval_type = decltype(retval);

return xinteger<retval_bitsize, retval_type>{retval};
}

template<typename T>
[[nodiscard]] constexpr auto operator>>(T rhs) const noexcept {
auto xrhs = to_extended_integer(rhs);
constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize);

auto retval = m_value >> xrhs.m_value;
using retval_type = decltype(retval);

return xinteger<retval_bitsize, retval_type>{retval};
}

[[nodiscard]] constexpr auto operator~() const noexcept {
static_assert(!is_signed, "Bitwise operators only work on unsigned values");

Expand Down
Loading

0 comments on commit c3a230e

Please sign in to comment.