using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Utilities; using System.Collections; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.LowLevel.Collections; public unsafe struct UnsafeHashMap : IUnsafeHashCollection> where TKey : unmanaged, IEquatable where TValue : unmanaged { public struct Enumerator : IEnumerator> { internal HashMapHelper.Enumerator _enumerator; public KeyValuePair Current => _enumerator.GetCurrent(); object IEnumerator.Current => Current; public Enumerator(HashMapHelper* data) { _enumerator = new HashMapHelper.Enumerator(data); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() => _enumerator.MoveNext(); public void Reset() => _enumerator.Reset(); public void Dispose() { } } private HashMapHelper _helper; public readonly int Count => _helper.Count; public readonly int Capacity => _helper.Capacity; public readonly bool IsCreated => _helper.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 (!_helper.TryGetValue(key, out var result)) { throw new ArgumentException($"Key: {key} is not present."); } return result; } set { var idx = _helper.Find(key); if (-1 != idx) { UnsafeUtility.WriteArrayElement(_helper.Buffer, idx, value); return; } TryAdd(key, value); } } public Enumerator GetEnumerator() => new((HashMapHelper*)UnsafeUtility.AddressOf(ref this)); IEnumerator> IEnumerable>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Invalid constructor, use or instead. /// public UnsafeHashMap() : this(0, Allocator.Invalid) { } public UnsafeHashMap(int capacity, AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) { _helper = new HashMapHelper(capacity, sizeof(TValue), (int)AlignOf(), HashMapHelper.MINIMAL_CAPACITY, handle, allocationOption); } public UnsafeHashMap(int capacity, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) : this(capacity, 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(in TKey key, TValue item) { var idx = _helper.TryAdd(key); if (idx != -1) { UnsafeUtility.WriteArrayElement(_helper.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(in 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(in TKey key) { return -1 != _helper.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(in TKey key, out TValue item) { return _helper.TryGetValue(key, out item); } /// /// Retrieves the value associated with the specified key, or returns a default value if the key is not found. /// /// The key whose value to retrieve. /// The value to return if the specified key does not exist. If not specified, the default value for the type is used. /// The value associated with the specified key if the key is found; otherwise, the specified default value. public TValue GetValueOrDefault(in TKey key, TValue defaultValue = default) { if (_helper.TryGetValue(key, out var value)) { return value; } return defaultValue; } public ref TValue GetValueRef(in TKey key, out bool exists) { return ref _helper.GetValueRef(key, out exists); } /// /// 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(in TKey key) { return -1 != _helper.Find(key); } /// /// Sets the capacity to match what it would be if it had been originally initialized with all its entries. /// public void TrimExcess() => _helper.TrimExcess(); public void Resize(int newSize, AllocationOption option = AllocationOption.None) { _helper.Resize(newSize); } public void Clear() { _helper.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) => _helper.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) => _helper.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) => _helper.GetKeyValueArrays(allocator); [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void* GetUnsafePtr() { return _helper.Buffer; } public void Dispose() { _helper.Dispose(); } }