Branch data Line data Source code
1 : : // Copyright (c) 2022 The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #ifndef BITCOIN_UTIL_BITDEQUE_H
6 : : #define BITCOIN_UTIL_BITDEQUE_H
7 : :
8 : : #include <bitset>
9 : : #include <cstddef>
10 : : #include <deque>
11 : : #include <limits>
12 : : #include <stdexcept>
13 : : #include <tuple>
14 : :
15 : : /** Class that mimics std::deque<bool>, but with std::vector<bool>'s bit packing.
16 : : *
17 : : * BlobSize selects the (minimum) number of bits that are allocated at once.
18 : : * Larger values reduce the asymptotic memory usage overhead, at the cost of
19 : : * needing larger up-front allocations. The default is 4096 bytes.
20 : : */
21 : : template<int BlobSize = 4096 * 8>
22 : : class bitdeque
23 : : {
24 : : // Internal definitions
25 : : using word_type = std::bitset<BlobSize>;
26 : : using deque_type = std::deque<word_type>;
27 : : static_assert(BlobSize > 0);
28 : : static constexpr int BITS_PER_WORD = BlobSize;
29 : :
30 : : // Forward and friend declarations of iterator types.
31 : : template<bool Const> class Iterator;
32 : : template<bool Const> friend class Iterator;
33 : :
34 : : /** Iterator to a bitdeque element, const or not. */
35 : : template<bool Const>
36 : : class Iterator
37 : : {
38 : : using deque_iterator = std::conditional_t<Const, typename deque_type::const_iterator, typename deque_type::iterator>;
39 : :
40 : : deque_iterator m_it;
41 : : int m_bitpos{0};
42 : 0 : Iterator(const deque_iterator& it, int bitpos) : m_it(it), m_bitpos(bitpos) {}
43 : : friend class bitdeque;
44 : :
45 : : public:
46 : : using iterator_category = std::random_access_iterator_tag;
47 : : using value_type = bool;
48 : : using pointer = void;
49 : : using const_pointer = void;
50 : : using reference = std::conditional_t<Const, bool, typename word_type::reference>;
51 : : using const_reference = bool;
52 : : using difference_type = std::ptrdiff_t;
53 : :
54 : : /** Default constructor. */
55 : : Iterator() = default;
56 : :
57 : : /** Default copy constructor. */
58 : 0 : Iterator(const Iterator&) = default;
59 : :
60 : : /** Conversion from non-const to const iterator. */
61 : : template<bool ConstArg = Const, typename = std::enable_if_t<Const && ConstArg>>
62 : 0 : Iterator(const Iterator<false>& x) : m_it(x.m_it), m_bitpos(x.m_bitpos) {}
63 : :
64 : 0 : Iterator& operator+=(difference_type dist)
65 : : {
66 [ # # # # ]: 0 : if (dist > 0) {
67 [ # # # # ]: 0 : if (dist + m_bitpos >= BITS_PER_WORD) {
68 : 0 : ++m_it;
69 : 0 : dist -= BITS_PER_WORD - m_bitpos;
70 : 0 : m_bitpos = 0;
71 : 0 : }
72 : 0 : auto jump = dist / BITS_PER_WORD;
73 : 0 : m_it += jump;
74 : 0 : m_bitpos += dist - jump * BITS_PER_WORD;
75 [ # # # # ]: 0 : } else if (dist < 0) {
76 : 0 : dist = -dist;
77 [ # # # # ]: 0 : if (dist > m_bitpos) {
78 : 0 : --m_it;
79 : 0 : dist -= m_bitpos + 1;
80 : 0 : m_bitpos = BITS_PER_WORD - 1;
81 : 0 : }
82 : 0 : auto jump = dist / BITS_PER_WORD;
83 : 0 : m_it -= jump;
84 : 0 : m_bitpos -= dist - jump * BITS_PER_WORD;
85 : 0 : }
86 : 0 : return *this;
87 : : }
88 : :
89 : 0 : friend difference_type operator-(const Iterator& x, const Iterator& y)
90 : : {
91 : 0 : return BITS_PER_WORD * (x.m_it - y.m_it) + x.m_bitpos - y.m_bitpos;
92 : : }
93 : :
94 : : Iterator& operator=(const Iterator&) = default;
95 : 0 : Iterator& operator-=(difference_type dist) { return operator+=(-dist); }
96 [ # # ]: 0 : Iterator& operator++() { ++m_bitpos; if (m_bitpos == BITS_PER_WORD) { m_bitpos = 0; ++m_it; }; return *this; }
97 : 0 : Iterator operator++(int) { auto ret{*this}; operator++(); return ret; }
98 [ # # ]: 0 : Iterator& operator--() { if (m_bitpos == 0) { m_bitpos = BITS_PER_WORD; --m_it; }; --m_bitpos; return *this; }
99 : : Iterator operator--(int) { auto ret{*this}; operator--(); return ret; }
100 : 0 : friend Iterator operator+(Iterator x, difference_type dist) { x += dist; return x; }
101 : : friend Iterator operator+(difference_type dist, Iterator x) { x += dist; return x; }
102 : 0 : friend Iterator operator-(Iterator x, difference_type dist) { x -= dist; return x; }
103 : : friend bool operator<(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) < std::tie(y.m_it, y.m_bitpos); }
104 : : friend bool operator>(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) > std::tie(y.m_it, y.m_bitpos); }
105 : : friend bool operator<=(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) <= std::tie(y.m_it, y.m_bitpos); }
106 : : friend bool operator>=(const Iterator& x, const Iterator& y) { return std::tie(x.m_it, x.m_bitpos) >= std::tie(y.m_it, y.m_bitpos); }
107 [ # # # # ]: 0 : friend bool operator==(const Iterator& x, const Iterator& y) { return x.m_it == y.m_it && x.m_bitpos == y.m_bitpos; }
108 [ # # ]: 0 : friend bool operator!=(const Iterator& x, const Iterator& y) { return x.m_it != y.m_it || x.m_bitpos != y.m_bitpos; }
109 : 0 : reference operator*() const { return (*m_it)[m_bitpos]; }
110 : 0 : reference operator[](difference_type pos) const { return *(*this + pos); }
111 : : };
112 : :
113 : : public:
114 : : using value_type = bool;
115 : : using size_type = std::size_t;
116 : : using difference_type = typename deque_type::difference_type;
117 : : using reference = typename word_type::reference;
118 : : using const_reference = bool;
119 : : using iterator = Iterator<false>;
120 : : using const_iterator = Iterator<true>;
121 : : using pointer = void;
122 : : using const_pointer = void;
123 : : using reverse_iterator = std::reverse_iterator<iterator>;
124 : : using const_reverse_iterator = std::reverse_iterator<const_iterator>;
125 : :
126 : : private:
127 : : /** Deque of bitsets storing the actual bit data. */
128 : : deque_type m_deque;
129 : :
130 : : /** Number of unused bits at the front of m_deque.front(). */
131 : : int m_pad_begin;
132 : :
133 : : /** Number of unused bits at the back of m_deque.back(). */
134 : : int m_pad_end;
135 : :
136 : : /** Shrink the container by n bits, removing from the end. */
137 : 0 : void erase_back(size_type n)
138 : : {
139 [ # # ]: 0 : if (n >= static_cast<size_type>(BITS_PER_WORD - m_pad_end)) {
140 : 0 : n -= BITS_PER_WORD - m_pad_end;
141 : 0 : m_pad_end = 0;
142 : 0 : m_deque.erase(m_deque.end() - 1 - (n / BITS_PER_WORD), m_deque.end());
143 : 0 : n %= BITS_PER_WORD;
144 : 0 : }
145 [ # # ]: 0 : if (n) {
146 : 0 : auto& last = m_deque.back();
147 [ # # ]: 0 : while (n) {
148 : 0 : last.reset(BITS_PER_WORD - 1 - m_pad_end);
149 : 0 : ++m_pad_end;
150 : 0 : --n;
151 : : }
152 : 0 : }
153 : 0 : }
154 : :
155 : : /** Extend the container by n bits, adding at the end. */
156 : 0 : void extend_back(size_type n)
157 : : {
158 [ # # ]: 0 : if (n > static_cast<size_type>(m_pad_end)) {
159 : 0 : n -= m_pad_end + 1;
160 : 0 : m_pad_end = BITS_PER_WORD - 1;
161 : 0 : m_deque.insert(m_deque.end(), 1 + (n / BITS_PER_WORD), {});
162 : 0 : n %= BITS_PER_WORD;
163 : 0 : }
164 : 0 : m_pad_end -= n;
165 : 0 : }
166 : :
167 : : /** Shrink the container by n bits, removing from the beginning. */
168 : 0 : void erase_front(size_type n)
169 : : {
170 [ # # ]: 0 : if (n >= static_cast<size_type>(BITS_PER_WORD - m_pad_begin)) {
171 : 0 : n -= BITS_PER_WORD - m_pad_begin;
172 : 0 : m_pad_begin = 0;
173 : 0 : m_deque.erase(m_deque.begin(), m_deque.begin() + 1 + (n / BITS_PER_WORD));
174 : 0 : n %= BITS_PER_WORD;
175 : 0 : }
176 [ # # ]: 0 : if (n) {
177 : 0 : auto& first = m_deque.front();
178 [ # # ]: 0 : while (n) {
179 : 0 : first.reset(m_pad_begin);
180 : 0 : ++m_pad_begin;
181 : 0 : --n;
182 : : }
183 : 0 : }
184 : 0 : }
185 : :
186 : : /** Extend the container by n bits, adding at the beginning. */
187 : 0 : void extend_front(size_type n)
188 : : {
189 [ # # ]: 0 : if (n > static_cast<size_type>(m_pad_begin)) {
190 : 0 : n -= m_pad_begin + 1;
191 : 0 : m_pad_begin = BITS_PER_WORD - 1;
192 : 0 : m_deque.insert(m_deque.begin(), 1 + (n / BITS_PER_WORD), {});
193 : 0 : n %= BITS_PER_WORD;
194 : 0 : }
195 : 0 : m_pad_begin -= n;
196 : 0 : }
197 : :
198 : : /** Insert a sequence of falses anywhere in the container. */
199 : 0 : void insert_zeroes(size_type before, size_type count)
200 : : {
201 : 0 : size_type after = size() - before;
202 [ # # ]: 0 : if (before < after) {
203 : 0 : extend_front(count);
204 : 0 : std::move(begin() + count, begin() + count + before, begin());
205 : 0 : } else {
206 : 0 : extend_back(count);
207 : 0 : std::move_backward(begin() + before, begin() + before + after, end());
208 : : }
209 : 0 : }
210 : :
211 : : public:
212 : : /** Construct an empty container. */
213 : 0 : explicit bitdeque() : m_pad_begin{0}, m_pad_end{0} {}
214 : :
215 : : /** Set the container equal to count times the value of val. */
216 : 0 : void assign(size_type count, bool val)
217 : : {
218 : 0 : m_deque.clear();
219 : 0 : m_deque.resize((count + BITS_PER_WORD - 1) / BITS_PER_WORD);
220 : 0 : m_pad_begin = 0;
221 : 0 : m_pad_end = 0;
222 [ # # ]: 0 : if (val) {
223 [ # # ]: 0 : for (auto& elem : m_deque) elem.flip();
224 : 0 : }
225 [ # # ]: 0 : if (count % BITS_PER_WORD) {
226 : 0 : erase_back(BITS_PER_WORD - (count % BITS_PER_WORD));
227 : 0 : }
228 : 0 : }
229 : :
230 : : /** Construct a container containing count times the value of val. */
231 [ # # ]: 0 : bitdeque(size_type count, bool val) { assign(count, val); }
232 : :
233 : : /** Construct a container containing count false values. */
234 [ # # ]: 0 : explicit bitdeque(size_t count) { assign(count, false); }
235 : :
236 : : /** Copy constructor. */
237 : : bitdeque(const bitdeque&) = default;
238 : :
239 : : /** Move constructor. */
240 : : bitdeque(bitdeque&&) noexcept = default;
241 : :
242 : : /** Copy assignment operator. */
243 : 0 : bitdeque& operator=(const bitdeque& other) = default;
244 : :
245 : : /** Move assignment operator. */
246 : 0 : bitdeque& operator=(bitdeque&& other) noexcept = default;
247 : :
248 : : // Iterator functions.
249 [ # # ]: 0 : iterator begin() noexcept { return {m_deque.begin(), m_pad_begin}; }
250 [ # # # # ]: 0 : iterator end() noexcept { return iterator{m_deque.end(), 0} - m_pad_end; }
251 [ # # ]: 0 : const_iterator begin() const noexcept { return const_iterator{m_deque.cbegin(), m_pad_begin}; }
252 [ # # ]: 0 : const_iterator cbegin() const noexcept { return const_iterator{m_deque.cbegin(), m_pad_begin}; }
253 [ # # # # ]: 0 : const_iterator end() const noexcept { return const_iterator{m_deque.cend(), 0} - m_pad_end; }
254 [ # # # # ]: 0 : const_iterator cend() const noexcept { return const_iterator{m_deque.cend(), 0} - m_pad_end; }
255 [ # # ]: 0 : reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
256 [ # # ]: 0 : reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
257 [ # # ]: 0 : const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{cend()}; }
258 [ # # ]: 0 : const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
259 [ # # ]: 0 : const_reverse_iterator rend() const noexcept { return const_reverse_iterator{cbegin()}; }
260 [ # # ]: 0 : const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
261 : :
262 : : /** Count the number of bits in the container. */
263 : 0 : size_type size() const noexcept { return m_deque.size() * BITS_PER_WORD - m_pad_begin - m_pad_end; }
264 : :
265 : : /** Determine whether the container is empty. */
266 : 0 : bool empty() const noexcept
267 : : {
268 [ # # # # ]: 0 : return m_deque.size() == 0 || (m_deque.size() == 1 && (m_pad_begin + m_pad_end == BITS_PER_WORD));
269 : : }
270 : :
271 : : /** Return the maximum size of the container. */
272 : 0 : size_type max_size() const noexcept
273 : : {
274 [ # # ]: 0 : if (m_deque.max_size() < std::numeric_limits<difference_type>::max() / BITS_PER_WORD) {
275 : 0 : return m_deque.max_size() * BITS_PER_WORD;
276 : : } else {
277 : 0 : return std::numeric_limits<difference_type>::max();
278 : : }
279 : 0 : }
280 : :
281 : : /** Set the container equal to the bits in [first,last). */
282 : : template<typename It>
283 : 0 : void assign(It first, It last)
284 : : {
285 : 0 : size_type count = std::distance(first, last);
286 : 0 : assign(count, false);
287 : 0 : auto it = begin();
288 [ # # ]: 0 : while (first != last) {
289 : 0 : *(it++) = *(first++);
290 : : }
291 : 0 : }
292 : :
293 : : /** Set the container equal to the bits in ilist. */
294 : 0 : void assign(std::initializer_list<bool> ilist)
295 : : {
296 : 0 : assign(ilist.size(), false);
297 : 0 : auto it = begin();
298 : 0 : auto init = ilist.begin();
299 [ # # ]: 0 : while (init != ilist.end()) {
300 : 0 : *(it++) = *(init++);
301 : : }
302 : 0 : }
303 : :
304 : : /** Set the container equal to the bits in ilist. */
305 : 0 : bitdeque& operator=(std::initializer_list<bool> ilist)
306 : : {
307 : 0 : assign(ilist);
308 : 0 : return *this;
309 : : }
310 : :
311 : : /** Construct a container containing the bits in [first,last). */
312 : : template<typename It>
313 [ # # ]: 0 : bitdeque(It first, It last) { assign(first, last); }
314 : :
315 : : /** Construct a container containing the bits in ilist. */
316 [ # # ]: 0 : bitdeque(std::initializer_list<bool> ilist) { assign(ilist); }
317 : :
318 : : // Access an element of the container, with bounds checking.
319 : 0 : reference at(size_type position)
320 : : {
321 [ # # # # ]: 0 : if (position >= size()) throw std::out_of_range("bitdeque::at() out of range");
322 : 0 : return begin()[position];
323 : 0 : }
324 : 0 : const_reference at(size_type position) const
325 : : {
326 [ # # # # ]: 0 : if (position >= size()) throw std::out_of_range("bitdeque::at() out of range");
327 : 0 : return cbegin()[position];
328 : 0 : }
329 : :
330 : : // Access elements of the container without bounds checking.
331 : 0 : reference operator[](size_type position) { return begin()[position]; }
332 : : const_reference operator[](size_type position) const { return cbegin()[position]; }
333 : 0 : reference front() { return *begin(); }
334 : 0 : const_reference front() const { return *cbegin(); }
335 : 0 : reference back() { return end()[-1]; }
336 : 0 : const_reference back() const { return cend()[-1]; }
337 : :
338 : : /** Release unused memory. */
339 : : void shrink_to_fit()
340 : : {
341 : : m_deque.shrink_to_fit();
342 : : }
343 : :
344 : : /** Empty the container. */
345 : 0 : void clear() noexcept
346 : : {
347 : 0 : m_deque.clear();
348 : 0 : m_pad_begin = m_pad_end = 0;
349 : 0 : }
350 : :
351 : : // Append an element to the container.
352 : 0 : void push_back(bool val)
353 : : {
354 : 0 : extend_back(1);
355 : 0 : back() = val;
356 : 0 : }
357 : : reference emplace_back(bool val)
358 : : {
359 : : extend_back(1);
360 : : auto ref = back();
361 : : ref = val;
362 : : return ref;
363 : : }
364 : :
365 : : // Prepend an element to the container.
366 : 0 : void push_front(bool val)
367 : : {
368 : 0 : extend_front(1);
369 : 0 : front() = val;
370 : 0 : }
371 : : reference emplace_front(bool val)
372 : : {
373 : : extend_front(1);
374 : : auto ref = front();
375 : : ref = val;
376 : : return ref;
377 : : }
378 : :
379 : : // Remove the last element from the container.
380 : 0 : void pop_back()
381 : : {
382 : 0 : erase_back(1);
383 : 0 : }
384 : :
385 : : // Remove the first element from the container.
386 : 0 : void pop_front()
387 : : {
388 : 0 : erase_front(1);
389 : 0 : }
390 : :
391 : : /** Resize the container. */
392 : 0 : void resize(size_type n)
393 : : {
394 [ # # ]: 0 : if (n < size()) {
395 : 0 : erase_back(size() - n);
396 : 0 : } else {
397 : 0 : extend_back(n - size());
398 : : }
399 : 0 : }
400 : :
401 : : // Swap two containers.
402 : 0 : void swap(bitdeque& other) noexcept
403 : : {
404 : 0 : std::swap(m_deque, other.m_deque);
405 : 0 : std::swap(m_pad_begin, other.m_pad_begin);
406 : 0 : std::swap(m_pad_end, other.m_pad_end);
407 : 0 : }
408 : 0 : friend void swap(bitdeque& b1, bitdeque& b2) noexcept { b1.swap(b2); }
409 : :
410 : : // Erase elements from the container.
411 : 0 : iterator erase(const_iterator first, const_iterator last)
412 : : {
413 : 0 : size_type before = std::distance(cbegin(), first);
414 : 0 : size_type dist = std::distance(first, last);
415 : 0 : size_type after = std::distance(last, cend());
416 [ # # ]: 0 : if (before < after) {
417 : 0 : std::move_backward(begin(), begin() + before, end() - after);
418 : 0 : erase_front(dist);
419 : 0 : return begin() + before;
420 : : } else {
421 : 0 : std::move(end() - after, end(), begin() + before);
422 : 0 : erase_back(dist);
423 : 0 : return end() - after;
424 : : }
425 : 0 : }
426 : :
427 : : iterator erase(iterator first, iterator last) { return erase(const_iterator{first}, const_iterator{last}); }
428 : 0 : iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
429 : : iterator erase(iterator pos) { return erase(const_iterator{pos}, const_iterator{pos} + 1); }
430 : :
431 : : // Insert elements into the container.
432 : 0 : iterator insert(const_iterator pos, bool val)
433 : : {
434 : 0 : size_type before = pos - cbegin();
435 : 0 : insert_zeroes(before, 1);
436 : 0 : auto it = begin() + before;
437 : 0 : *it = val;
438 : 0 : return it;
439 : : }
440 : :
441 : 0 : iterator emplace(const_iterator pos, bool val) { return insert(pos, val); }
442 : :
443 : 0 : iterator insert(const_iterator pos, size_type count, bool val)
444 : : {
445 : 0 : size_type before = pos - cbegin();
446 : 0 : insert_zeroes(before, count);
447 : 0 : auto it_begin = begin() + before;
448 : 0 : auto it = it_begin;
449 : 0 : auto it_end = it + count;
450 [ # # ]: 0 : while (it != it_end) *(it++) = val;
451 : 0 : return it_begin;
452 : : }
453 : :
454 : : template<typename It>
455 : 0 : iterator insert(const_iterator pos, It first, It last)
456 : : {
457 : 0 : size_type before = pos - cbegin();
458 : 0 : size_type count = std::distance(first, last);
459 : 0 : insert_zeroes(before, count);
460 : 0 : auto it_begin = begin() + before;
461 : 0 : auto it = it_begin;
462 [ # # ]: 0 : while (first != last) {
463 : 0 : *(it++) = *(first++);
464 : : }
465 : 0 : return it_begin;
466 : : }
467 : : };
468 : :
469 : : #endif // BITCOIN_UTIL_BITDEQUE_H
|