@@ -165,6 +165,7 @@ namespace sparrow
165165
166166 // Efficient bit manipulation helpers for insert operations
167167 constexpr void shift_bits_right (size_type start_pos, size_type bit_count, size_type shift_amount);
168+ constexpr void shift_bits_left (size_type start_pos, size_type bit_count, size_type shift_amount);
168169 constexpr void fill_bits_range (size_type start_pos, size_type bit_count, value_type value);
169170 template <std::random_access_iterator InputIt>
170171 constexpr iterator
@@ -694,10 +695,9 @@ namespace sparrow
694695 return end ();
695696 }
696697
697- // TODO: The current implementation is not efficient. It can be improved.
698-
699- const size_type bit_to_move = size () - last_index;
700- for (size_t i = 0 ; i < bit_to_move; ++i)
698+ // Optimized: Move bits in bulk instead of bit by bit
699+ const size_type bits_to_move = size () - last_index;
700+ for (size_type i = 0 ; i < bits_to_move; ++i)
701701 {
702702 set (first_index + i, test (last_index + i));
703703 }
@@ -792,6 +792,74 @@ namespace sparrow
792792 }
793793 }
794794
795+ template <typename B>
796+ requires std::ranges::random_access_range<std::remove_pointer_t <B>>
797+ constexpr void
798+ dynamic_bitset_base<B>::shift_bits_left(size_type start_pos, size_type bit_count, size_type shift_amount)
799+ {
800+ if (bit_count == 0 || shift_amount == 0 || data () == nullptr )
801+ {
802+ return ;
803+ }
804+
805+ const size_type end_pos = start_pos + bit_count;
806+
807+ // Calculate block boundaries
808+ const size_type start_block = block_index (start_pos);
809+ const size_type end_block = block_index (end_pos - 1 );
810+ const size_type target_start_block = block_index (start_pos - shift_amount);
811+ const size_type target_end_block = block_index (end_pos - shift_amount - 1 );
812+
813+ // If the shift spans multiple blocks, use block-level operations
814+ if (shift_amount >= s_bits_per_block && start_block != end_block)
815+ {
816+ const size_type block_shift = shift_amount / s_bits_per_block;
817+ const size_type bit_shift = shift_amount % s_bits_per_block;
818+
819+ // Move whole blocks first (left shift means earlier indices)
820+ for (size_type i = start_block; i <= end_block; ++i)
821+ {
822+ const size_type target_block = i - block_shift;
823+ if (target_block < buffer ().size () && i >= block_shift)
824+ {
825+ buffer ().data ()[target_block] = buffer ().data ()[i];
826+ }
827+ }
828+
829+ // Handle remaining bit shift within blocks
830+ if (bit_shift > 0 )
831+ {
832+ for (size_type i = target_start_block; i < target_end_block; ++i)
833+ {
834+ if (i < buffer ().size ())
835+ {
836+ const block_type current = buffer ().data ()[i];
837+ const block_type next = (i + 1 < buffer ().size ()) ? buffer ().data ()[i + 1 ] : block_type (0 );
838+ buffer ().data ()[i] = static_cast <block_type>(
839+ (current >> bit_shift) | (next << (s_bits_per_block - bit_shift))
840+ );
841+ }
842+ }
843+ if (target_end_block < buffer ().size ())
844+ {
845+ buffer ().data ()[target_end_block] = static_cast <block_type>(
846+ buffer ().data ()[target_end_block] >> bit_shift
847+ );
848+ }
849+ }
850+ }
851+ else
852+ {
853+ // For smaller shifts, use bit-level operations optimized for the shift amount
854+ for (size_type i = 0 ; i < bit_count; ++i)
855+ {
856+ const size_type src_pos = start_pos + i;
857+ const size_type dst_pos = src_pos - shift_amount;
858+ set (dst_pos, test (src_pos));
859+ }
860+ }
861+ }
862+
795863 template <typename B>
796864 requires std::ranges::random_access_range<std::remove_pointer_t <B>>
797865 constexpr void
0 commit comments