17
17
18
18
#include < algorithm>
19
19
#include < bit>
20
+ #include < iterator>
20
21
#include < stdexcept>
21
22
#include < string>
22
23
#include < type_traits>
24
+ #include < vector>
23
25
24
26
#include " sparrow/buffer/dynamic_bitset/bitset_iterator.hpp"
25
27
#include " sparrow/buffer/dynamic_bitset/bitset_reference.hpp"
@@ -161,6 +163,13 @@ namespace sparrow
161
163
[[nodiscard]] constexpr size_type count_extra_bits () const noexcept ;
162
164
constexpr void update_null_count (bool old_value, bool new_value);
163
165
166
+ // Efficient bit manipulation helpers for insert operations
167
+ constexpr void shift_bits_right (size_type start_pos, size_type bit_count, size_type shift_amount);
168
+ constexpr void fill_bits_range (size_type start_pos, size_type bit_count, value_type value);
169
+ template <std::random_access_iterator InputIt>
170
+ constexpr iterator
171
+ insert_range_random_access (const_iterator pos, InputIt first, size_type count, size_type index);
172
+
164
173
storage_type m_buffer;
165
174
size_type m_size;
166
175
size_type m_null_count;
@@ -592,30 +601,31 @@ namespace sparrow
592
601
SPARROW_ASSERT_TRUE (cbegin () <= pos);
593
602
SPARROW_ASSERT_TRUE (pos <= cend ());
594
603
const auto index = static_cast <size_type>(std::distance (cbegin (), pos));
604
+
595
605
if (data () == nullptr && value)
596
606
{
597
607
m_size += count;
608
+ return iterator (this , index);
598
609
}
599
- else
600
- {
601
- const size_type old_size = size ();
602
- const size_type new_size = old_size + count;
603
610
604
- // TODO: The current implementation is not efficient. It can be improved.
611
+ if (count == 0 )
612
+ {
613
+ return iterator (this , index);
614
+ }
605
615
606
- resize (new_size);
616
+ const size_type old_size = size ();
617
+ const size_type new_size = old_size + count;
618
+ const size_type bits_to_move = old_size - index;
607
619
608
- for (size_type i = old_size + count - 1 ; i >= index + count; --i)
609
- {
610
- set (i, test (i - count));
611
- }
620
+ resize (new_size);
612
621
613
- for (size_type i = 0 ; i < count; ++i)
614
- {
615
- set (index + i, value);
616
- }
622
+ if (bits_to_move > 0 )
623
+ {
624
+ shift_bits_right (index, bits_to_move, count);
617
625
}
618
626
627
+ fill_bits_range (index, count, value);
628
+
619
629
return iterator (this , index);
620
630
}
621
631
@@ -626,43 +636,19 @@ namespace sparrow
626
636
dynamic_bitset_base<B>::insert(const_iterator pos, InputIt first, InputIt last)
627
637
{
628
638
const auto index = static_cast <size_type>(std::distance (cbegin (), pos));
629
- const auto count = static_cast <size_type>(std::distance (first, last));
630
- if (data () == nullptr )
631
- {
632
- if (std::all_of (
633
- first,
634
- last,
635
- [](auto v)
636
- {
637
- return bool (v);
638
- }
639
- ))
640
- {
641
- m_size += count;
642
- }
643
- return iterator (this , index);
644
- }
645
- SPARROW_ASSERT_TRUE (cbegin () <= pos);
646
- SPARROW_ASSERT_TRUE (pos <= cend ());
647
-
648
- const size_type old_size = size ();
649
- const size_type new_size = old_size + count;
650
639
651
- resize (new_size);
652
-
653
- // TODO: The current implementation is not efficient. It can be improved.
654
-
655
- for (size_type i = old_size + count - 1 ; i >= index + count; --i)
640
+ if constexpr (std::random_access_iterator<InputIt>)
656
641
{
657
- set (i, test (i - count));
642
+ // Fast path for random access iterators
643
+ const auto count = static_cast <size_type>(std::distance (first, last));
644
+ return insert_range_random_access (pos, first, count, index);
658
645
}
659
-
660
- for (size_type i = 0 ; i < count; ++i)
646
+ else
661
647
{
662
- set (index + i, *first++);
648
+ // Slower path for input iterators - collect values first
649
+ std::vector<value_type> values (first, last);
650
+ return insert_range_random_access (pos, values.begin (), values.size (), index);
663
651
}
664
-
665
- return iterator (this , index);
666
652
}
667
653
668
654
template <typename B>
@@ -711,7 +697,7 @@ namespace sparrow
711
697
// TODO: The current implementation is not efficient. It can be improved.
712
698
713
699
const size_type bit_to_move = size () - last_index;
714
- for (size_type i = 0 ; i < bit_to_move; ++i)
700
+ for (size_t i = 0 ; i < bit_to_move; ++i)
715
701
{
716
702
set (first_index + i, test (last_index + i));
717
703
}
@@ -738,4 +724,223 @@ namespace sparrow
738
724
}
739
725
resize (size () - 1 );
740
726
}
727
+
728
+ // Efficient helper functions for insert operations
729
+
730
+ template <typename B>
731
+ requires std::ranges::random_access_range<std::remove_pointer_t <B>>
732
+ constexpr void
733
+ dynamic_bitset_base<B>::shift_bits_right(size_type start_pos, size_type bit_count, size_type shift_amount)
734
+ {
735
+ if (bit_count == 0 || shift_amount == 0 || data () == nullptr )
736
+ {
737
+ return ;
738
+ }
739
+
740
+ const size_type end_pos = start_pos + bit_count;
741
+
742
+ // Calculate block boundaries
743
+ const size_type start_block = block_index (start_pos);
744
+ const size_type end_block = block_index (end_pos - 1 );
745
+ const size_type target_start_block = block_index (start_pos + shift_amount);
746
+ const size_type target_end_block = block_index (end_pos + shift_amount - 1 );
747
+
748
+ // If the shift spans multiple blocks, use block-level operations
749
+ if (shift_amount >= s_bits_per_block && start_block != end_block)
750
+ {
751
+ const size_type block_shift = shift_amount / s_bits_per_block;
752
+ const size_type bit_shift = shift_amount % s_bits_per_block;
753
+
754
+ // Move whole blocks first
755
+ for (size_type i = end_block; i >= start_block && i != SIZE_MAX; --i)
756
+ {
757
+ const size_type target_block = i + block_shift;
758
+ if (target_block < buffer ().size ())
759
+ {
760
+ buffer ().data ()[target_block] = buffer ().data ()[i];
761
+ }
762
+ }
763
+
764
+ // Handle remaining bit shift within blocks
765
+ if (bit_shift > 0 )
766
+ {
767
+ for (size_type i = target_end_block; i > target_start_block && i != SIZE_MAX; --i)
768
+ {
769
+ const block_type current = buffer ().data ()[i];
770
+ const block_type previous = (i > 0 ) ? buffer ().data ()[i - 1 ] : block_type (0 );
771
+ buffer ().data ()[i] = static_cast <block_type>(
772
+ (current << bit_shift) | (previous >> (s_bits_per_block - bit_shift))
773
+ );
774
+ }
775
+ if (target_start_block < buffer ().size ())
776
+ {
777
+ buffer ().data ()[target_start_block] = static_cast <block_type>(
778
+ buffer ().data ()[target_start_block] << bit_shift
779
+ );
780
+ }
781
+ }
782
+ }
783
+ else
784
+ {
785
+ // For smaller shifts, use bit-level operations optimized for the shift amount
786
+ for (size_type i = bit_count; i > 0 ; --i)
787
+ {
788
+ const size_t src_pos = start_pos + i - 1 ;
789
+ const size_t dst_pos = src_pos + shift_amount;
790
+ set (dst_pos, test (src_pos));
791
+ }
792
+ }
793
+ }
794
+
795
+ template <typename B>
796
+ requires std::ranges::random_access_range<std::remove_pointer_t <B>>
797
+ constexpr void
798
+ dynamic_bitset_base<B>::fill_bits_range(size_type start_pos, size_type bit_count, value_type value)
799
+ {
800
+ if (bit_count == 0 || data () == nullptr )
801
+ {
802
+ return ;
803
+ }
804
+
805
+ const size_type end_pos = start_pos + bit_count;
806
+ const size_type start_block = block_index (start_pos);
807
+ const size_type end_block = block_index (end_pos - 1 );
808
+
809
+ const block_type fill_value = value ? block_type (~block_type (0 )) : block_type (0 );
810
+
811
+ if (start_block == end_block)
812
+ {
813
+ // All bits are in the same block - use efficient bit masking
814
+ const size_type start_bit = bit_index (start_pos);
815
+ const size_type end_bit = bit_index (end_pos - 1 );
816
+ const size_type mask_width = end_bit - start_bit + 1 ;
817
+ const block_type mask = static_cast <block_type>(((block_type (1 ) << mask_width) - 1 ) << start_bit);
818
+
819
+ if (value)
820
+ {
821
+ buffer ().data ()[start_block] |= mask;
822
+ }
823
+ else
824
+ {
825
+ buffer ().data ()[start_block] &= ~mask;
826
+ }
827
+ }
828
+ else
829
+ {
830
+ // Handle first partial block
831
+ const size_type start_bit = bit_index (start_pos);
832
+ if (start_bit != 0 )
833
+ {
834
+ const block_type mask = static_cast <block_type>(~block_type (0 ) << start_bit);
835
+ if (value)
836
+ {
837
+ buffer ().data ()[start_block] |= mask;
838
+ }
839
+ else
840
+ {
841
+ buffer ().data ()[start_block] &= ~mask;
842
+ }
843
+ }
844
+ else
845
+ {
846
+ buffer ().data ()[start_block] = fill_value;
847
+ }
848
+
849
+ // Handle full blocks in between
850
+ for (size_type block = start_block + 1 ; block < end_block; ++block)
851
+ {
852
+ buffer ().data ()[block] = fill_value;
853
+ }
854
+
855
+ // Handle last partial block
856
+ const size_type end_bit = bit_index (end_pos - 1 );
857
+ const block_type mask = static_cast <block_type>((block_type (1 ) << (end_bit + 1 )) - 1 );
858
+ if (value)
859
+ {
860
+ buffer ().data ()[end_block] |= mask;
861
+ }
862
+ else
863
+ {
864
+ buffer ().data ()[end_block] &= ~mask;
865
+ }
866
+ }
867
+
868
+ m_null_count = m_size - count_non_null ();
869
+ }
870
+
871
+ template <typename B>
872
+ requires std::ranges::random_access_range<std::remove_pointer_t <B>>
873
+ template <std::random_access_iterator InputIt>
874
+ constexpr auto dynamic_bitset_base<B>::insert_range_random_access(
875
+ const_iterator /* pos */ ,
876
+ InputIt first,
877
+ size_type count,
878
+ size_type index
879
+ ) -> iterator
880
+ {
881
+ if (data () == nullptr )
882
+ {
883
+ if (std::all_of (
884
+ first,
885
+ std::next (first, static_cast <std::ptrdiff_t >(count)),
886
+ [](auto v)
887
+ {
888
+ return bool (v);
889
+ }
890
+ ))
891
+ {
892
+ m_size += count;
893
+ }
894
+ return iterator (this , index);
895
+ }
896
+
897
+ if (count == 0 )
898
+ {
899
+ return iterator (this , index);
900
+ }
901
+
902
+ const size_type old_size = size ();
903
+ const size_type new_size = old_size + count;
904
+ const size_type bits_to_move = old_size - index;
905
+
906
+ resize (new_size);
907
+
908
+ if (bits_to_move > 0 )
909
+ {
910
+ shift_bits_right (index, bits_to_move, count);
911
+ }
912
+
913
+ // Set bits efficiently in batches
914
+ constexpr size_type batch_size = s_bits_per_block;
915
+ for (size_type i = 0 ; i < count; i += batch_size)
916
+ {
917
+ const size_type current_batch_size = std::min (batch_size, count - i);
918
+ const size_type batch_start = index + i;
919
+
920
+ // Process bits in the current batch
921
+ if (current_batch_size == s_bits_per_block && bit_index (batch_start) == 0 )
922
+ {
923
+ // Optimized path: entire block can be set at once
924
+ block_type block_value = 0 ;
925
+ for (size_type j = 0 ; j < s_bits_per_block; ++j)
926
+ {
927
+ if (bool (*(std::next (first, static_cast <std::ptrdiff_t >(i + j)))))
928
+ {
929
+ block_value |= static_cast <block_type>(block_type (1 ) << j);
930
+ }
931
+ }
932
+ buffer ().data ()[block_index (batch_start)] = block_value;
933
+ }
934
+ else
935
+ {
936
+ // Fallback to bit-by-bit setting for partial blocks
937
+ for (size_type j = 0 ; j < current_batch_size; ++j)
938
+ {
939
+ set (batch_start + j, bool (*(std::next (first, static_cast <std::ptrdiff_t >(i + j)))));
940
+ }
941
+ }
942
+ }
943
+
944
+ return iterator (this , index);
945
+ }
741
946
}
0 commit comments