Skip to content

Commit d5858a9

Browse files
author
Wayfarer
committed
improve immutable lookup
1 parent a0ec77f commit d5858a9

3 files changed

Lines changed: 99 additions & 40 deletions

File tree

-189 Bytes
Loading
-48 Bytes
Loading

ExtendedSystemObjects/ImmutableLookupMapUnmanaged.cs

Lines changed: 99 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/*
22
* COPYRIGHT: See COPYING in the top level directory
3-
* PROJECT: ExtendedSystemObjects
4-
* FILE: ImmutableLookupMap.cs
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>.
3+
* PROJECT: ExtendedSystemObjects
4+
* FILE: ImmutableLookupMap.cs
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>.
77
* PROGRAMMER: Peter Geinitz (Wayfarer)
88
*/
99

@@ -30,8 +30,8 @@ public sealed unsafe class ImmutableLookupMapUnmanaged<TKey, TValue> : IDisposab
3030
/// <summary>
3131
/// Internal entry structure to ensure data locality (Cache-friendly).
3232
/// </summary>
33-
[StructLayout(LayoutKind.Sequential, Pack = 1)]
34-
private struct Entry
33+
[StructLayout(LayoutKind.Sequential)]
34+
internal struct Entry
3535
{
3636
public byte IsPresent;
3737
public TKey Key;
@@ -53,6 +53,11 @@ private struct Entry
5353
/// </summary>
5454
private readonly UnmanagedArray<Entry> _entries;
5555

56+
/// <summary>
57+
/// The disposed
58+
/// </summary>
59+
private bool _disposed;
60+
5661
/// <summary>
5762
/// Initializes a new instance of the <see cref="ImmutableLookupMapUnmanaged{TKey, TValue}" /> class
5863
/// with the specified key-value data.
@@ -117,44 +122,12 @@ public ImmutableLookupMapUnmanaged(IDictionary<TKey, TValue> data)
117122
/// </summary>
118123
public void Dispose()
119124
{
125+
if(_disposed) return;
120126
_entries.Dispose();
127+
_disposed = true;
121128
}
122129

123130
/// <inheritdoc />
124-
/// <summary>
125-
/// Returns an enumerator for iterating over the key-value pairs in the map.
126-
/// </summary>
127-
/// <remarks>
128-
/// Note: We avoid pointers here because yield return cannot exist in an unsafe context
129-
/// that captures pointers. We use the array indexer instead.
130-
/// </remarks>
131-
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
132-
{
133-
for (var i = 0; i < _capacity; i++)
134-
{
135-
// We use the safe indexer of UnmanagedArray.
136-
// This causes a value-copy of the Entry struct, but it's yield-compatible.
137-
var entry = _entries[i];
138-
139-
if (entry.IsPresent != 0)
140-
{
141-
yield return new KeyValuePair<TKey, TValue>(entry.Key, entry.Value);
142-
}
143-
}
144-
}
145-
146-
/// <inheritdoc />
147-
/// <summary>
148-
/// Returns an enumerator that iterates through a collection.
149-
/// </summary>
150-
/// <returns>
151-
/// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
152-
/// </returns>
153-
IEnumerator IEnumerable.GetEnumerator()
154-
{
155-
return GetEnumerator();
156-
}
157-
158131
/// <summary>
159132
/// Gets the value associated with the specified key.
160133
/// </summary>
@@ -224,5 +197,91 @@ private static int GetHash(TKey key)
224197
{
225198
return key.GetHashCode();
226199
}
200+
201+
/// <summary>
202+
/// Gets the enumerator.
203+
/// </summary>
204+
/// <returns>An enumerator for the lookup map.</returns>
205+
public Enumerator GetEnumerator()
206+
{
207+
return new Enumerator(_entries.Pointer, _capacity);
208+
}
209+
210+
/// <inheritdoc />
211+
IEnumerator IEnumerable.GetEnumerator()
212+
{
213+
return GetEnumerator();
214+
}
215+
216+
/// <inheritdoc />
217+
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
218+
{
219+
return GetEnumerator();
220+
}
221+
222+
/// <summary>
223+
/// High performance enumerator for iterating over the entries in the lookup map. Uses unsafe code and pointer arithmetic for maximum speed.
224+
/// </summary>
225+
/// <seealso cref="System.IDisposable" />
226+
/// <seealso cref="System.Collections.Generic.IEnumerable&lt;System.Collections.Generic.KeyValuePair&lt;TKey, TValue&gt;&gt;" />
227+
public unsafe struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>
228+
{
229+
private readonly Entry* _entries;
230+
private readonly int _capacity;
231+
private int _index;
232+
private KeyValuePair<TKey, TValue> _current;
233+
234+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
235+
internal Enumerator(Entry* entries, int capacity)
236+
{
237+
_entries = entries;
238+
_capacity = capacity;
239+
_index = -1;
240+
_current = default;
241+
}
242+
243+
/// <inheritdoc />
244+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
245+
public bool MoveNext()
246+
{
247+
// Nutzt superschnelle Pointer-Arithmetik statt des Array-Indexers
248+
while (++_index < _capacity)
249+
{
250+
Entry* entry = _entries + _index;
251+
if (entry->IsPresent != 0)
252+
{
253+
_current = new KeyValuePair<TKey, TValue>(entry->Key, entry->Value);
254+
return true;
255+
}
256+
}
257+
return false;
258+
}
259+
260+
/// <inheritdoc />
261+
public readonly KeyValuePair<TKey, TValue> Current
262+
{
263+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
264+
get => _current;
265+
}
266+
267+
/// <inheritdoc />
268+
readonly object IEnumerator.Current => Current;
269+
270+
/// <summary>
271+
/// Sets the enumerator to its initial position, which is before the first element in the collection.
272+
/// </summary>
273+
public void Reset()
274+
{
275+
_index = -1;
276+
_current = default;
277+
}
278+
279+
/// <summary>
280+
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
281+
/// </summary>
282+
public readonly void Dispose()
283+
{
284+
}
285+
}
227286
}
228287
}

0 commit comments

Comments
 (0)