1- using System ;
1+ // ReSharper disable MemberCanBePrivate.Global
2+
3+ using System ;
24using System . Buffers ;
35using System . Collections . Generic ;
4- using System . Runtime . InteropServices ;
56
67namespace ExtendedSystemObjects
78{
8- public sealed class UnmanagedKvStore : IDisposable
9+ public sealed class SortedKvStore : IDisposable
910 {
1011 private IntArray _keys ;
1112 private IntArray _values ;
1213 private IntArray _occupied ; // 0/1 flags
1314
1415 public int Count { get ; private set ; }
1516
16- public UnmanagedKvStore ( int initialCapacity = 16 )
17+ public SortedKvStore ( int initialCapacity = 16 )
1718 {
1819 _keys = new IntArray ( initialCapacity ) ;
1920 _values = new IntArray ( initialCapacity ) ;
@@ -36,6 +37,7 @@ public void Add(int key, int value)
3637 {
3738 _values [ idx ] = value ;
3839 }
40+
3941 return ;
4042 }
4143
@@ -96,18 +98,64 @@ public void RemoveByKey(int key)
9698 }
9799 }
98100
99- public void RemoveManyByKey ( IEnumerable < int > keysToRemove )
101+ public void RemoveManyByKey ( ReadOnlySpan < int > keysToRemove )
100102 {
101- var removalSet = new HashSet < int > ( keysToRemove ) ;
103+ if ( keysToRemove . Length == 0 )
104+ return ;
102105
103106 var occSpan = _occupied . AsSpan ( ) [ ..Count ] ;
104107 var keysSpan = _keys . AsSpan ( ) [ ..Count ] ;
105108
106- for ( int i = 0 ; i < Count ; i ++ )
109+ // Optional: if keysToRemove is sorted, do a linear merge
110+ // This path is faster than HashSet-based if input is sorted and large
111+ bool isSorted = true ;
112+ for ( int i = 1 ; i < keysToRemove . Length ; i ++ )
107113 {
108- if ( occSpan [ i ] != 0 && removalSet . Contains ( keysSpan [ i ] ) )
114+ if ( keysToRemove [ i ] >= keysToRemove [ i - 1 ] )
109115 {
110- occSpan [ i ] = 0 ;
116+ continue ;
117+ }
118+
119+ isSorted = false ;
120+ break ;
121+ }
122+
123+ if ( isSorted )
124+ {
125+ int i = 0 , j = 0 ;
126+ while ( i < Count && j < keysToRemove . Length )
127+ {
128+ int currentKey = keysSpan [ i ] ;
129+ int removeKey = keysToRemove [ j ] ;
130+
131+ if ( currentKey < removeKey )
132+ {
133+ i ++ ;
134+ }
135+ else if ( currentKey > removeKey )
136+ {
137+ j ++ ;
138+ }
139+ else
140+ {
141+ if ( occSpan [ i ] != 0 )
142+ occSpan [ i ] = 0 ;
143+
144+ i ++ ;
145+ j ++ ;
146+ }
147+ }
148+ }
149+ else
150+ {
151+ // Fall back to binary search (cheap because keys are sorted in store)
152+ for ( int n = 0 ; n < keysToRemove . Length ; n ++ )
153+ {
154+ int idx = BinarySearch ( keysToRemove [ n ] ) ;
155+ if ( idx >= 0 && occSpan [ idx ] != 0 )
156+ {
157+ occSpan [ idx ] = 0 ;
158+ }
111159 }
112160 }
113161 }
0 commit comments