1- using System . Runtime . CompilerServices ;
1+ namespace FastCloner . Code ;
22
3- namespace FastCloner . Code ;
3+ using System . Runtime . CompilerServices ;
44
5- internal class FastCloneState
5+ internal sealed class FastCloneState
66{
77 private MiniDictionary ? loops ;
88 private readonly object [ ] baseFromTo = new object [ 6 ] ;
99 private int idx ;
1010
11+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1112 public object ? GetKnownRef ( object from )
1213 {
13- // this is faster than call Dictionary from begin
14- // also, small poco objects does not have a lot of references
15- object [ ] baseFromTo = this . baseFromTo ;
16- if ( ReferenceEquals ( from , baseFromTo [ 0 ] ) ) return baseFromTo [ 3 ] ;
17- if ( ReferenceEquals ( from , baseFromTo [ 1 ] ) ) return baseFromTo [ 4 ] ;
18- if ( ReferenceEquals ( from , baseFromTo [ 2 ] ) ) return baseFromTo [ 5 ] ;
19-
20- return loops ? . FindEntry ( from ) ;
14+ return idx switch
15+ {
16+ 1 when ReferenceEquals ( from , baseFromTo [ 0 ] ) => baseFromTo [ 3 ] ,
17+ 2 when ReferenceEquals ( from , baseFromTo [ 0 ] ) => baseFromTo [ 3 ] ,
18+ 2 when ReferenceEquals ( from , baseFromTo [ 1 ] ) => baseFromTo [ 4 ] ,
19+ 3 when ReferenceEquals ( from , baseFromTo [ 0 ] ) => baseFromTo [ 3 ] ,
20+ 3 when ReferenceEquals ( from , baseFromTo [ 1 ] ) => baseFromTo [ 4 ] ,
21+ 3 when ReferenceEquals ( from , baseFromTo [ 2 ] ) => baseFromTo [ 5 ] ,
22+ _ => loops ? . FindEntry ( from )
23+ } ;
2124 }
2225
2326 public void AddKnownRef ( object from , object to )
@@ -34,22 +37,22 @@ public void AddKnownRef(object from, object to)
3437 loops . Insert ( from , to ) ;
3538 }
3639
37- private class MiniDictionary
40+ private sealed class MiniDictionary
3841 {
39- private struct Entry
42+ private readonly struct Entry ( int hashCode , int next , object key , object value )
4043 {
41- public int HashCode ;
42- public int Next ;
43- public object Key ;
44- public object Value ;
44+ public readonly int HashCode = hashCode ;
45+ public readonly int Next = next ;
46+ public readonly object Key = key ;
47+ public readonly object Value = value ;
4548 }
4649
4750 private int [ ] ? buckets ;
4851 private Entry [ ] entries ;
4952 private int count ;
53+ private const int DefaultCapacity = 5 ;
5054
51-
52- public MiniDictionary ( ) : this ( 5 )
55+ public MiniDictionary ( ) : this ( DefaultCapacity )
5356 {
5457 }
5558
@@ -59,22 +62,29 @@ public MiniDictionary(int capacity)
5962 Initialize ( capacity ) ;
6063 }
6164
62- public object FindEntry ( object key )
65+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
66+ public object ? FindEntry ( object key )
6367 {
64- if ( buckets != null )
68+ if ( buckets is null )
6569 {
66- int hashCode = RuntimeHelpers . GetHashCode ( key ) & 0x7FFFFFFF ;
67- Entry [ ] entries1 = entries ;
68- for ( int i = buckets [ hashCode % buckets . Length ] ; i >= 0 ; i = entries1 [ i ] . Next )
69- {
70- if ( entries1 [ i ] . HashCode == hashCode && ReferenceEquals ( entries1 [ i ] . Key , key ) )
71- return entries1 [ i ] . Value ;
72- }
70+ return null ;
71+ }
72+
73+ int hashCode = RuntimeHelpers . GetHashCode ( key ) & 0x7FFFFFFF ;
74+ int bucketIndex = hashCode % buckets . Length ;
75+
76+ Entry [ ] entriesLocal = entries ;
77+ for ( int i = buckets [ bucketIndex ] ; i >= 0 ; i = entriesLocal [ i ] . Next )
78+ {
79+ ref readonly Entry entry = ref entriesLocal [ i ] ;
80+ if ( entry . HashCode == hashCode && ReferenceEquals ( entry . Key , key ) )
81+ return entry . Value ;
7382 }
7483
7584 return null ;
7685 }
7786
87+ // Hash Table Primes, stabilizes to ~1.2x increase
7888 private static readonly int [ ] primes =
7989 [
8090 3 , 7 , 11 , 17 , 23 , 29 , 37 , 47 , 59 , 71 , 89 , 107 , 131 , 163 , 197 , 239 , 293 , 353 , 431 , 521 , 631 , 761 , 919 ,
@@ -84,84 +94,89 @@ public object FindEntry(object key)
8494 1674319 , 2009191 , 2411033 , 2893249 , 3471899 , 4166287 , 4999559 , 5999471 , 7199369
8595 ] ;
8696
97+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
8798 private static int GetPrime ( int min )
8899 {
89- for ( int i = 0 ; i < primes . Length ; i ++ )
100+ int left = 0 ;
101+ int right = primes . Length - 1 ;
102+
103+ while ( left <= right )
90104 {
91- int prime = primes [ i ] ;
92- if ( prime >= min ) return prime ;
105+ int mid = left + ( right - left ) / 2 ;
106+ if ( primes [ mid ] >= min )
107+ {
108+ right = mid - 1 ;
109+ }
110+ else
111+ {
112+ left = mid + 1 ;
113+ }
93114 }
115+
116+ return left < primes . Length ? primes [ left ] : GeneratePrime ( min ) ;
117+ }
94118
95- //outside of our predefined table.
96- //compute the hard way.
119+ private static int GeneratePrime ( int min )
120+ {
97121 for ( int i = min | 1 ; i < int . MaxValue ; i += 2 )
98122 {
99- if ( IsPrime ( i ) && ( i - 1 ) % 101 != 0 )
123+ if ( IsPrime ( i ) && ( i - 1 ) % 101 is not 0 )
100124 return i ;
101125 }
102-
103126 return min ;
104127 }
105128
106129 private static bool IsPrime ( int candidate )
107130 {
108- if ( ( candidate & 1 ) != 0 )
131+ if ( ( candidate & 1 ) is 0 )
132+ return candidate is 2 ;
133+
134+ int limit = ( int ) Math . Sqrt ( candidate ) ;
135+ for ( int divisor = 3 ; divisor <= limit ; divisor += 2 )
109136 {
110- int limit = ( int ) Math . Sqrt ( candidate ) ;
111- for ( int divisor = 3 ; divisor <= limit ; divisor += 2 )
112- {
113- if ( ( candidate % divisor ) == 0 )
114- return false ;
115- }
116-
117- return true ;
137+ if ( candidate % divisor is 0 )
138+ return false ;
118139 }
119-
120- return candidate == 2 ;
140+ return true ;
121141 }
122142
123143 private static int ExpandPrime ( int oldSize )
124144 {
125145 int newSize = 2 * oldSize ;
126-
127146 if ( ( uint ) newSize > 0x7FEFFFFD && 0x7FEFFFFD > oldSize )
128- {
129147 return 0x7FEFFFFD ;
130- }
131-
132148 return GetPrime ( newSize ) ;
133149 }
134150
135151 private void Initialize ( int size )
136152 {
137153 buckets = new int [ size ] ;
138- for ( int i = 0 ; i < buckets . Length ; i ++ )
139- buckets [ i ] = - 1 ;
154+ Array . Fill ( buckets , - 1 ) ;
140155 entries = new Entry [ size ] ;
141156 }
142157
158+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
143159 public void Insert ( object key , object value )
144160 {
145- if ( buckets == null ) Initialize ( 0 ) ;
161+ if ( buckets is null )
162+ Initialize ( DefaultCapacity ) ;
163+
146164 int hashCode = RuntimeHelpers . GetHashCode ( key ) & 0x7FFFFFFF ;
147- int targetBucket = hashCode % buckets . Length ;
148-
149- Entry [ ] entries1 = entries ;
165+ int targetBucket = hashCode % buckets ! . Length ;
150166
151- if ( count == entries1 . Length )
167+ if ( count == entries . Length )
152168 {
153169 Resize ( ) ;
154- entries1 = entries ;
155170 targetBucket = hashCode % buckets . Length ;
156171 }
157172
158- int index = count ;
159- count ++ ;
160-
161- entries1 [ index ] . HashCode = hashCode ;
162- entries1 [ index ] . Next = buckets [ targetBucket ] ;
163- entries1 [ index ] . Key = key ;
164- entries1 [ index ] . Value = value ;
173+ int index = count ++ ;
174+ entries [ index ] = new Entry (
175+ hashCode ,
176+ buckets [ targetBucket ] ,
177+ key ,
178+ value
179+ ) ;
165180 buckets [ targetBucket ] = index ;
166181 }
167182
@@ -170,23 +185,30 @@ public void Insert(object key, object value)
170185 private void Resize ( int newSize )
171186 {
172187 int [ ] newBuckets = new int [ newSize ] ;
173- for ( int i = 0 ; i < newBuckets . Length ; i ++ )
174- newBuckets [ i ] = - 1 ;
188+ Array . Fill ( newBuckets , - 1 ) ;
189+
175190 Entry [ ] newEntries = new Entry [ newSize ] ;
176191 Array . Copy ( entries , 0 , newEntries , 0 , count ) ;
177192
178193 for ( int i = 0 ; i < count ; i ++ )
179194 {
180- if ( newEntries [ i ] . HashCode >= 0 )
195+ if ( newEntries [ i ] . HashCode < 0 )
181196 {
182- int bucket = newEntries [ i ] . HashCode % newSize ;
183- newEntries [ i ] . Next = newBuckets [ bucket ] ;
184- newBuckets [ bucket ] = i ;
197+ continue ;
185198 }
199+
200+ int bucket = newEntries [ i ] . HashCode % newSize ;
201+ newEntries [ i ] = new Entry (
202+ newEntries [ i ] . HashCode ,
203+ newBuckets [ bucket ] ,
204+ newEntries [ i ] . Key ,
205+ newEntries [ i ] . Value
206+ ) ;
207+ newBuckets [ bucket ] = i ;
186208 }
187209
188210 buckets = newBuckets ;
189211 entries = newEntries ;
190212 }
191213 }
192- }
214+ }
0 commit comments