@@ -126,89 +126,55 @@ public void RemoveAt(int index)
126126 /// Removes all specified indices from the array
127127 /// </summary>
128128 /// <param name="indicesToRemove">The indices to remove.</param>
129- public void RemoveMultiple ( int [ ] indicesToRemove )
129+ /// <summary>
130+ /// Removes multiple elements efficiently. If the indices are sequential and sorted,
131+ /// uses a fast bulk-removal path. Otherwise falls back to element-wise compaction.
132+ /// </summary>
133+ /// <param name="indices">Indices to remove. Must be sorted and within bounds.</param>
134+ public void RemoveMultiple ( ReadOnlySpan < int > indices )
130135 {
131- if ( indicesToRemove == null || indicesToRemove . Length == 0 )
132- {
133- return ;
134- }
136+ if ( indices . Length == 0 ) return ;
135137
136- // Sort indices ascending (important!)
137- Array . Sort ( indicesToRemove ) ;
138+ // === Fast-path: Trivial sequential sequence ===
139+ // If [3,4,5,6] then indices[^1] - indices[0] == indices.Length - 1
140+ if ( indices . Length > 1 && indices [ ^ 1 ] - indices [ 0 ] == indices . Length - 1 )
141+ {
142+ int start = indices [ 0 ] ;
143+ int count = indices . Length ;
138144
139- // Validate indices within bounds
140145#if DEBUG
141- foreach ( var idx in indicesToRemove )
142- {
143- if ( idx < 0 || idx >= Length )
144- {
145- throw new IndexOutOfRangeException ( $ "Index { idx } is out of range.") ;
146- }
147- }
146+ if ( start < 0 || start + count > Length )
147+ throw new IndexOutOfRangeException ( ) ;
148148#endif
149149
150- if ( IsSequential ( indicesToRemove ) )
151- {
152- // Optimized path for continuous block removal
153- var start = indicesToRemove [ 0 ] ;
154- var count = indicesToRemove . Length ;
155-
156- // Move tail elements left by count positions
157- var tailLength = Length - ( start + count ) ;
158- if ( tailLength > 0 )
159- {
160- Buffer . MemoryCopy (
161- _ptr + start + count ,
162- _ptr + start ,
163- tailLength * sizeof ( int ) ,
164- tailLength * sizeof ( int )
165- ) ;
166- }
150+ int moveCount = Length - ( start + count ) ;
151+ if ( moveCount > 0 )
152+ Buffer . MemoryCopy ( _ptr + start + count , _ptr + start , moveCount * sizeof ( int ) , moveCount * sizeof ( int ) ) ;
167153
168154 Length -= count ;
155+ return ;
169156 }
170- else
171- {
172- // General multi-block removal, no assumption on indices continuity
173157
174- var srcIndex = 0 ;
175- var dstIndex = 0 ;
158+ // === Fallback: Compact array by skipping indices ===
159+ int readIndex = 0 , writeIndex = 0 , removeIndex = 0 ;
176160
177- foreach ( var removeIndex in indicesToRemove )
161+ while ( readIndex < Length )
162+ {
163+ if ( removeIndex < indices . Length && readIndex == indices [ removeIndex ] )
178164 {
179- var lengthToCopy = removeIndex - srcIndex ;
180-
181- if ( lengthToCopy > 0 )
182- {
183- Buffer . MemoryCopy (
184- _ptr + srcIndex ,
185- _ptr + dstIndex ,
186- ( Length - dstIndex ) * sizeof ( int ) ,
187- lengthToCopy * sizeof ( int )
188- ) ;
189- dstIndex += lengthToCopy ;
190- }
191-
192- srcIndex = removeIndex + 1 ;
165+ readIndex ++ ;
166+ removeIndex ++ ;
193167 }
194-
195- // Copy remaining tail block after last removed index
196- var tailLength = Length - srcIndex ;
197- if ( tailLength > 0 )
168+ else
198169 {
199- Buffer . MemoryCopy (
200- _ptr + srcIndex ,
201- _ptr + dstIndex ,
202- ( Length - dstIndex ) * sizeof ( int ) ,
203- tailLength * sizeof ( int )
204- ) ;
205- dstIndex += tailLength ;
170+ _ptr [ writeIndex ++ ] = _ptr [ readIndex ++ ] ;
206171 }
207-
208- Length = dstIndex ;
209172 }
173+
174+ Length = writeIndex ;
210175 }
211176
177+
212178 /// <summary>
213179 /// Resizes the internal array to the specified new size.
214180 /// Contents will be preserved up to the minimum of old and new size.
@@ -242,31 +208,6 @@ public Span<int> AsSpan()
242208 return new ( ( void * ) _buffer , Length ) ;
243209 }
244210
245- /// <summary>
246- /// Determines whether the specified sorted indices is sequential.
247- /// </summary>
248- /// <param name="sortedIndices">The sorted indices.</param>
249- /// <returns>
250- /// <c>true</c> if the specified sorted indices is sequential; otherwise, <c>false</c>.
251- /// </returns>
252- private static bool IsSequential ( IReadOnlyList < int > sortedIndices )
253- {
254- if ( sortedIndices is not { Count : > 1 } )
255- {
256- return true ;
257- }
258-
259- for ( var i = 1 ; i < sortedIndices . Count ; i ++ )
260- {
261- if ( sortedIndices [ i ] != sortedIndices [ i - 1 ] + 1 )
262- {
263- return false ;
264- }
265- }
266-
267- return true ;
268- }
269-
270211 /// <summary>
271212 /// Finalizes an instance of the <see cref="IntArray" /> class.
272213 /// </summary>
0 commit comments