header_utils
Loading...
Searching...
No Matches
bit_view.h
1
4
5#pragma once
6
7#include <span>
8#include <algorithm>
9#include "bits.h"
10#include "ranges.h"
11
12namespace ghassanpl
13{
16
18 template <bit_integral INTEGER_TYPE>
19 struct bit_view
20 {
21 // TODO: Should this have extent, like span<> ?
22 // TODO: Should this store a bitcount and bitstart, so we can have views over non-integral bit counts?
23
24 using integer_type = INTEGER_TYPE;
26 static constexpr size_t integer_type_bit_count = bit_count<integer_type>;
27
28 static constexpr bool is_const = std::is_const_v<integer_type>;
29
30 bit_view() noexcept = default;
31 bit_view(std::span<integer_type> span) noexcept : m_integers(span) {}
32
33 bit_view(bit_view const&) noexcept = default;
34 bit_view(bit_view&&) noexcept = default;
35 bit_view& operator=(bit_view const&) noexcept = default;
36 bit_view& operator=(bit_view&&) noexcept = default;
37
38 template <typename INTEGER>
39 struct base_iterator
40 {
41 using value_type = bit_reference<INTEGER>;
42 using difference_type = ptrdiff_t;
43
44 [[nodiscard]] constexpr value_type operator*() const
45 {
46 return bit_reference<INTEGER>{ this->integer_at_bit(bit_number), this->real_bit_at_bit(bit_number) };
47 }
48
49 template <typename U>
50 [[nodiscard]] constexpr difference_type operator-(base_iterator<U> other) const
51 {
52 return bit_number - other.bit_number;
53 }
54 constexpr base_iterator& operator++()
55 {
56 if (bit_number < bit_view(integers).size())
57 ++bit_number;
58 return *this;
59 }
60 constexpr base_iterator operator++(int)
61 {
62 auto ret = *this;
63 ++(*this);
64 return ret;
65 }
66 constexpr base_iterator& operator--()
67 {
68 if (bit_number > 0)
69 --bit_number;
70 return *this;
71 }
72 constexpr base_iterator operator--(int)
73 {
74 auto ret = *this;
75 --(*this);
76 return ret;
77 }
78 constexpr base_iterator& operator+=(difference_type n)
79 {
80 bit_number = std::clamp(bit_number + n, 0, bit_view(integers).size());
81 return *this;
82 }
83 constexpr base_iterator& operator-=(difference_type n)
84 {
85 bit_number = std::clamp(bit_number + n, 0, bit_view(integers).size());
86 return *this;
87 }
88
89 [[nodiscard]] constexpr base_iterator operator+(difference_type n) const;
90 friend constexpr base_iterator operator+(difference_type, base_iterator);
91
92 [[nodiscard]] constexpr base_iterator operator-(difference_type) const;
93 [[nodiscard]] constexpr value_type operator[](difference_type) const;
94
95 template <typename U>
96 [[nodiscard]] constexpr bool operator==(base_iterator<U> const& other) const noexcept
97 {
98 return integers.data() == other.integers.data() && integers.size() == other.integers.size() && bit_number == other.bit_number;
99 }
100
101 template <typename U>
102 [[nodiscard]] constexpr std::strong_ordering operator<=>(base_iterator<U> const& other) const noexcept
103 {
104 return std::tie(integers.data(), integers.size(), bit_number) <=> std::tie(other.integers.data(), other.integers.size(), other.bit_number);
105 }
106
107 base_iterator() noexcept = default;
108
109 base_iterator(base_iterator const&) noexcept = default;
110 base_iterator(base_iterator&&) noexcept = default;
111 base_iterator& operator=(base_iterator const&) noexcept = default;
112 base_iterator& operator=(base_iterator&&) noexcept = default;
113
114 private:
115
116 base_iterator(std::span<INTEGER> span, size_t num) noexcept : integers(span), bit_number(num) {}
117 friend struct bit_view;
118
119 [[nodiscard]] constexpr INTEGER& integer_at_bit(size_t bit) const { return ::ghassanpl::at(integers, bit / integer_type_bit_count); }
120 [[nodiscard]] static constexpr size_t real_bit_at_bit(size_t bit) { return bit % integer_type_bit_count; }
121
122 std::span<INTEGER> integers;
123 size_t bit_number{};
124 };
125
126 using iterator = base_iterator<INTEGER_TYPE>;
127 using const_iterator = base_iterator<INTEGER_TYPE const>;
128
129 static_assert(std::random_access_iterator<iterator>);
130
131 [[nodiscard]] constexpr const_iterator begin() const { return const_iterator{ m_integers, 0 }; }
132 [[nodiscard]] constexpr const_iterator end() const { return const_iterator{ m_integers, size() }; }
133 [[nodiscard]] constexpr iterator begin() { return iterator{ m_integers, 0 }; }
134 [[nodiscard]] constexpr iterator end() { return iterator{ m_integers, size() }; }
135 [[nodiscard]] constexpr const_iterator cbegin();
136 [[nodiscard]] constexpr const_iterator cend();
137
138 [[nodiscard]] constexpr element_type at(size_t index) requires (!is_const)
139 {
140 return bit_reference<integer_type>{ integer_at_bit(index), real_bit_at_bit(index) };
141 }
142
143 [[nodiscard]] constexpr element_type at(size_t index) const
144 {
145 return bit_reference<integer_type const>{ integer_at_bit(index), real_bit_at_bit(index) };
146 }
147
148 [[nodiscard]] constexpr element_type operator[](size_t index) requires (!is_const)
149 {
150 return bit_reference<integer_type>{ integer_at_bit_unsafe(index), real_bit_at_bit(index) };
151 }
152
153 [[nodiscard]] constexpr element_type operator[](size_t index) const
154 {
155 return bit_reference<integer_type const>{ integer_at_bit_unsafe(index), real_bit_at_bit(index) };
156 }
157
158 constexpr void set(size_t index) requires (!is_const);
159 constexpr void clear(size_t index) requires (!is_const);
160 [[nodiscard]] constexpr bool is_set(size_t index) const;
161
162 constexpr void set_all() requires (!is_const);
163 constexpr void clear_all() requires (!is_const);
164 constexpr void toggle_all() requires (!is_const);
165
166 [[nodiscard]] constexpr bool are_all_set() const;
167
168 template <typename U>
169 [[nodiscard]] constexpr bool are_all_set(bit_view<U> const& bit_set) const;
170
171 [[nodiscard]] constexpr bool are_any_set() const;
172
173 template <typename U>
174 [[nodiscard]] constexpr bool are_any_set(bit_view<U> const& bit_set) const;
175
176 [[nodiscard]] constexpr inline size_t size() const noexcept { return size_t(m_integers.size() * integer_type_bit_count); }
177
178 [[nodiscard]] constexpr integer_type& integer_at_bit(size_t bit) { return ::ghassanpl::at(m_integers, bit / integer_type_bit_count); }
179 [[nodiscard]] constexpr integer_type const& integer_at_bit(size_t bit) const { return ::ghassanpl::at(m_integers, bit / integer_type_bit_count); }
180
181 [[nodiscard]] static constexpr size_t real_bit_at_bit(size_t bit) { return bit % integer_type_bit_count; }
182
183 [[nodiscard]] constexpr auto integers() const noexcept { return m_integers; }
184
186
187 template <typename U>
188 constexpr void copy_to(bit_view<U>& target) const requires (!std::is_const_v<U>);
189 template <typename U>
190 constexpr void and_with(bit_view<U>& target) const requires (!std::is_const_v<U>);
191 template <typename U>
192 constexpr void or_with(bit_view<U>& target) const requires (!std::is_const_v<U>);
193 template <typename U>
194 constexpr void xor_with(bit_view<U>& target) const requires (!std::is_const_v<U>);
195
196 [[nodiscard]] constexpr size_t find_first_bit(bool that_is) const noexcept;
197 [[nodiscard]] constexpr size_t find_last_bit(bool that_is) const noexcept;
198 [[nodiscard]] constexpr size_t count_bits(bool that_are) const noexcept;
199
200 private:
201
202 [[nodiscard]] constexpr integer_type& integer_at_bit_unsafe(size_t bit) { return m_integers[bit / integer_type_bit_count]; }
203 [[nodiscard]] constexpr integer_type const& integer_at_bit_unsafe(size_t bit) const { return m_integers[bit / integer_type_bit_count]; }
204
205 std::span<integer_type> m_integers;
206 };
207
208 static_assert(std::ranges::random_access_range<bit_view<int>>);
209
210 template <std::ranges::contiguous_range RANGE_TYPE>
211 bit_view(RANGE_TYPE& T) -> bit_view<std::ranges::range_value_t<std::decay_t<decltype(T)>>>;
212 template <std::ranges::contiguous_range RANGE_TYPE>
213 bit_view(RANGE_TYPE const& T) -> bit_view<std::ranges::range_value_t<std::decay_t<decltype(T)>> const>;
214
215 template <std::ranges::contiguous_range RANGE_TYPE>
216 [[nodiscard]] auto make_bit_reference(RANGE_TYPE&& range, size_t bit_num)
217 {
218 using range_value = std::remove_pointer_t<typename std::iterator_traits<std::ranges::iterator_t<decltype(range)>>::pointer>;
219 return bit_reference<range_value>{bit_view<range_value>{range}.integer_at_bit(bit_num), bit_num % bit_count<range_value>};
220 }
221
222 template <size_t BIT_NUM, std::ranges::contiguous_range RANGE_TYPE>
223 [[nodiscard]] auto make_bit_reference(RANGE_TYPE&& range)
224 {
225 using range_value = std::remove_pointer_t<typename std::iterator_traits<std::ranges::iterator_t<decltype(range)>>::pointer>;
227 }
228
229 template <bit_integral VALUE_TYPE>
230 [[nodiscard]] auto make_bit_reference(VALUE_TYPE& value, size_t bit_num)
231 {
232 return bit_reference<VALUE_TYPE>{value, bit_num};
233 }
234
235 template <size_t BIT_NUM, bit_integral VALUE_TYPE>
236 [[nodiscard]] auto make_bit_reference(VALUE_TYPE& value)
237 {
238 static_assert(BIT_NUM < bit_count<VALUE_TYPE>, "BIT_NUM template argument can't be greater than or equal to the number of bits in the value type");
240 }
241
243}
244
245template <ghassanpl::bit_integral VALUE_TYPE>
246struct std::hash<ghassanpl::bit_view<VALUE_TYPE>>
247{
248 [[nodiscard]] size_t operator()(ghassanpl::bit_view<VALUE_TYPE> view) const noexcept
249 {
250 return hash_range(view.integers());
251 }
252};
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
std::ranges::range_value_t< std::decay_t< RANGE > > range_value
Helper template to get the value type of a type that decays to a range
Definition ranges.h:26
Primary namespace for everything in this library.
Definition align+rec2.h:10
Models a reference to a specific bit in a variable. Can be a statically-defined bit number (BIT_NUM !...
Definition bits.h:165
A view over an integral value allowing for iteration and modification of its individual bits.
Definition bit_view.h:20
constexpr void copy_to(bit_view< U > &target) const
[[nodiscard]] constexpr bit_view<integer_type> subview(size_t starting_at_bit, size_t bit_count) cons...