22 * COPYRIGHT: See COPYING in the top level directory
33 * PROJECT: ExtendedSystemObjects
44 * FILE: ExtendedSystemObjects/ImmutableLookupMap.cs
5- * PURPOSE: A high-performance, immutable lookup map that uses an array-based internal structure for fast key-value lookups. Tis one is for unmanaged only. It uses my UnmanagedArray.
6- * PROGRAMER: Peter Geinitz (Wayfarer)
5+ * PURPOSE: A high-performance, immutable lookup map that uses an array-based internal structure for fast key-value lookups.
6+ * This version is limited to unmanaged types and uses UnmanagedArray<T>.
7+ * PROGRAMMER: Peter Geinitz (Wayfarer)
78 */
89
910// ReSharper disable UnusedMember.Global
@@ -19,19 +20,45 @@ namespace ExtendedSystemObjects
1920{
2021 /// <summary>
2122 /// A high-performance, immutable lookup map using unmanaged arrays.
23+ /// Suitable for value types only. Keys must be unique.
2224 /// </summary>
2325 public sealed unsafe class ImmutableLookupMapUnmanaged < TKey , TValue > : IDisposable , IEnumerable < KeyValuePair < TKey , TValue > >
2426 where TKey : unmanaged, IEquatable < TKey >
2527 where TValue : unmanaged
2628 {
29+ /// <summary>
30+ /// The keys
31+ /// </summary>
2732 private readonly UnmanagedArray < TKey > _keys ;
33+
34+ /// <summary>
35+ /// The values
36+ /// </summary>
2837 private readonly UnmanagedArray < TValue > _values ;
38+
39+ /// <summary>
40+ /// Indicates whether a key is present at the given hash slot.
41+ /// </summary>
2942 private readonly UnmanagedArray < byte > _keyPresence ;
3043
44+ /// <summary>
45+ /// The capacity
46+ /// </summary>
3147 private readonly int _capacity ;
3248
49+ /// <summary>
50+ /// Gets the number of entries in the map.
51+ /// </summary>
3352 public int Count { get ; }
3453
54+ /// <summary>
55+ /// Initializes a new instance of the <see cref="ImmutableLookupMapUnmanaged{TKey, TValue}"/> class
56+ /// with the specified key-value data.
57+ /// </summary>
58+ /// <param name="data">A dictionary containing the initial key-value pairs.</param>
59+ /// <exception cref="ArgumentNullException">Thrown when <paramref name="data"/> is null.</exception>
60+ /// <exception cref="InvalidOperationException">Thrown when a duplicate key is found.</exception>
61+
3562 public ImmutableLookupMapUnmanaged ( IDictionary < TKey , TValue > data )
3663 {
3764 if ( data == null )
@@ -48,7 +75,7 @@ public ImmutableLookupMapUnmanaged(IDictionary<TKey, TValue> data)
4875 {
4976 for ( var i = 0 ; i < _capacity ; i ++ )
5077 {
51- var hash = ( GetHash ( key , _capacity ) + i * i ) % _capacity ;
78+ var hash = ( GetHash ( key , _capacity ) + ( i * i ) ) % _capacity ;
5279
5380 if ( _keyPresence [ hash ] == 0 )
5481 {
@@ -59,11 +86,17 @@ public ImmutableLookupMapUnmanaged(IDictionary<TKey, TValue> data)
5986 }
6087
6188 if ( _keys [ hash ] . Equals ( key ) )
62- throw new InvalidOperationException ( $ "Duplicate key detected: { key } " ) ;
89+ throw new InvalidOperationException ( string . Format ( SharedResources . ErrorDuplicateKey , key ) ) ;
6390 }
6491 }
6592 }
6693
94+ /// <summary>
95+ /// Gets the value associated with the specified key.
96+ /// </summary>
97+ /// <param name="key">The key to lookup.</param>
98+ /// <returns>The value associated with the key.</returns>
99+ /// <exception cref="KeyNotFoundException">Thrown if the key is not found in the map.</exception>
67100 public TValue Get ( TKey key )
68101 {
69102 var hash = GetHash ( key , _capacity ) ;
@@ -79,9 +112,15 @@ public TValue Get(TKey key)
79112 break ;
80113 }
81114
82- throw new KeyNotFoundException ( $ "Key ' { key } ' not found in lookup." ) ;
115+ throw new KeyNotFoundException ( SharedResources . ErrorValueNotFound ) ;
83116 }
84117
118+ /// <summary>
119+ /// Attempts to retrieve the value associated with the specified key.
120+ /// </summary>
121+ /// <param name="key">The key to lookup.</param>
122+ /// <param name="value">When this method returns, contains the value associated with the key, if found; otherwise, the default value.</param>
123+ /// <returns><c>true</c> if the key was found; otherwise, <c>false</c>.</returns>
85124 public bool TryGetValue ( TKey key , out TValue value )
86125 {
87126 var hash = GetHash ( key , _capacity ) ;
@@ -104,6 +143,10 @@ public bool TryGetValue(TKey key, out TValue value)
104143 return false ;
105144 }
106145
146+ /// <inheritdoc />
147+ /// <summary>
148+ /// Returns an enumerator for iterating over the key-value pairs in the map.
149+ /// </summary>
107150 public IEnumerator < KeyValuePair < TKey , TValue > > GetEnumerator ( )
108151 {
109152 for ( var i = 0 ; i < _capacity ; i ++ )
@@ -113,28 +156,55 @@ public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
113156 }
114157 }
115158
159+ /// <inheritdoc />
160+ /// <summary>
161+ /// Returns an enumerator that iterates through a collection.
162+ /// </summary>
163+ /// <returns>
164+ /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
165+ /// </returns>
116166 IEnumerator IEnumerable . GetEnumerator ( ) => GetEnumerator ( ) ;
117167
168+ /// <summary>
169+ /// Releases the unmanaged memory used by the lookup map.
170+ /// </summary>
118171 public void Dispose ( )
119172 {
120173 _keys . Dispose ( ) ;
121174 _values . Dispose ( ) ;
122175 _keyPresence . Dispose ( ) ;
123176 }
124177
178+ /// <summary>
179+ /// Computes the hash code of a key and reduces it to fit the map's capacity.
180+ /// </summary>
181+ /// <param name="key">The key.</param>
182+ /// <param name="capacity">The internal array size.</param>
183+ /// <returns>A non-negative integer hash.</returns>
125184 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
126185 private static int GetHash ( TKey key , int capacity )
127186 {
128187 return Math . Abs ( key . GetHashCode ( ) % capacity ) ;
129188 }
130189
190+ /// <summary>
191+ /// Finds the next prime number greater than or equal to the specified number.
192+ /// </summary>
193+ /// <param name="number">The minimum value.</param>
194+ /// <returns>The next prime number ≥ <paramref name="number"/>.</returns>
131195 private static int FindNextPrime ( int number )
132196 {
133197 while ( ! IsPrime ( number ) )
134198 number ++ ;
135199 return number ;
136200 }
137201
202+ /// <summary>
203+ /// Determines whether a number is prime.
204+ /// Uses precomputed small primes for optimization.
205+ /// </summary>
206+ /// <param name="number">The number to check.</param>
207+ /// <returns><c>true</c> if prime; otherwise, <c>false</c>.</returns>
138208 private static bool IsPrime ( int number )
139209 {
140210 if ( number < 2 ) return false ;
0 commit comments