summaryrefslogtreecommitdiffhomepage
path: root/serialize/packbits-read.hpp
blob: 4ad3aa74d265da6b495a054897f4184fc654454c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#pragma once
#include "packbits-impl.hpp"
#include "compat/assert.hpp"
#include <type_traits>
#include <concepts>
#include <tuple>

namespace floormat::Pack_impl {

template<std::unsigned_integral T, size_t LENGTH>
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<std::unsigned_integral T, size_t LEFT>
struct input
{
    static_assert(LEFT > 0);
    static_assert(LEFT <= sizeof(T)*8);
    static constexpr size_t Left = LEFT;

    T value;

    template<size_t N>
    struct next_
    {
        static_assert(N <= LEFT);
        using type = input<T, LEFT - N>;
    };
    template<size_t N> using next = typename next_<N>::type;

    template<size_t N>
    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<size_t N>
    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<std::unsigned_integral T>
struct input<T, 0>
{
    static constexpr size_t Left = 0;
    using Type = T;
    T value;

    template<size_t N> [[maybe_unused]] constexpr T get() const = delete;
    template<size_t N> [[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<size_t N> struct next
    {
        static_assert(!N, "reading past the end");
        static_assert( N, "reading past the end");
    };
};

template<typename T> struct is_input_field : std::bool_constant<false> {};
template<std::unsigned_integral T, size_t N> struct is_input_field<input_field<T, N>> : std::bool_constant<true> { static_assert(N > 0); };

template<typename Field>
requires requires (Field& x)
{
    { size_t{Field::Length} > 0 };
    sizeof(std::decay_t<decltype(x.value)>);
    requires std::unsigned_integral<std::decay_t<decltype(x.value)>>;
}
struct is_input_field<Field> : std::bool_constant<true> {};

template<std::unsigned_integral T, typename Tuple, size_t Left, size_t I, size_t... Is>
constexpr CORRADE_ALWAYS_INLINE void read_(Tuple&& tuple, input<T, Left> st, std::index_sequence<I, Is...>)
{
    using U = std::decay_t<Tuple>;
    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<U> >= sizeof...(Is)+1, "index count larger than tuple element count");
    static_assert(I < std::tuple_size_v<U>, "too few tuple elements");
    using Field = std::decay_t<std::tuple_element_t<I, U>>;
    static_assert(is_input_field<Field>{}, "tuple element must be input_field<T, N>");
    constexpr size_t Size = Field::Length;
    static_assert(Size <= Left, "data type too small");
    using next_type = typename input<T, Left>::template next<Size>;
    std::get<I>(tuple).value = st.template get<Size>();
    T next_value = st.template advance<Size>();
    read_(floormat::forward<Tuple>(tuple), next_type{ next_value }, std::index_sequence<Is...>{});
}

template<std::unsigned_integral T, typename Tuple, size_t Left>
constexpr CORRADE_ALWAYS_INLINE void read_(Tuple&&, input<T, Left> st, std::index_sequence<>)
{
    if (!st.check_zero()) [[unlikely]]
        throw_on_read_nonzero();
}

} // namespace floormat::Pack_impl

namespace floormat {

template<std::unsigned_integral T, typename Tuple> constexpr void pack_read(Tuple&& tuple, T value)
requires requires (const Tuple& tuple) {
    std::tuple_size_v<Tuple> > 0uz;
    Pack_impl::is_input_field<std::decay_t<decltype(std::get<0>(tuple))>>::value;
}
{
    constexpr size_t nbits = sizeof(T)*8,
                     tuple_size = std::tuple_size_v<std::decay_t<Tuple>>;
    Pack_impl::read_(floormat::forward<Tuple>(tuple),
                     Pack_impl::input<T, nbits>{value},
                     std::make_index_sequence<tuple_size>{});
}

} // namespace floormat