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