sparrow 2.2.1
C++20 idiomatic APIs for the Apache Arrow Columnar Format
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 <algorithm>
19#include <stdexcept>
20#include <string>
21#include <type_traits>
22
27
28namespace sparrow
29{
66 template <typename B, null_count_policy NCP = tracking_null_count<>>
67 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
68 class dynamic_bitset_base : protected NCP
69 {
70 public:
71
74 using storage_type = B;
75 using storage_type_without_cvrefpointer = std::remove_pointer_t<
76 std::remove_cvref_t<storage_type>>;
77 using block_type = typename storage_type_without_cvrefpointer::value_type;
79 using value_type = bool;
81 using const_reference = bool;
82 using size_type = typename storage_type_without_cvrefpointer::size_type;
84 using difference_type = typename storage_type_without_cvrefpointer::difference_type;
89
95 [[nodiscard]] constexpr size_type size() const noexcept;
96
97 constexpr void set_size(size_type new_size) noexcept;
98
104 [[nodiscard]] constexpr size_type offset() const noexcept;
105
115 constexpr void set_offset(size_type offset) noexcept;
116
122 [[nodiscard]] constexpr bool empty() const noexcept;
123
130 [[nodiscard]] constexpr size_type null_count() const noexcept
131 requires(NCP::track_null_count);
132
141 [[nodiscard]] constexpr bool test(size_type pos) const;
142
153 constexpr void set(size_type pos, value_type value);
154
163 [[nodiscard]] constexpr const_reference at(size_type pos) const;
164
173 [[nodiscard]] constexpr reference at(size_type pos);
174
183 [[nodiscard]] constexpr reference operator[](size_type i);
184
193 [[nodiscard]] constexpr const_reference operator[](size_type i) const;
194
200 [[nodiscard]] constexpr block_type* data() noexcept;
201
207 [[nodiscard]] constexpr const block_type* data() const noexcept;
208
214 [[nodiscard]] constexpr size_type block_count() const noexcept;
215
222 constexpr void swap(self_type& rhs) noexcept;
223
229 [[nodiscard]] constexpr iterator begin();
230
236 [[nodiscard]] constexpr iterator end();
237
243 [[nodiscard]] constexpr const_iterator begin() const;
244
250 [[nodiscard]] constexpr const_iterator end() const;
251
257 [[nodiscard]] constexpr const_iterator cbegin() const;
258
264 [[nodiscard]] constexpr const_iterator cend() const;
273 [[nodiscard]] constexpr reference front();
274
283 [[nodiscard]] constexpr const_reference front() const;
284
292 [[nodiscard]] constexpr reference back();
293
302 [[nodiscard]] constexpr const_reference back() const;
303
309 [[nodiscard]] constexpr const storage_type_without_cvrefpointer& buffer() const noexcept
310 {
311 if constexpr (std::is_pointer_v<storage_type>)
312 {
313 return *m_buffer;
314 }
315 else
316 {
317 return m_buffer;
318 }
319 }
320
326 [[nodiscard]] constexpr storage_type_without_cvrefpointer& buffer() noexcept
327 {
328 if constexpr (std::is_pointer_v<storage_type>)
329 {
330 return *m_buffer;
331 }
332 else
333 {
334 return m_buffer;
335 }
336 }
337
345 [[nodiscard]] static constexpr size_type compute_block_count(size_type bits_count) noexcept;
346
354 [[nodiscard]] storage_type extract_storage() noexcept
356 {
357 return std::move(m_buffer);
358 }
359
360 protected:
361
371
383
397 requires(NCP::track_null_count);
398
399 constexpr ~dynamic_bitset_base() = default;
400
401 constexpr dynamic_bitset_base(const dynamic_bitset_base&) = default;
402 constexpr dynamic_bitset_base(dynamic_bitset_base&&) noexcept = default;
403
404 constexpr dynamic_bitset_base& operator=(const dynamic_bitset_base&) = default;
405 constexpr dynamic_bitset_base& operator=(dynamic_bitset_base&&) noexcept = default;
406
416 constexpr void resize(size_type n, value_type b = false);
417
424 constexpr void clear() noexcept;
425
437
449 constexpr iterator insert(const_iterator pos, size_type count, value_type value);
450
463 template <std::input_iterator InputIt>
464 constexpr iterator insert(const_iterator pos, InputIt first, InputIt last);
465
474 constexpr iterator insert(const_iterator pos, std::initializer_list<value_type> ilist);
475
486
496
507
514 constexpr void push_back(value_type value);
515
522 constexpr void pop_back();
523
529 constexpr void zero_unused_bits();
530
531 private:
532
533 static constexpr std::size_t s_bits_per_block = sizeof(block_type) * CHAR_BIT;
535
541 [[nodiscard]] static constexpr size_type block_index(size_type pos) noexcept;
542
548 [[nodiscard]] static constexpr size_type bit_index(size_type pos) noexcept;
549
555 [[nodiscard]] static constexpr block_type bit_mask(size_type pos) noexcept;
556
561 [[nodiscard]] constexpr size_type count_extra_bits() const noexcept;
562
569 constexpr void shift_bits_right(size_type start, size_type length, size_type shift_amount);
570
577 constexpr void fill_bits(size_type start, size_type count, value_type value);
578
579 storage_type m_buffer;
580 size_type m_size;
581 size_type m_offset;
582
583 friend class bitset_iterator<self_type, true>;
584 friend class bitset_iterator<self_type, false>;
585 friend class bitset_reference<self_type>;
586 };
587
588 template <typename B, null_count_policy NCP>
589 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
590 constexpr auto dynamic_bitset_base<B, NCP>::size() const noexcept -> size_type
591 {
592 return m_size;
593 }
594
595 template <typename B, null_count_policy NCP>
596 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
597 constexpr void dynamic_bitset_base<B, NCP>::set_size(size_type new_size) noexcept
598 {
599 m_size = new_size;
600 }
601
602 template <typename B, null_count_policy NCP>
603 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
604 constexpr bool dynamic_bitset_base<B, NCP>::empty() const noexcept
605 {
606 return m_size == 0;
607 }
608
609 template <typename B, null_count_policy NCP>
610 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
611 constexpr auto dynamic_bitset_base<B, NCP>::offset() const noexcept -> size_type
612 {
613 return m_offset;
614 }
615
616 template <typename B, null_count_policy NCP>
617 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
619 {
620 m_offset = offset;
621 }
622
623 template <typename B, null_count_policy NCP>
624 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
625 constexpr auto dynamic_bitset_base<B, NCP>::null_count() const noexcept -> size_type
626 requires(NCP::track_null_count)
627 {
628 return NCP::null_count();
629 }
630
631 template <typename B, null_count_policy NCP>
632 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
634 {
635 SPARROW_ASSERT_TRUE(pos < size());
636 return reference(*this, pos);
637 }
638
639 template <typename B, null_count_policy NCP>
640 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
642 {
643 return test(pos);
644 }
645
646 template <typename B, null_count_policy NCP>
647 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
649 {
650 SPARROW_ASSERT_TRUE(pos < size());
651 if constexpr (std::is_pointer_v<storage_type>)
652 {
653 if (m_buffer == nullptr)
654 {
655 return true;
656 }
657 }
658 if (data() == nullptr)
659 {
660 return true;
661 }
662 const size_type actual_pos = m_offset + pos;
663 // Ensure the specific bit we're accessing is within the buffer
664 SPARROW_ASSERT_TRUE(block_index(actual_pos) < buffer().size());
665 return buffer().data()[block_index(actual_pos)] & bit_mask(actual_pos);
666 }
667
668 template <typename B, null_count_policy NCP>
669 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
671 {
672 SPARROW_ASSERT_TRUE(pos < size());
673 const size_type actual_pos = m_offset + pos;
674 const size_type target_block = block_index(actual_pos);
675
676 if (data() == nullptr)
677 {
678 if (value == true) // In this case, we don't need to set the bit
679 {
680 return;
681 }
682 else
683 {
684 if constexpr (requires(storage_type_without_cvrefpointer s) { s.resize(0); })
685 {
686 constexpr block_type true_value = block_type(~block_type(0));
687 const auto block_count = compute_block_count(size() + m_offset);
688 buffer().resize(block_count, true_value);
690 }
691 else
692 {
693 throw std::runtime_error("Cannot set a bit in a null buffer.");
694 }
695 }
696 }
697 else if (target_block >= buffer().size())
698 {
699 // Buffer exists but is too small for the bit we're accessing
700 if constexpr (requires(storage_type_without_cvrefpointer s) { s.resize(0); })
701 {
702 constexpr block_type true_value = block_type(~block_type(0));
703 buffer().resize(target_block + 1, true_value);
705 }
706 else
707 {
708 SPARROW_ASSERT_TRUE(target_block < buffer().size());
709 }
710 }
711
712 block_type& block = buffer().data()[target_block];
713 const bool old_value = block & bit_mask(actual_pos);
714 if (value)
715 {
716 block |= bit_mask(actual_pos);
717 }
718 else
719 {
720 block &= block_type(~bit_mask(actual_pos));
721 }
722 this->update_null_count(old_value, value);
723 }
724
725 template <typename B, null_count_policy NCP>
726 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
727 constexpr auto dynamic_bitset_base<B, NCP>::data() noexcept -> block_type*
728 {
729 if constexpr (std::is_pointer_v<storage_type>)
730 {
731 if (m_buffer == nullptr)
732 {
733 return nullptr;
734 }
735 }
736 return buffer().data();
737 }
738
739 template <typename B, null_count_policy NCP>
740 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
741 constexpr auto dynamic_bitset_base<B, NCP>::data() const noexcept -> const block_type*
742 {
743 return buffer().data();
744 }
745
746 template <typename B, null_count_policy NCP>
747 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
748 constexpr auto dynamic_bitset_base<B, NCP>::block_count() const noexcept -> size_type
749 {
750 return buffer().size();
751 }
752
753 template <typename B, null_count_policy NCP>
754 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
755 constexpr void dynamic_bitset_base<B, NCP>::swap(self_type& rhs) noexcept
756 {
757 using std::swap;
758 swap(m_buffer, rhs.m_buffer);
759 swap(m_size, rhs.m_size);
760 swap(m_offset, rhs.m_offset);
761 this->swap_null_count(rhs);
762 }
763
764 template <typename B, null_count_policy NCP>
765 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
767 {
768 return iterator(this, 0u);
769 }
770
771 template <typename B, null_count_policy NCP>
772 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
774 {
775 return iterator(this, size());
776 }
777
778 template <typename B, null_count_policy NCP>
779 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
781 {
782 return cbegin();
783 }
784
785 template <typename B, null_count_policy NCP>
786 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
788 {
789 return cend();
790 }
791
792 template <typename B, null_count_policy NCP>
793 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
795 {
796 return const_iterator(this, 0);
797 }
798
799 template <typename B, null_count_policy NCP>
800 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
802 {
803 return const_iterator(this, size());
804 }
805
806 template <typename B, null_count_policy NCP>
807 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
809 {
810 if (pos >= size())
811 {
812 throw std::out_of_range(
813 "dynamic_bitset_base::at: index out of range for dynamic_bitset_base of size"
814 + std::to_string(size()) + " at index " + std::to_string(pos)
815 );
816 }
817 return (*this)[pos];
818 }
819
820 template <typename B, null_count_policy NCP>
821 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
823 {
824 if (pos >= size())
825 {
826 throw std::out_of_range(
827 "dynamic_bitset_base::at: index out of range for dynamic_bitset_base of size"
828 + std::to_string(size()) + " at index " + std::to_string(pos)
829 );
830 }
831 return test(pos);
832 }
833
834 template <typename B, null_count_policy NCP>
835 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
837 {
839 return (*this)[0];
840 }
841
842 template <typename B, null_count_policy NCP>
843 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
845 {
847 if (data() == nullptr)
848 {
849 return true;
850 }
851 return (*this)[0];
852 }
853
854 template <typename B, null_count_policy NCP>
855 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
857 {
859 return (*this)[size() - 1];
860 }
861
862 template <typename B, null_count_policy NCP>
863 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
865 {
867 if (data() == nullptr)
868 {
869 return true;
870 }
871 return (*this)[size() - 1];
872 }
873
874 template <typename B, null_count_policy NCP>
875 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
877 : m_buffer(std::move(buf))
878 , m_size(size)
879 , m_offset(0)
880 {
881 this->initialize_null_count(data(), m_size, buffer().size(), m_offset);
882 }
883
884 template <typename B, null_count_policy NCP>
885 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
887 : m_buffer(std::move(buf))
888 , m_size(size)
889 , m_offset(offset)
890 {
891 this->initialize_null_count(data(), m_size, buffer().size(), m_offset);
892 }
893
894 template <typename B, null_count_policy NCP>
895 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
897 storage_type buf,
901 )
902 requires(NCP::track_null_count)
903 : m_buffer(std::move(buf))
904 , m_size(size)
905 , m_offset(offset)
906 {
908 }
909
910 template <typename B, null_count_policy NCP>
911 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
913 {
914 return bits_count / s_bits_per_block + static_cast<size_type>(bits_count % s_bits_per_block != 0);
915 }
916
917 template <typename B, null_count_policy NCP>
918 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
919 constexpr auto dynamic_bitset_base<B, NCP>::block_index(size_type pos) noexcept -> size_type
920 {
921 return pos / s_bits_per_block;
922 }
923
924 template <typename B, null_count_policy NCP>
925 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
926 constexpr auto dynamic_bitset_base<B, NCP>::bit_index(size_type pos) noexcept -> size_type
927 {
928 return pos % s_bits_per_block;
929 }
930
931 template <typename B, null_count_policy NCP>
932 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
933 constexpr auto dynamic_bitset_base<B, NCP>::bit_mask(size_type pos) noexcept -> block_type
934 {
935 const size_type bit = bit_index(pos);
936 return static_cast<block_type>(block_type(1) << bit);
937 }
938
939 template <typename B, null_count_policy NCP>
940 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
941 constexpr auto dynamic_bitset_base<B, NCP>::count_extra_bits() const noexcept -> size_type
942 {
943 return bit_index(size() + m_offset);
944 }
945
946 template <typename B, null_count_policy NCP>
947 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
949 {
950 if (data() == nullptr)
951 {
952 return;
953 }
954 const size_type extra_bits = count_extra_bits();
955 if (extra_bits != 0)
956 {
957 buffer().back() &= block_type(~(~block_type(0) << extra_bits));
958 }
959 }
960
961 template <typename B, null_count_policy NCP>
962 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
964 {
965 if ((data() == nullptr) && b)
966 {
967 m_size = n;
968 return;
969 }
970 auto& buffer = this->buffer();
971 const size_type old_size = m_size;
972 size_type old_block_count = buffer.size();
973 const size_type new_block_count = compute_block_count(n + m_offset);
974 const block_type value = b ? block_type(~block_type(0)) : block_type(0);
975
976 if (new_block_count != old_block_count)
977 {
978 if (data() == nullptr)
979 {
980 constexpr block_type true_value = block_type(~block_type(0));
981 old_block_count = compute_block_count(old_size + m_offset);
982 buffer.resize(old_block_count, true_value);
983 // Zero out bits beyond old_size in the last block
984 const size_type old_extra_bits = bit_index(old_size + m_offset);
985 if (old_extra_bits != 0)
986 {
987 buffer.back() &= block_type(~(~block_type(0) << old_extra_bits));
988 }
989 }
990 buffer.resize(new_block_count, value);
991 }
992
993 if (n > old_size)
994 {
995 // Calculate extra bits based on OLD size
996 const size_type old_extra_bits = bit_index(old_size + m_offset);
997 if (old_extra_bits > 0)
998 {
999 const size_type old_last_block_idx = compute_block_count(old_size + m_offset) - 1;
1000 auto& last_block = buffer.data()[old_last_block_idx];
1001 if (b)
1002 {
1003 // Set bits from old_extra_bits to end of block to true
1004 const auto mask = static_cast<block_type>(value << old_extra_bits);
1005 last_block |= mask;
1006 }
1007 else
1008 {
1009 // Clear bits from old_extra_bits to end of block
1010 const auto mask = static_cast<block_type>(~(~block_type(0) << old_extra_bits));
1011 last_block &= mask;
1012 }
1013 }
1014 }
1015
1016 m_size = n;
1018 this->recompute_null_count(data(), m_size, buffer.size(), m_offset);
1019 }
1020
1021 template <typename B, null_count_policy NCP>
1022 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1023 constexpr void dynamic_bitset_base<B, NCP>::clear() noexcept
1024 {
1025 buffer().clear();
1026 m_size = 0;
1027 this->clear_null_count();
1028 }
1029
1030 template <typename B, null_count_policy NCP>
1031 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1034 {
1035 return insert(pos, 1, value);
1036 }
1037
1038 template <typename B, null_count_policy NCP>
1039 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1042 {
1043 SPARROW_ASSERT_TRUE(cbegin() <= pos);
1044 SPARROW_ASSERT_TRUE(pos <= cend());
1045 const auto index = static_cast<size_type>(std::distance(cbegin(), pos));
1046 if (data() == nullptr && value)
1047 {
1048 m_size += count;
1049 }
1050 else
1051 {
1052 const size_type old_size = size();
1053 const size_type new_size = old_size + count;
1054
1055 resize(new_size);
1056
1057 // Shift existing bits to make room for new ones
1058 const size_type bits_to_shift = old_size - index;
1059 if (bits_to_shift > 0)
1060 {
1061 shift_bits_right(index + m_offset, bits_to_shift, count);
1062 }
1063
1064 // Fill the inserted region with the specified value
1065 fill_bits(index + m_offset, count, value);
1066
1067 // Recompute null count after all bit manipulations are complete
1068 this->recompute_null_count(data(), m_size, buffer().size(), m_offset);
1069 }
1070
1071 return iterator(this, index);
1072 }
1073
1074 template <typename B, null_count_policy NCP>
1075 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1076 template <std::input_iterator InputIt>
1078 dynamic_bitset_base<B, NCP>::insert(const_iterator pos, InputIt first, InputIt last)
1079 {
1080 const auto index = static_cast<size_type>(std::distance(cbegin(), pos));
1081 const auto count = static_cast<size_type>(std::distance(first, last));
1082 if (data() == nullptr)
1083 {
1084 if (std::all_of(
1085 first,
1086 last,
1087 [](auto v)
1088 {
1089 return bool(v);
1090 }
1091 ))
1092 {
1093 m_size += count;
1094 }
1095 return iterator(this, index);
1096 }
1097 SPARROW_ASSERT_TRUE(cbegin() <= pos);
1098 SPARROW_ASSERT_TRUE(pos <= cend());
1099
1100 const size_type old_size = size();
1101 const size_type new_size = old_size + count;
1102
1103 resize(new_size);
1104
1105 // Shift existing bits to make room for new ones
1106 const size_type bits_to_shift = old_size - index;
1107 if (bits_to_shift > 0)
1108 {
1109 shift_bits_right(index + m_offset, bits_to_shift, count);
1110 }
1111
1112 // Insert bits from the iterator range
1113 for (size_type i = 0; i < count; ++i)
1114 {
1115 set(index + i, *first++);
1116 }
1117
1118 // Recompute null count after all bit manipulations are complete
1119 this->recompute_null_count(data(), m_size, buffer().size(), m_offset);
1120
1121 return iterator(this, index);
1122 }
1123
1124 template <typename B, null_count_policy NCP>
1125 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1128 {
1129 return insert(pos, value);
1130 }
1131
1132 template <typename B, null_count_policy NCP>
1133 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1135 {
1136 SPARROW_ASSERT_TRUE(cbegin() <= pos);
1137 SPARROW_ASSERT_TRUE(pos < cend());
1138
1139 return erase(pos, pos + 1);
1140 }
1141
1142 template <typename B, null_count_policy NCP>
1143 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1146 {
1147 SPARROW_ASSERT_TRUE(cbegin() <= first);
1148 SPARROW_ASSERT_TRUE(first <= last);
1149 SPARROW_ASSERT_TRUE(last <= cend());
1150
1151 const auto first_index = static_cast<size_type>(std::distance(cbegin(), first));
1152 const auto last_index = static_cast<size_type>(std::distance(cbegin(), last));
1153 const size_type count = last_index - first_index;
1154
1155 if (data() == nullptr)
1156 {
1157 m_size -= count;
1158 }
1159 else
1160 {
1161 if (last == cend())
1162 {
1163 resize(first_index);
1164 return end();
1165 }
1166
1167 // Shift bits left using block-level operations
1168 const size_type bits_to_move = size() - last_index;
1169 if (bits_to_move > 0)
1170 {
1171 // Copy bits from [last_index, end) to [first_index, ...)
1172 for (size_type i = 0; i < bits_to_move; ++i)
1173 {
1174 const bool bit_value = test(last_index + i);
1175 set(first_index + i, bit_value);
1176 }
1177 }
1178
1179 resize(size() - count);
1180 }
1181 return iterator(this, first_index);
1182 }
1183
1184 template <typename B, null_count_policy NCP>
1185 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1187 {
1188 resize(size() + 1, value);
1189 }
1190
1191 template <typename B, null_count_policy NCP>
1192 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1194 {
1195 if (empty())
1196 {
1197 return;
1198 }
1199 resize(size() - 1);
1200 }
1201
1202 template <typename B, null_count_policy NCP>
1203 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1204 constexpr void
1205 dynamic_bitset_base<B, NCP>::shift_bits_right(size_type start, size_type length, size_type shift_amount)
1206 {
1207 if (length == 0 || shift_amount == 0)
1208 {
1209 return;
1210 }
1211
1212 auto* blocks = data();
1213 const size_type src_start_bit = start;
1214 const size_type dst_start_bit = start + shift_amount;
1215
1216 // Work backwards to avoid overwriting source data
1217 // Process bit-by-bit in reverse order, but in block-sized chunks where possible
1218 size_type remaining = length;
1219
1220 while (remaining > 0)
1221 {
1222 // Calculate position for this iteration (working backwards)
1223 const size_type current_offset = remaining - 1;
1224 const size_type src_bit = src_start_bit + current_offset;
1225 const size_type dst_bit = dst_start_bit + current_offset;
1226
1227 const size_type src_block_idx = block_index(src_bit);
1228 const size_type dst_block_idx = block_index(dst_bit);
1229 const size_type src_bit_offset = bit_index(src_bit);
1230 const size_type dst_bit_offset = bit_index(dst_bit);
1231
1232 // Determine how many bits we can process in this iteration
1233 // We can process up to the start of the current block, or all remaining bits
1234 const size_type bits_available_in_src_block = src_bit_offset + 1;
1235 const size_type bits_available_in_dst_block = dst_bit_offset + 1;
1236 const size_type bits_this_iter = std::min(
1237 {remaining, bits_available_in_src_block, bits_available_in_dst_block}
1238 );
1239
1240 // Extract bits from source
1241 const size_type extract_start_offset = src_bit_offset - (bits_this_iter - 1);
1242 const block_type mask = static_cast<block_type>(
1243 ((block_type(1) << bits_this_iter) - 1) << extract_start_offset
1244 );
1245 const block_type src_bits = static_cast<block_type>(
1246 (blocks[src_block_idx] & mask) >> extract_start_offset
1247 );
1248
1249 // Write bits to destination
1250 const size_type write_start_offset = dst_bit_offset - (bits_this_iter - 1);
1251 const block_type dst_mask = static_cast<block_type>(
1252 ((block_type(1) << bits_this_iter) - 1) << write_start_offset
1253 );
1254 blocks[dst_block_idx] = static_cast<block_type>(
1255 (blocks[dst_block_idx] & ~dst_mask) | ((src_bits << write_start_offset) & dst_mask)
1256 );
1257
1258 remaining -= bits_this_iter;
1259 }
1260 }
1261
1262 template <typename B, null_count_policy NCP>
1263 requires std::ranges::random_access_range<std::remove_pointer_t<B>>
1264 constexpr void dynamic_bitset_base<B, NCP>::fill_bits(size_type start, size_type count, value_type value)
1265 {
1266 if (count == 0)
1267 {
1268 return;
1269 }
1270
1271 auto* blocks = data();
1272 const block_type fill_pattern = value ? static_cast<block_type>(~block_type(0)) : block_type(0);
1273
1274 size_type current_bit = start;
1275 size_type remaining = count;
1276
1277 // Handle first partial block if not aligned
1278 const size_type first_bit_offset = bit_index(current_bit);
1279 if (first_bit_offset != 0)
1280 {
1281 const size_type bits_in_first = std::min(remaining, s_bits_per_block - first_bit_offset);
1282 const block_type mask = static_cast<block_type>(
1283 ((block_type(1) << bits_in_first) - 1) << first_bit_offset
1284 );
1285
1286 const size_type block_idx = block_index(current_bit);
1287 if (value)
1288 {
1289 blocks[block_idx] |= mask;
1290 }
1291 else
1292 {
1293 blocks[block_idx] &= ~mask;
1294 }
1295
1296 current_bit += bits_in_first;
1297 remaining -= bits_in_first;
1298 }
1299
1300 // Fill complete blocks
1301 while (remaining >= s_bits_per_block)
1302 {
1303 blocks[block_index(current_bit)] = fill_pattern;
1304 current_bit += s_bits_per_block;
1305 remaining -= s_bits_per_block;
1306 }
1307
1308 // Handle last partial block
1309 if (remaining > 0)
1310 {
1311 const block_type mask = static_cast<block_type>((block_type(1) << remaining) - 1);
1312 const size_type block_idx = block_index(current_bit);
1313 if (value)
1314 {
1315 blocks[block_idx] |= mask;
1316 }
1317 else
1318 {
1319 blocks[block_idx] &= ~mask;
1320 }
1321 }
1322 }
1323
1324}
Iterator used to iterate over the bits of a dynamic bitset as if they were addressable values.
A proxy reference class that provides mutable access to individual bits in a bitset.
constexpr const_reference at(size_type pos) const
constexpr reference operator[](size_type i)
Accesses a bit without bounds checking.
constexpr size_type block_count() const noexcept
typename storage_type_without_cvrefpointer::value_type block_type
constexpr dynamic_bitset_base(const dynamic_bitset_base &)=default
constexpr block_type * data() noexcept
constexpr void set(size_type pos, value_type value)
constexpr void set_size(size_type new_size) noexcept
static constexpr size_type compute_block_count(size_type bits_count) noexcept
Computes the number of blocks needed to store the specified number of bits.
constexpr const_iterator cbegin() const
constexpr iterator insert(const_iterator pos, value_type value)
constexpr storage_type_without_cvrefpointer & buffer() noexcept
Returns a mutable reference to the underlying buffer.
constexpr iterator erase(const_iterator pos)
constexpr bool test(size_type pos) const
constexpr iterator emplace(const_iterator pos, value_type value)
constexpr void resize(size_type n, value_type b=false)
bitset_iterator< self_type, true > const_iterator
constexpr size_type size() const noexcept
Returns the number of bits in the bitset.
bitset_iterator< self_type, false > iterator
constexpr const storage_type_without_cvrefpointer & buffer() const noexcept
typename storage_type_without_cvrefpointer::size_type size_type
constexpr const_iterator cend() const
constexpr bool empty() const noexcept
constexpr dynamic_bitset_base(storage_type buffer, size_type size)
Constructs a bitset with the given storage and size.
storage_type extract_storage() noexcept
Extracts the underlying storage (move operation).
std::remove_pointer_t< std::remove_cvref_t< storage_type > > storage_type_without_cvrefpointer
constexpr ~dynamic_bitset_base()=default
constexpr size_type null_count() const noexcept
constexpr dynamic_bitset_base(dynamic_bitset_base &&) noexcept=default
constexpr size_type offset() const noexcept
constexpr dynamic_bitset_base(storage_type buffer, size_type size, size_type offset)
Constructs a bitset with the given storage, size, and null count.
constexpr void push_back(value_type value)
constexpr void swap(self_type &rhs) noexcept
constexpr dynamic_bitset_base(storage_type buffer, size_type size, size_type offset, size_type null_count)
Constructs a bitset with the given storage, size, offset, and null count.
constexpr void set_offset(size_type offset) noexcept
dynamic_bitset_base< buffer< T >, tracking_null_count<> > self_type
typename storage_type_without_cvrefpointer::difference_type difference_type
void initialize_null_count(const BlockType *data, size_type bit_size, size_type block_count, size_type offset=0) noexcept
Initializes the null count by counting bits in the buffer.
constexpr void set_null_count(size_type count) noexcept
constexpr void swap_null_count(tracking_null_count &other) noexcept
void recompute_null_count(const BlockType *data, size_type bit_size, size_type block_count, size_type offset=0) noexcept
Recomputes the null count from the buffer.
constexpr void clear_null_count() noexcept
constexpr void update_null_count(bool old_value, bool new_value) noexcept
static constexpr bool track_null_count
Concept that checks if a type is a valid null count policy.
#define SPARROW_ASSERT_TRUE(expr__)
SPARROW_API int count(const std::string &key, int disabled_value=0)
constexpr std::size_t size(typelist< T... >={})
Gets the count of types contained in a typelist.
Definition mp_utils.hpp:216
Extensions to the C++ standard library.