#pragma once #include "packbits-impl.hpp" #include "compat/assert.hpp" #include #include #include namespace floormat::Pack_impl { template struct input_field final { static_assert(LENGTH > 0); static_assert(LENGTH <= sizeof(T)*8); static constexpr size_t Length = LENGTH; T value; constexpr T operator*() const { return value; } constexpr operator T() const { return value; } }; template struct input { static_assert(LEFT > 0); static_assert(LEFT <= sizeof(T)*8); static constexpr size_t Left = LEFT; T value; template struct next_ { static_assert(N <= LEFT); using type = input; }; template using next = typename next_::type; template constexpr CORRADE_ALWAYS_INLINE T get() const { static_assert(N > 0); static_assert(N <= sizeof(T)*8); static_assert(N <= LEFT); return T(value & (T{1} << N) - T{1}); } template constexpr T advance() const { static_assert(N <= sizeof(T)*8); static_assert(N <= LEFT); return T(value >> N); } constexpr bool operator==(const input&) const noexcept = default; [[nodiscard]] constexpr inline bool check_zero() const { return value == T{0}; } }; template struct input { static constexpr size_t Left = 0; using Type = T; T value; template [[maybe_unused]] constexpr T get() const = delete; template [[maybe_unused]] constexpr T advance() const = delete; constexpr bool operator==(const input&) const noexcept = default; [[nodiscard]] constexpr inline bool check_zero() const { return true; } template struct next { static_assert(!N, "reading past the end"); static_assert( N, "reading past the end"); }; }; template struct is_input_field : std::bool_constant {}; template struct is_input_field> : std::bool_constant { static_assert(N > 0); }; template requires requires (Field& x) { { size_t{Field::Length} > 0 }; sizeof(std::decay_t); requires std::unsigned_integral>; } struct is_input_field : std::bool_constant {}; template constexpr CORRADE_ALWAYS_INLINE void read_(Tuple&& tuple, input st, std::index_sequence) { using U = std::decay_t; static_assert(Left <= sizeof(T)*8, "bits to read count too large"); static_assert(Left > 0, "too many bits to write"); static_assert(std::tuple_size_v >= sizeof...(Is)+1, "index count larger than tuple element count"); static_assert(I < std::tuple_size_v, "too few tuple elements"); using Field = std::decay_t>; static_assert(is_input_field{}, "tuple element must be input_field"); constexpr size_t Size = Field::Length; static_assert(Size <= Left, "data type too small"); using next_type = typename input::template next; std::get(tuple).value = st.template get(); T next_value = st.template advance(); read_(floormat::forward(tuple), next_type{ next_value }, std::index_sequence{}); } template constexpr CORRADE_ALWAYS_INLINE void read_(Tuple&&, input st, std::index_sequence<>) { if (!st.check_zero()) [[unlikely]] throw_on_read_nonzero(); } } // namespace floormat::Pack_impl namespace floormat { template constexpr void pack_read(Tuple&& tuple, T value) requires requires (const Tuple& tuple) { std::tuple_size_v > 0uz; Pack_impl::is_input_field(tuple))>>::value; } { constexpr size_t nbits = sizeof(T)*8, tuple_size = std::tuple_size_v>; Pack_impl::read_(floormat::forward(tuple), Pack_impl::input{value}, std::make_index_sequence{}); } } // namespace floormat