sparrow 0.6.0
Loading...
Searching...
No Matches
dynamic_bitset_base.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17
18#include <bit>
19#include <stdexcept>
20#include <string>
21#include <type_traits>
22
26
27namespace sparrow
28{
40 template <typename B>
41 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
43 {
44 public:
47 using storage_type = B;
48 using storage_type_without_cvrefpointer = std::remove_pointer_t<std::remove_cvref_t<storage_type>>;
49 using block_type = typename storage_type_without_cvrefpointer::value_type;
50 using value_type = bool;
52 using const_reference = bool;
53 using size_type = typename storage_type_without_cvrefpointer::size_type;
54 using difference_type = typename storage_type_without_cvrefpointer::difference_type;
57
58 [[nodiscard]] constexpr size_type size() const noexcept;
59 [[nodiscard]] constexpr bool empty() const noexcept;
60 [[nodiscard]] constexpr size_type null_count() const noexcept;
61
62 [[nodiscard]] constexpr bool test(size_type pos) const;
63 constexpr void set(size_type pos, value_type value);
64
65 [[nodiscard]] constexpr const_reference at(size_type pos) const;
66 [[nodiscard]] constexpr reference at(size_type pos);
67
68 [[nodiscard]] constexpr reference operator[](size_type i);
69 [[nodiscard]] constexpr const_reference operator[](size_type i) const;
70
71 [[nodiscard]] constexpr block_type* data() noexcept;
72 [[nodiscard]] constexpr const block_type* data() const noexcept;
73 [[nodiscard]] constexpr size_type block_count() const noexcept;
75 constexpr void swap(self_type&) noexcept;
77 [[nodiscard]] constexpr iterator begin();
78 [[nodiscard]] constexpr iterator end();
79
80 [[nodiscard]] constexpr const_iterator begin() const;
81 [[nodiscard]] constexpr const_iterator end() const;
82 [[nodiscard]] constexpr const_iterator cbegin() const;
83 [[nodiscard]] constexpr const_iterator cend() const;
84
85 [[nodiscard]] constexpr reference front();
86 [[nodiscard]] constexpr const_reference front() const;
87 [[nodiscard]] constexpr reference back();
88 [[nodiscard]] constexpr const_reference back() const;
89
90 [[nodiscard]] constexpr const storage_type_without_cvrefpointer& buffer() const noexcept
91 {
92 if constexpr (std::is_pointer_v<storage_type>)
93 {
94 return *m_buffer;
95 }
96 else
97 {
98 return m_buffer;
99 }
100 }
101
102 [[nodiscard]] constexpr storage_type_without_cvrefpointer& buffer() noexcept
103 {
104 if constexpr (std::is_pointer_v<storage_type>)
105 {
106 return *m_buffer;
107 }
108 else
109 {
110 return m_buffer;
111 }
112 }
113
114 [[nodiscard]] static constexpr size_type compute_block_count(size_type bits_count) noexcept;
115
116 // storage_type is a value_type
117 [[nodiscard]] storage_type extract_storage() noexcept
119 {
120 return std::move(m_buffer);
121 }
122
123 protected:
124
127 constexpr ~dynamic_bitset_base() = default;
128
129 constexpr dynamic_bitset_base(const dynamic_bitset_base&) = default;
130 constexpr dynamic_bitset_base(dynamic_bitset_base&&) noexcept = default;
131
132 constexpr dynamic_bitset_base& operator=(const dynamic_bitset_base&) = default;
133 constexpr dynamic_bitset_base& operator=(dynamic_bitset_base&&) noexcept = default;
134
135 constexpr void resize(size_type n, value_type b = false);
136 constexpr void clear() noexcept;
137
139 constexpr iterator insert(const_iterator pos, size_type count, value_type value);
140 template <std::input_iterator InputIt>
141 constexpr iterator insert(const_iterator pos, InputIt first, InputIt last);
142 constexpr iterator insert(const_iterator pos, std::initializer_list<value_type> ilist);
146
147 constexpr void push_back(value_type value);
148 constexpr void pop_back();
149
150 constexpr void zero_unused_bits();
151 [[nodiscard]] size_type count_non_null() const noexcept;
152
153 private:
154
155 static constexpr std::size_t s_bits_per_block = sizeof(block_type) * CHAR_BIT;
156 [[nodiscard]] static constexpr size_type block_index(size_type pos) noexcept;
157 [[nodiscard]] static constexpr size_type bit_index(size_type pos) noexcept;
158 [[nodiscard]] static constexpr block_type bit_mask(size_type pos) noexcept;
159
160 [[nodiscard]] constexpr size_type count_extra_bits() const noexcept;
161 constexpr void update_null_count(bool old_value, bool new_value);
162
163 storage_type m_buffer;
164 size_type m_size;
165 size_type m_null_count;
166
167 friend class bitset_iterator<self_type, true>;
168 friend class bitset_iterator<self_type, false>;
169 friend class bitset_reference<self_type>;
170 };
171
172 template <typename B>
173 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
174 constexpr auto dynamic_bitset_base<B>::size() const noexcept -> size_type
175 {
176 return m_size;
177 }
178
179 template <typename B>
180 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
181 constexpr bool dynamic_bitset_base<B>::empty() const noexcept
182 {
183 return m_size == 0;
184 }
185
186 template <typename B>
187 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
188 constexpr auto dynamic_bitset_base<B>::null_count() const noexcept -> size_type
189 {
190 return m_null_count;
191 }
192
193 template <typename B>
194 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
196 {
197 SPARROW_ASSERT_TRUE(pos < size());
198 SPARROW_ASSERT_TRUE(data() != nullptr);
199 return reference(*this, buffer().data()[block_index(pos)], bit_mask(pos));
200 }
201
202 template <typename B>
203 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
205 {
206 return test(pos);
207 }
208
209 template <typename B>
210 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
211 constexpr bool dynamic_bitset_base<B>::test(size_type pos) const
212 {
213 SPARROW_ASSERT_TRUE(pos < size());
214 if (data() == nullptr)
215 {
216 return true;
217 }
218 return !m_null_count || buffer().data()[block_index(pos)] & bit_mask(pos);
219 }
220
221 template <typename B>
222 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
224 {
225 SPARROW_ASSERT_TRUE(pos < size());
226 SPARROW_ASSERT_TRUE(data() != nullptr);
227 block_type& block = buffer().data()[block_index(pos)];
228 const bool old_value = block & bit_mask(pos);
229 if (value)
230 {
231 block |= bit_mask(pos);
232 }
233 else
234 {
235 block &= block_type(~bit_mask(pos));
236 }
237 update_null_count(old_value, value);
238 }
239
240 template <typename B>
241 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
242 constexpr auto dynamic_bitset_base<B>::data() noexcept -> block_type*
243 {
244 return buffer().data();
245 }
246
247 template <typename B>
248 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
249 constexpr auto dynamic_bitset_base<B>::data() const noexcept -> const block_type*
250 {
251 return buffer().data();
252 }
253
254 template <typename B>
255 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
256 constexpr auto dynamic_bitset_base<B>::block_count() const noexcept -> size_type
257 {
258 return buffer().size();
259 }
260
261 template <typename B>
262 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
263 constexpr void dynamic_bitset_base<B>::swap(self_type& rhs) noexcept
264 {
265 using std::swap;
266 swap(m_buffer, rhs.m_buffer);
267 swap(m_size, rhs.m_size);
268 swap(m_null_count, rhs.m_null_count);
269 }
270
271 template <typename B>
272 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
274 {
275 return iterator(this, data(), 0u);
276 }
277
278 template <typename B>
279 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
281 {
282 block_type* block = buffer().size() ? data() + buffer().size() - 1 : data();
283 return iterator(this, block, size() % s_bits_per_block);
284 }
285
286 template <typename B>
287 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
289 {
290 return cbegin();
291 }
292
293 template <typename B>
294 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
296 {
297 return cend();
298 }
299
300 template <typename B>
301 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
303 {
304 return const_iterator(this, data(), 0u);
305 }
306
307 template <typename B>
308 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
310 {
311 const block_type* block = buffer().size() ? data() + buffer().size() - 1 : data();
312 return const_iterator(this, block, size() % s_bits_per_block);
313 }
314
315 template <typename B>
316 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
318 {
319 if (pos >= size())
320 {
321 throw std::out_of_range(
322 "dynamic_bitset_base::at: index out of range for dynamic_bitset_base of size"
323 + std::to_string(size()) + " at index " + std::to_string(pos)
324 );
325 }
326 return (*this)[pos];
327 }
328
329 template <typename B>
330 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
332 {
333 if (pos >= size())
334 {
335 throw std::out_of_range(
336 "dynamic_bitset_base::at: index out of range for dynamic_bitset_base of size"
337 + std::to_string(size()) + " at index " + std::to_string(pos)
338 );
339 }
340 return test(pos);
341 }
342
343 template <typename B>
344 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
346 {
348 return (*this)[0];
349 }
350
351 template <typename B>
352 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
354 {
356 if (data() == nullptr)
357 {
358 return true;
359 }
360 return (*this)[0];
361 }
362
363 template <typename B>
364 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
366 {
368 return (*this)[size() - 1];
369 }
370
371 template <typename B>
372 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
374 {
376 if (data() == nullptr)
377 {
378 return true;
379 }
380 return (*this)[size() - 1];
381 }
382
383 template <typename B>
384 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
386 : m_buffer(std::move(buf))
387 , m_size(size)
388 , m_null_count(m_size - count_non_null())
389 {
390 }
391
392 template <typename B>
393 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
395 : m_buffer(std::move(buf))
396 , m_size(size)
397 , m_null_count(null_count)
398 {
399 }
400
401 template <typename B>
402 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
404 {
405 return bits_count / s_bits_per_block + static_cast<size_type>(bits_count % s_bits_per_block != 0);
406 }
407
408 template <typename B>
409 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
410 constexpr auto dynamic_bitset_base<B>::block_index(size_type pos) noexcept -> size_type
411 {
412 return pos / s_bits_per_block;
413 }
414
415 template <typename B>
416 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
417 constexpr auto dynamic_bitset_base<B>::bit_index(size_type pos) noexcept -> size_type
418 {
419 return pos % s_bits_per_block;
420 }
421
422 template <typename B>
423 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
424 constexpr auto dynamic_bitset_base<B>::bit_mask(size_type pos) noexcept -> block_type
425 {
426 const size_type bit = bit_index(pos);
427 return static_cast<block_type>(block_type(1) << bit);
428 }
429
430 template <typename B>
431 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
433 {
434 if (data() == nullptr)
435 {
436 return m_size;
437 }
438 if (buffer().empty())
439 {
440 return 0u;
441 }
442
443 int res = 0;
444 size_t full_blocks = m_size / s_bits_per_block;
445 for (size_t i = 0; i < full_blocks; ++i)
446 {
447 res += std::popcount(buffer().data()[i]);
448 }
449 if (full_blocks != buffer().size())
450 {
451 const size_t bits_count = m_size % s_bits_per_block;
452 const block_type mask = ~block_type(~block_type(0) << bits_count);
453 const block_type block = buffer().data()[full_blocks] & mask;
454 res += std::popcount(block);
455 }
456
457 return static_cast<size_t>(res);
458 }
459
460 template <typename B>
461 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
462 constexpr auto dynamic_bitset_base<B>::count_extra_bits() const noexcept -> size_type
463 {
464 return bit_index(size());
465 }
466
467 template <typename B>
468 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
470 {
471 if (data() == nullptr)
472 {
473 return;
474 }
475 const size_type extra_bits = count_extra_bits();
476 if (extra_bits != 0)
477 {
478 buffer().back() &= block_type(~(~block_type(0) << extra_bits));
479 }
480 }
481
482 template <typename B>
483 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
484 constexpr void dynamic_bitset_base<B>::update_null_count(bool old_value, bool new_value)
485 {
486 if (new_value && !old_value)
487 {
488 --m_null_count;
489 }
490 else if (!new_value && old_value)
491 {
492 ++m_null_count;
493 }
494 }
495
496 template <typename B>
497 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
499 {
500 const size_type old_block_count = buffer().size();
501 const size_type new_block_count = compute_block_count(n);
502 const block_type value = b ? block_type(~block_type(0)) : block_type(0);
503
504 if (new_block_count != old_block_count)
505 {
506 buffer().resize(new_block_count, value);
507 }
508
509 if (b && n > m_size)
510 {
511 const size_type extra_bits = count_extra_bits();
512 if (extra_bits > 0)
513 {
514 buffer().data()[old_block_count - 1] |= static_cast<block_type>(value << extra_bits);
515 }
516 }
517
518 m_size = n;
519 m_null_count = m_size - count_non_null();
521 }
522
523 template <typename B>
524 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
525 constexpr void dynamic_bitset_base<B>::clear() noexcept
526 {
527 buffer().clear();
528 m_size = 0;
529 m_null_count = 0;
530 }
531
532 template <typename B>
533 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
536 {
537 return insert(pos, 1, value);
538 }
539
540 template <typename B>
541 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
544 {
545 SPARROW_ASSERT_TRUE(data() != nullptr);
546 SPARROW_ASSERT_TRUE(cbegin() <= pos);
547 SPARROW_ASSERT_TRUE(pos <= cend());
548 const auto index = static_cast<size_type>(std::distance(cbegin(), pos));
549 const size_type old_size = size();
550 const size_type new_size = old_size + count;
551
552 // TODO: The current implementation is not efficient. It can be improved.
553
554 resize(new_size);
555
556 for (size_type i = old_size + count - 1; i >= index + count; --i)
557 {
558 set(i, test(i - count));
559 }
560
561 for (size_type i = 0; i < count; ++i)
562 {
563 set(index + i, value);
564 }
565
566 auto block = data() + block_index(index);
567 return iterator(this, block, bit_index(index));
568 }
569
570 template <typename B>
571 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
572 template <std::input_iterator InputIt>
574 dynamic_bitset_base<B>::insert(const_iterator pos, InputIt first, InputIt last)
575 {
576 SPARROW_ASSERT_TRUE(data() != nullptr);
577 SPARROW_ASSERT_TRUE(cbegin() <= pos);
578 SPARROW_ASSERT_TRUE(pos <= cend());
579 const auto index = static_cast<size_type>(std::distance(cbegin(), pos));
580 const size_type old_size = size();
581 const size_type count = static_cast<size_type>(std::distance(first, last));
582 const size_type new_size = old_size + count;
583
584 resize(new_size);
585
586 // TODO: The current implementation is not efficient. It can be improved.
587
588 for (size_type i = old_size + count - 1; i >= index + count; --i)
589 {
590 set(i, test(i - count));
591 }
592
593 for (size_type i = 0; i < count; ++i)
594 {
595 set(index + i, *first++);
596 }
597
598 auto block = data() + block_index(index);
599 return iterator(this, block, bit_index(index));
600 }
601
602 template <typename B>
603 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
606 {
607 return insert(pos, value);
608 }
609
610 template <typename B>
611 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
613 {
614 SPARROW_ASSERT_TRUE(cbegin() <= pos);
615 SPARROW_ASSERT_TRUE(pos < cend());
616
617 return erase(pos, pos + 1);
618 }
619
620 template <typename B>
621 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
624 {
625 SPARROW_ASSERT_TRUE(data() != nullptr);
626 SPARROW_ASSERT_TRUE(cbegin() <= first);
627 SPARROW_ASSERT_TRUE(first <= last);
628 SPARROW_ASSERT_TRUE(last <= cend());
629
630 const auto first_index = static_cast<size_type>(std::distance(cbegin(), first));
631
632 if (last == cend())
633 {
634 resize(first_index);
635 return end();
636 }
637
638 // TODO: The current implementation is not efficient. It can be improved.
639
640 const auto last_index = static_cast<size_type>(std::distance(cbegin(), last));
641 const size_type count = last_index - first_index;
642
643 const size_type bit_to_move = size() - last_index;
644 for (size_type i = 0; i < bit_to_move; ++i)
645 {
646 set(first_index + i, test(last_index + i));
647 }
648
649 resize(size() - count);
650
651 auto block = data() + block_index(first_index);
652 return iterator(this, block, bit_index(first_index));
653 }
654
655 template <typename B>
656 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
658 {
659 resize(size() + 1, value);
660 }
661
662 template <typename B>
663 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
665 {
666 resize(size() - 1);
667 }
668}
Iterator used to iterate over the bits of a dynamic bitset as if they were addressable values.
Reference proxy used by the bitset_iterator class to make it possible to assign a bit of a bitset as ...
typename storage_type_without_cvrefpointer::value_type block_type
constexpr ~dynamic_bitset_base()=default
bitset_iterator< self_type, false > iterator
constexpr iterator insert(const_iterator pos, value_type value)
constexpr const_iterator cbegin() const
size_type count_non_null() const noexcept
constexpr void push_back(value_type value)
constexpr iterator erase(const_iterator pos)
typename storage_type_without_cvrefpointer::size_type size_type
constexpr bool empty() const noexcept
constexpr iterator emplace(const_iterator pos, value_type value)
dynamic_bitset_base< buffer< T > * > self_type
storage_type extract_storage() noexcept
constexpr const_reference at(size_type pos) const
constexpr bool test(size_type pos) const
constexpr void resize(size_type n, value_type b=false)
constexpr size_type null_count() const noexcept
bitset_iterator< self_type, true > const_iterator
std::remove_pointer_t< std::remove_cvref_t< storage_type > > storage_type_without_cvrefpointer
static constexpr size_type compute_block_count(size_type bits_count) noexcept
constexpr size_type size() const noexcept
constexpr dynamic_bitset_base(dynamic_bitset_base &&) noexcept=default
constexpr void set(size_type pos, value_type value)
typename storage_type_without_cvrefpointer::difference_type difference_type
constexpr size_type block_count() const noexcept
constexpr const storage_type_without_cvrefpointer & buffer() const noexcept
constexpr reference operator[](size_type i)
constexpr dynamic_bitset_base(storage_type buffer, size_type size, size_type null_count)
constexpr dynamic_bitset_base(const dynamic_bitset_base &)=default
constexpr void swap(self_type &) noexcept
constexpr block_type * data() noexcept
constexpr dynamic_bitset_base(storage_type buffer, size_type size)
constexpr const_iterator cend() const
constexpr storage_type_without_cvrefpointer & buffer() noexcept
typename base_type::block_type block_type
#define SPARROW_ASSERT_TRUE(expr__)