using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Contracts; using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.LowLevel.Collections; public unsafe struct UnsafeHashMap : IUnsafeCollection> where TKey : unmanaged, IEquatable where TValue : unmanaged { public struct Enumerator : IEnumerator> { internal HashMapHelper.Enumerator _enumerator; public Enumerator(HashMapHelper* data) { _enumerator = new HashMapHelper.Enumerator(data); } /// /// The current key-value pair. /// /// The current key-value pair. public KeyValuePair Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _enumerator.GetCurrent(); } /// /// Gets the element at the current position of the enumerator in the container. /// object IEnumerator.Current => Current; /// /// Advances the enumerator to the next key-value pair. /// /// True if is valid to read after the call. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => _enumerator.MoveNext(); /// /// Resets the enumerator to its initial state. /// public void Reset() => _enumerator.Reset(); /// /// Does nothing. /// public void Dispose() { } } private HashMapHelper _hashMap; public readonly int Count => _hashMap.Count; public readonly int Capacity => _hashMap.Capacity; public readonly bool IsCreated => _hashMap.IsCreated; /// /// Gets and sets values by key. /// /// Getting a key that is not present will throw. Setting a key that is not already present will add the key. /// The key to look up. /// The value associated with the key. /// For getting, thrown if the key was not present. public TValue this[TKey key] { get { if (!_hashMap.TryGetValue(key, out var result)) { throw new ArgumentException($"Key: {key} is not present."); } return result; } set { var idx = _hashMap.Find(key); if (-1 != idx) { UnsafeUtilities.WriteArrayElement(_hashMap.Buffer, idx, value); return; } TryAdd(key, value); } } public IEnumerator> GetEnumerator() => new Enumerator((HashMapHelper*)UnsafeUtilities.AddressOf(ref _hashMap)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public UnsafeHashMap(int capacity, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) { _hashMap = new HashMapHelper(capacity, sizeof(TValue), HashMapHelper.MINIMAL_CAPACITY, ref handle, allocationOption); } public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) : this(capacity, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) { } /// /// Adds a new key-value pair. /// /// If the key is already present, this method returns false without modifying the hash map. /// The key to add. /// The value to add. /// True if the key-value pair was added. public bool TryAdd(TKey key, TValue item) { var idx = _hashMap.TryAdd(key); if (idx != -1) { UnsafeUtilities.WriteArrayElement(_hashMap.Buffer, idx, item); return true; } return false; } /// /// Adds a new key-value pair. /// /// If the key is already present, this method throws without modifying the hash map. /// The key to add. /// The value to add. /// Thrown if the key was already present. public void Add(TKey key, TValue item) { var result = TryAdd(key, item); if (!result) { throw new ArgumentException($"An item with the same key has already been added: {key}"); } } /// /// Removes a particular key and its value. /// /// The value to remove. /// True if the value was present. public bool Remove(TKey key) { return -1 != _hashMap.TryRemove(key); } /// /// Returns the value associated with a key. /// /// The key to look up. /// Outputs the value associated with the key. Outputs default if the key was not present. /// True if the key was present. public bool TryGetValue(TKey key, out TValue item) { return _hashMap.TryGetValue(key, out item); } /// /// Returns true if a given key is present in this hash map. /// /// The key to look up. /// True if the key was present. public bool ContainsKey(TKey key) { return -1 != _hashMap.Find(key); } /// /// Sets the capacity to match what it would be if it had been originally initialized with all its entries. /// public void TrimExcess() => _hashMap.TrimExcess(); public void Resize(int newSize) { _hashMap.Resize(newSize); } public void Clear() { _hashMap.Clear(); } /// /// Retrieves an array of keys from the hash map. /// /// An array containing the keys stored in the hash map. public UnsafeArray GetKeyArray(Allocator allocator) => _hashMap.GetKeyArray(allocator); /// /// Retrieves an array of values from the underlying hash map. /// /// An UnsafeArray containing the values stored in the hash map. public UnsafeArray GetValueArray(Allocator allocator) => _hashMap.GetValueArray(allocator); /// /// Retrieves an array of key-value pairs from the hash map. The keys are of type TKey and the values are of type /// TValue. /// /// Returns an UnsafeArray containing KeyValuePair objects. public UnsafeArray> GetKeyValueArrays(Allocator allocator) => _hashMap.GetKeyValueArrays(allocator); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void* GetUnsafePtr() { return _hashMap.Buffer; } public void Dispose() { _hashMap.Dispose(); } public void Test(ref HashMapHelper t) { Console.WriteLine(t.Equals(_hashMap)); } }