diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2022-10-27 18:28:42 +0200 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2022-10-27 18:28:42 +0200 |
commit | 4dce8e6aeb770fee1a7190526b04d03f3da69cb0 (patch) | |
tree | d38a607139d51b8b6399490fcca8bab303a5cd5d /serialize | |
parent | 96f2ef35072e9caf1e87debf0566d61c35a05d65 (diff) |
wip binary serializer
Diffstat (limited to 'serialize')
-rw-r--r-- | serialize/binary-serializer.cpp | 86 | ||||
-rw-r--r-- | serialize/binary-serializer.hpp | 82 | ||||
-rw-r--r-- | serialize/binary-serializer.inl | 54 |
3 files changed, 222 insertions, 0 deletions
diff --git a/serialize/binary-serializer.cpp b/serialize/binary-serializer.cpp new file mode 100644 index 00000000..08606768 --- /dev/null +++ b/serialize/binary-serializer.cpp @@ -0,0 +1,86 @@ +#include "binary-serializer.inl" +#include <array> + +namespace floormat::Serialize { + +#if 0 +template<std::size_t N> +struct byte_array_iterator final +{ + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = char; + using pointer = char*; + using reference = char&; + + constexpr byte_array_iterator(char (&buf)[N]) : buf{&buf}, i{0} {} + constexpr byte_array_iterator(char (&buf)[N], std::size_t i) : buf{&buf}, i{i} {} + + constexpr reference operator*() const { fm_assert(i < N); return (*buf)[i]; } + constexpr pointer operator->() { fm_assert(i < N); return &(*buf)[i]; } + constexpr byte_array_iterator<N>& operator++() noexcept { i++; return *this; } + constexpr byte_array_iterator<N> operator++(int) noexcept { byte_array_iterator<N> tmp = *this; ++(*this); return tmp; } + friend constexpr bool operator==(const byte_array_iterator<N>&& a, const byte_array_iterator<N>&& b) noexcept = default; + +private: + char (*buf)[N]; + std::size_t i; +}; + +template struct byte_array_iterator<sizeof(double)>; +#endif + +struct value_buf final { + value_u value; + std::int8_t len; + + explicit constexpr value_buf(value_u value, std::int8_t len) : value{value}, len{len} {} + constexpr bool operator==(const value_buf& o) const noexcept; + + fm_DECLARE_DEFAULT_MOVE_ASSIGNMENT_(value_buf); + fm_DECLARE_DEFAULT_COPY_ASSIGNMENT(value_buf); +}; + +constexpr bool value_buf::operator==(const value_buf& o) const noexcept +{ + const auto N = len; + if (N != o.len) + return false; + for (std::int8_t i = 0; i < N; i++) + if (value.bytes[i] != o.value.bytes[i]) + return false; + return true; +} + +[[maybe_unused]] +static constexpr bool test1() +{ + constexpr std::array<char, 4> bytes = { 1, 0, 1, 0 }; + auto x = binary_reader(bytes.cbegin(), bytes.cend()); + return x.read_u<unsigned char>().bytes[0] == 1 && + x.read_u<unsigned char>().bytes[0] == 0 && + x.read_u<unsigned char>().bytes[0] == 1 && + x.read_u<unsigned char>().bytes[0] == 0; +} +static_assert(test1()); + +[[maybe_unused]] +static constexpr bool test2() +{ + constexpr std::array<char, 4> bytes = { 1, 0, 1, 0 }; + auto r = binary_reader(bytes.cbegin(), bytes.cend()); + const auto x = r.read_u<int>(); + return x.bytes[0] == 1 && x.bytes[1] == 0 && x.bytes[2] == 1 && x.bytes[3] == 0; +} +static_assert(test2()); + +template<typename T> +[[maybe_unused]] static constexpr T maybe_byteswap(T x) +{ + if constexpr(std::endian::native == std::endian::big) + return std::byteswap(x); + else + return x; +} + +} // namespace floormat::Serialize diff --git a/serialize/binary-serializer.hpp b/serialize/binary-serializer.hpp new file mode 100644 index 00000000..19aa5548 --- /dev/null +++ b/serialize/binary-serializer.hpp @@ -0,0 +1,82 @@ +#pragma once +#include "compat/integer-types.hpp" +#include "compat/defs.hpp" + +#include <bit> +#include <iterator> +#include <concepts> +#include <type_traits> + +namespace floormat::Serialize { + +static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little); + +enum class value_type : std::uint8_t { + none, uc, u8, u16, u32, u64, + f32, f64, + COUNT +}; + +template<std::size_t N> struct make_integer; +template<std::size_t N> using make_integer_t = typename make_integer<N>::type; + +#define FM_SERIALIZE_MAKE_INTEGER(T) template<> struct make_integer<sizeof(T)> { using type = T; } +FM_SERIALIZE_MAKE_INTEGER(std::uint8_t); +FM_SERIALIZE_MAKE_INTEGER(std::uint16_t); +FM_SERIALIZE_MAKE_INTEGER(std::uint32_t); +FM_SERIALIZE_MAKE_INTEGER(std::uint64_t); +#undef FN_SERIALIZE_MAKE_INTEGER + +template<typename T> +concept integer = requires(T x) { + requires std::integral<T>; + requires sizeof(T) == sizeof(make_integer_t<sizeof(T)>); +}; + +union value_u { + alignas(alignof(double)) char bytes[8]; + unsigned char uc; + std::uint8_t u8; + std::uint16_t u16; + std::uint32_t u32; + std::uint64_t u64; + float f32; + double f64; +}; + +static_assert(sizeof(value_u) == 8); + +template<typename T> +concept char_sequence = requires(T& x, const T& cx) { + requires std::same_as<decltype(std::begin(x)), decltype(std::end(x))>; + requires std::same_as<decltype(std::cbegin(cx)), decltype(std::cend(cx))>; + requires std::forward_iterator<decltype(std::begin(x))>; + requires std::forward_iterator<decltype(std::cbegin(cx))>; + requires std::same_as<char, std::decay_t<decltype(*std::begin(x))>>; + requires std::same_as<char, std::decay_t<decltype(*std::cbegin(x))>>; +}; + +template<std::forward_iterator It> +struct binary_reader final { + fm_DECLARE_DEFAULT_MOVE_COPY_ASSIGNMENTS(binary_reader<It>); + constexpr binary_reader(It begin, It end) noexcept : it{begin}, end{end} {} + template<char_sequence Seq> + explicit constexpr binary_reader(const Seq& seq) noexcept : it{std::begin(seq)}, end{std::end(seq)} {} + constexpr ~binary_reader() noexcept; + + template<integer T> constexpr value_u read_u() noexcept; + template<std::floating_point T> constexpr value_u read_u() noexcept; + template<typename T> T read() noexcept; + + static_assert(std::is_same_v<char, std::decay_t<decltype(*std::declval<It>())>>); + +private: + It it, end; +}; + +template<std::forward_iterator It> binary_reader(It&& begin, It&& end) -> binary_reader<std::decay_t<It>>; + +template<typename Array> +binary_reader(Array&& array) -> binary_reader<std::decay_t<decltype(std::begin(array))>>; + +} // namespace floormat::Serialize diff --git a/serialize/binary-serializer.inl b/serialize/binary-serializer.inl new file mode 100644 index 00000000..9e7e58fa --- /dev/null +++ b/serialize/binary-serializer.inl @@ -0,0 +1,54 @@ +#pragma once + +#include "binary-serializer.hpp" + +namespace floormat::Serialize { + +template<std::forward_iterator It> +template<std::floating_point T> +constexpr value_u binary_reader<It>::read_u() noexcept +{ + value_u buf; + static_assert(sizeof(T) <= sizeof(buf)); + fm_assert(std::distance(it, end) >= sizeof(T)); + for (int i = 0; i < sizeof(T); i++) + buf.bytes[i] = *it++; + return buf; +} + +template<std::forward_iterator It> +template<typename T> +T binary_reader<It>::read() noexcept +{ + value_u buf = read_u<T>(); + return *reinterpret_cast<T>(buf.bytes); +} + +template<std::forward_iterator It> +constexpr binary_reader<It>::~binary_reader() noexcept +{ + fm_assert(it == end); +} + +template<std::forward_iterator It> +template<integer T> +constexpr value_u binary_reader<It>::read_u() noexcept +{ + value_u buf; + if (std::is_constant_evaluated()) + for (std::size_t i = 0; i < std::size(buf.bytes); i++) + buf.bytes[i] = 0; + static_assert(sizeof(T) <= sizeof(buf)); + fm_assert(std::distance(it, end) >= (std::ptrdiff_t) sizeof(T)); + if constexpr(std::endian::native == std::endian::big) + for (int i = sizeof(T) - 1; i >= 0; i--) + buf.bytes[i] = *it++; + else + for (std::size_t i = 0; i < sizeof(T); i++) + buf.bytes[i] = *it++; + return buf; +} + +} // namespace floormat::Serialize + + |