/ src / util / bitdeque.h
bitdeque.h
  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          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          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          Iterator(const Iterator<false>& x) : m_it(x.m_it), m_bitpos(x.m_bitpos) {}
 63  
 64          Iterator& operator+=(difference_type dist)
 65          {
 66              if (dist > 0) {
 67                  if (dist + m_bitpos >= BITS_PER_WORD) {
 68                      ++m_it;
 69                      dist -= BITS_PER_WORD - m_bitpos;
 70                      m_bitpos = 0;
 71                  }
 72                  auto jump = dist / BITS_PER_WORD;
 73                  m_it += jump;
 74                  m_bitpos += dist - jump * BITS_PER_WORD;
 75              } else if (dist < 0) {
 76                  dist = -dist;
 77                  if (dist > m_bitpos) {
 78                      --m_it;
 79                      dist -= m_bitpos + 1;
 80                      m_bitpos = BITS_PER_WORD - 1;
 81                  }
 82                  auto jump = dist / BITS_PER_WORD;
 83                  m_it -= jump;
 84                  m_bitpos -= dist - jump * BITS_PER_WORD;
 85              }
 86              return *this;
 87          }
 88  
 89          friend difference_type operator-(const Iterator& x, const Iterator& y)
 90          {
 91              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          Iterator& operator-=(difference_type dist) { return operator+=(-dist); }
 96          Iterator& operator++() { ++m_bitpos; if (m_bitpos == BITS_PER_WORD) { m_bitpos = 0; ++m_it; }; return *this; }
 97          Iterator operator++(int) { auto ret{*this}; operator++(); return ret; }
 98          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          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          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          friend bool operator==(const Iterator& x, const Iterator& y) { return x.m_it == y.m_it && x.m_bitpos == y.m_bitpos; }
108          friend bool operator!=(const Iterator& x, const Iterator& y) { return x.m_it != y.m_it || x.m_bitpos != y.m_bitpos; }
109          reference operator*() const { return (*m_it)[m_bitpos]; }
110          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      void erase_back(size_type n)
138      {
139          if (n >= static_cast<size_type>(BITS_PER_WORD - m_pad_end)) {
140              n -= BITS_PER_WORD - m_pad_end;
141              m_pad_end = 0;
142              m_deque.erase(m_deque.end() - 1 - (n / BITS_PER_WORD), m_deque.end());
143              n %= BITS_PER_WORD;
144          }
145          if (n) {
146              auto& last = m_deque.back();
147              while (n) {
148                  last.reset(BITS_PER_WORD - 1 - m_pad_end);
149                  ++m_pad_end;
150                  --n;
151              }
152          }
153      }
154  
155      /** Extend the container by n bits, adding at the end. */
156      void extend_back(size_type n)
157      {
158          if (n > static_cast<size_type>(m_pad_end)) {
159              n -= m_pad_end + 1;
160              m_pad_end = BITS_PER_WORD - 1;
161              m_deque.insert(m_deque.end(), 1 + (n / BITS_PER_WORD), {});
162              n %= BITS_PER_WORD;
163          }
164          m_pad_end -= n;
165      }
166  
167      /** Shrink the container by n bits, removing from the beginning. */
168      void erase_front(size_type n)
169      {
170          if (n >= static_cast<size_type>(BITS_PER_WORD - m_pad_begin)) {
171              n -= BITS_PER_WORD - m_pad_begin;
172              m_pad_begin = 0;
173              m_deque.erase(m_deque.begin(), m_deque.begin() + 1 + (n / BITS_PER_WORD));
174              n %= BITS_PER_WORD;
175          }
176          if (n) {
177              auto& first = m_deque.front();
178              while (n) {
179                  first.reset(m_pad_begin);
180                  ++m_pad_begin;
181                  --n;
182              }
183          }
184      }
185  
186      /** Extend the container by n bits, adding at the beginning. */
187      void extend_front(size_type n)
188      {
189          if (n > static_cast<size_type>(m_pad_begin)) {
190              n -= m_pad_begin + 1;
191              m_pad_begin = BITS_PER_WORD - 1;
192              m_deque.insert(m_deque.begin(), 1 + (n / BITS_PER_WORD), {});
193              n %= BITS_PER_WORD;
194          }
195          m_pad_begin -= n;
196      }
197  
198      /** Insert a sequence of falses anywhere in the container. */
199      void insert_zeroes(size_type before, size_type count)
200      {
201          size_type after = size() - before;
202          if (before < after) {
203              extend_front(count);
204              std::move(begin() + count, begin() + count + before, begin());
205          } else {
206              extend_back(count);
207              std::move_backward(begin() + before, begin() + before + after, end());
208          }
209      }
210  
211  public:
212      /** Construct an empty container. */
213      explicit bitdeque() : m_pad_begin{0}, m_pad_end{0} {}
214  
215      /** Set the container equal to count times the value of val. */
216      void assign(size_type count, bool val)
217      {
218          m_deque.clear();
219          m_deque.resize((count + BITS_PER_WORD - 1) / BITS_PER_WORD);
220          m_pad_begin = 0;
221          m_pad_end = 0;
222          if (val) {
223              for (auto& elem : m_deque) elem.flip();
224          }
225          if (count % BITS_PER_WORD) {
226              erase_back(BITS_PER_WORD - (count % BITS_PER_WORD));
227          }
228      }
229  
230      /** Construct a container containing count times the value of val. */
231      bitdeque(size_type count, bool val) { assign(count, val); }
232  
233      /** Construct a container containing count false values. */
234      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      bitdeque& operator=(const bitdeque& other) = default;
244  
245      /** Move assignment operator. */
246      bitdeque& operator=(bitdeque&& other) noexcept = default;
247  
248      // Iterator functions.
249      iterator begin() noexcept { return {m_deque.begin(), m_pad_begin}; }
250      iterator end() noexcept { return iterator{m_deque.end(), 0} - m_pad_end; }
251      const_iterator begin() const noexcept { return const_iterator{m_deque.cbegin(), m_pad_begin}; }
252      const_iterator cbegin() const noexcept { return const_iterator{m_deque.cbegin(), m_pad_begin}; }
253      const_iterator end() const noexcept { return const_iterator{m_deque.cend(), 0} - m_pad_end; }
254      const_iterator cend() const noexcept { return const_iterator{m_deque.cend(), 0} - m_pad_end; }
255      reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
256      reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
257      const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{cend()}; }
258      const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; }
259      const_reverse_iterator rend() const noexcept { return const_reverse_iterator{cbegin()}; }
260      const_reverse_iterator crend() const noexcept { return const_reverse_iterator{cbegin()}; }
261  
262      /** Count the number of bits in the container. */
263      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      bool empty() const noexcept
267      {
268          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      size_type max_size() const noexcept
273      {
274          if (m_deque.max_size() < std::numeric_limits<difference_type>::max() / BITS_PER_WORD) {
275              return m_deque.max_size() * BITS_PER_WORD;
276          } else {
277              return std::numeric_limits<difference_type>::max();
278          }
279      }
280  
281      /** Set the container equal to the bits in [first,last). */
282      template<typename It>
283      void assign(It first, It last)
284      {
285          size_type count = std::distance(first, last);
286          assign(count, false);
287          auto it = begin();
288          while (first != last) {
289              *(it++) = *(first++);
290          }
291      }
292  
293      /** Set the container equal to the bits in ilist. */
294      void assign(std::initializer_list<bool> ilist)
295      {
296          assign(ilist.size(), false);
297          auto it = begin();
298          auto init = ilist.begin();
299          while (init != ilist.end()) {
300              *(it++) = *(init++);
301          }
302      }
303  
304      /** Set the container equal to the bits in ilist. */
305      bitdeque& operator=(std::initializer_list<bool> ilist)
306      {
307          assign(ilist);
308          return *this;
309      }
310  
311      /** Construct a container containing the bits in [first,last). */
312      template<typename It>
313      bitdeque(It first, It last) { assign(first, last); }
314  
315      /** Construct a container containing the bits in ilist. */
316      bitdeque(std::initializer_list<bool> ilist) { assign(ilist); }
317  
318      // Access an element of the container, with bounds checking.
319      reference at(size_type position)
320      {
321          if (position >= size()) throw std::out_of_range("bitdeque::at() out of range");
322          return begin()[position];
323      }
324      const_reference at(size_type position) const
325      {
326          if (position >= size()) throw std::out_of_range("bitdeque::at() out of range");
327          return cbegin()[position];
328      }
329  
330      // Access elements of the container without bounds checking.
331      reference operator[](size_type position) { return begin()[position]; }
332      const_reference operator[](size_type position) const { return cbegin()[position]; }
333      reference front() { return *begin(); }
334      const_reference front() const { return *cbegin(); }
335      reference back() { return end()[-1]; }
336      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      void clear() noexcept
346      {
347          m_deque.clear();
348          m_pad_begin = m_pad_end = 0;
349      }
350  
351      // Append an element to the container.
352      void push_back(bool val)
353      {
354          extend_back(1);
355          back() = val;
356      }
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      void push_front(bool val)
367      {
368          extend_front(1);
369          front() = val;
370      }
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      void pop_back()
381      {
382          erase_back(1);
383      }
384  
385      // Remove the first element from the container.
386      void pop_front()
387      {
388          erase_front(1);
389      }
390  
391      /** Resize the container. */
392      void resize(size_type n)
393      {
394          if (n < size()) {
395              erase_back(size() - n);
396          } else {
397              extend_back(n - size());
398          }
399      }
400  
401      // Swap two containers.
402      void swap(bitdeque& other) noexcept
403      {
404          std::swap(m_deque, other.m_deque);
405          std::swap(m_pad_begin, other.m_pad_begin);
406          std::swap(m_pad_end, other.m_pad_end);
407      }
408      friend void swap(bitdeque& b1, bitdeque& b2) noexcept { b1.swap(b2); }
409  
410      // Erase elements from the container.
411      iterator erase(const_iterator first, const_iterator last)
412      {
413          size_type before = std::distance(cbegin(), first);
414          size_type dist = std::distance(first, last);
415          size_type after = std::distance(last, cend());
416          if (before < after) {
417              std::move_backward(begin(), begin() + before, end() - after);
418              erase_front(dist);
419              return begin() + before;
420          } else {
421              std::move(end() - after, end(), begin() + before);
422              erase_back(dist);
423              return end() - after;
424          }
425      }
426  
427      iterator erase(iterator first, iterator last) { return erase(const_iterator{first}, const_iterator{last}); }
428      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      iterator insert(const_iterator pos, bool val)
433      {
434          size_type before = pos - cbegin();
435          insert_zeroes(before, 1);
436          auto it = begin() + before;
437          *it = val;
438          return it;
439      }
440  
441      iterator emplace(const_iterator pos, bool val) { return insert(pos, val); }
442  
443      iterator insert(const_iterator pos, size_type count, bool val)
444      {
445          size_type before = pos - cbegin();
446          insert_zeroes(before, count);
447          auto it_begin = begin() + before;
448          auto it = it_begin;
449          auto it_end = it + count;
450          while (it != it_end) *(it++) = val;
451          return it_begin;
452      }
453  
454      template<typename It>
455      iterator insert(const_iterator pos, It first, It last)
456      {
457          size_type before = pos - cbegin();
458          size_type count = std::distance(first, last);
459          insert_zeroes(before, count);
460          auto it_begin = begin() + before;
461          auto it = it_begin;
462          while (first != last) {
463              *(it++) = *(first++);
464          }
465          return it_begin;
466      }
467  };
468  
469  #endif // BITCOIN_UTIL_BITDEQUE_H