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