12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using Unity.Mathematics;
- using Unity.Jobs;
- using System.Runtime.InteropServices;
- using Unity.Jobs.LowLevel.Unsafe;
-
- namespace Unity.Collections.LowLevel.Unsafe
- {
- [StructLayout(LayoutKind.Sequential)]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- internal unsafe struct HashMapHelper<TKey>
- where TKey : unmanaged, IEquatable<TKey>
- {
- [NativeDisableUnsafePtrRestriction]
- internal byte* Ptr;
-
- [NativeDisableUnsafePtrRestriction]
- internal TKey* Keys;
-
- [NativeDisableUnsafePtrRestriction]
- internal int* Next;
-
- [NativeDisableUnsafePtrRestriction]
- internal int* Buckets;
-
- internal int Count;
- internal int Capacity;
- internal int Log2MinGrowth;
- internal int BucketCapacity;
- internal int AllocatedIndex;
- internal int FirstFreeIdx;
- internal int SizeOfTValue;
- internal AllocatorManager.AllocatorHandle Allocator;
-
- internal const int kMinimumCapacity = 256;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal int CalcCapacityCeilPow2(int capacity)
- {
- capacity = math.max(math.max(1, Count), capacity);
- var newCapacity = math.max(capacity, 1 << Log2MinGrowth);
- var result = math.ceilpow2(newCapacity);
-
- return result;
- }
-
- internal static int GetBucketSize(int capacity)
- {
- return capacity * 2;
- }
-
- internal readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => Ptr != null;
- }
-
- internal readonly bool IsEmpty
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => !IsCreated || Count == 0;
- }
-
- internal void Clear()
- {
- UnsafeUtility.MemSet(Buckets, 0xff, BucketCapacity * sizeof(int));
- UnsafeUtility.MemSet(Next, 0xff, Capacity * sizeof(int));
-
- Count = 0;
- FirstFreeIdx = -1;
- AllocatedIndex = 0;
- }
-
- internal void Init(int capacity, int sizeOfValueT, int minGrowth, AllocatorManager.AllocatorHandle allocator)
- {
- Count = 0;
- Log2MinGrowth = (byte)(32 - math.lzcnt(math.max(1, minGrowth) - 1));
-
- capacity = CalcCapacityCeilPow2(capacity);
- Capacity = capacity;
- BucketCapacity = GetBucketSize(capacity);
- Allocator = allocator;
- SizeOfTValue = sizeOfValueT;
-
- int keyOffset, nextOffset, bucketOffset;
- int totalSize = CalculateDataSize(capacity, BucketCapacity, sizeOfValueT, out keyOffset, out nextOffset, out bucketOffset);
-
- Ptr = (byte*)Memory.Unmanaged.Allocate(totalSize, JobsUtility.CacheLineSize, allocator);
- Keys = (TKey*)(Ptr + keyOffset);
- Next = (int*)(Ptr + nextOffset);
- Buckets = (int*)(Ptr + bucketOffset);
-
- Clear();
- }
-
- internal void Dispose()
- {
- Memory.Unmanaged.Free(Ptr, Allocator);
- Ptr = null;
- Keys = null;
- Next = null;
- Buckets = null;
- Count = 0;
- BucketCapacity = 0;
- }
-
- internal static HashMapHelper<TKey>* Alloc(int capacity, int sizeOfValueT, int minGrowth, AllocatorManager.AllocatorHandle allocator)
- {
- var data = (HashMapHelper<TKey>*)Memory.Unmanaged.Allocate(sizeof(HashMapHelper<TKey>), UnsafeUtility.AlignOf<HashMapHelper<TKey>>(), allocator);
- data->Init(capacity, sizeOfValueT, minGrowth, allocator);
-
- return data;
- }
-
- internal static void Free(HashMapHelper<TKey>* data)
- {
- if (data == null)
- {
- throw new InvalidOperationException("Hash based container has yet to be created or has been destroyed!");
- }
- data->Dispose();
-
- Memory.Unmanaged.Free(data, data->Allocator);
- }
-
- internal void Resize(int newCapacity)
- {
- newCapacity = math.max(newCapacity, Count);
- var newBucketCapacity = math.ceilpow2(GetBucketSize(newCapacity));
-
- if (Capacity == newCapacity && BucketCapacity == newBucketCapacity)
- {
- return;
- }
-
- ResizeExact(newCapacity, newBucketCapacity);
- }
-
- internal void ResizeExact(int newCapacity, int newBucketCapacity)
- {
- int keyOffset, nextOffset, bucketOffset;
- int totalSize = CalculateDataSize(newCapacity, newBucketCapacity, SizeOfTValue, out keyOffset, out nextOffset, out bucketOffset);
-
- var oldPtr = Ptr;
- var oldKeys = Keys;
- var oldNext = Next;
- var oldBuckets = Buckets;
- var oldBucketCapacity = BucketCapacity;
-
- Ptr = (byte*)Memory.Unmanaged.Allocate(totalSize, JobsUtility.CacheLineSize, Allocator);
- Keys = (TKey*)(Ptr + keyOffset);
- Next = (int*)(Ptr + nextOffset);
- Buckets = (int*)(Ptr + bucketOffset);
- Capacity = newCapacity;
- BucketCapacity = newBucketCapacity;
-
- Clear();
-
- for (int i = 0, num = oldBucketCapacity; i < num; ++i)
- {
- for (int idx = oldBuckets[i]; idx != -1; idx = oldNext[idx])
- {
- var newIdx = TryAdd(oldKeys[idx]);
- UnsafeUtility.MemCpy(Ptr + SizeOfTValue * newIdx, oldPtr + SizeOfTValue * idx, SizeOfTValue);
- }
- }
-
- Memory.Unmanaged.Free(oldPtr, Allocator);
- }
-
- internal void TrimExcess()
- {
- var capacity = CalcCapacityCeilPow2(Count);
- ResizeExact(capacity, GetBucketSize(capacity));
- }
-
- internal static int CalculateDataSize(int capacity, int bucketCapacity, int sizeOfTValue, out int outKeyOffset, out int outNextOffset, out int outBucketOffset)
- {
- var sizeOfTKey = sizeof(TKey);
- var sizeOfInt = sizeof(int);
-
- var valuesSize = sizeOfTValue * capacity;
- var keysSize = sizeOfTKey * capacity;
- var nextSize = sizeOfInt * capacity;
- var bucketSize = sizeOfInt * bucketCapacity;
- var totalSize = valuesSize + keysSize + nextSize + bucketSize;
-
- outKeyOffset = 0 + valuesSize;
- outNextOffset = outKeyOffset + keysSize;
- outBucketOffset = outNextOffset + nextSize;
-
- return totalSize;
- }
-
- internal readonly int GetCount()
- {
- if (AllocatedIndex <= 0)
- {
- return 0;
- }
-
- var numFree = 0;
-
- for (var freeIdx = FirstFreeIdx; freeIdx >= 0; freeIdx = Next[freeIdx])
- {
- ++numFree;
- }
-
- return math.min(Capacity, AllocatedIndex) - numFree;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- int GetBucket(in TKey key)
- {
- return (int)((uint)key.GetHashCode() & (BucketCapacity - 1));
- }
-
- internal int TryAdd(in TKey key)
- {
- if (-1 == Find(key))
- {
- // Allocate an entry from the free list
- int idx;
- int* next;
-
- if (AllocatedIndex >= Capacity && FirstFreeIdx < 0)
- {
- int newCap = CalcCapacityCeilPow2(Capacity + (1 << Log2MinGrowth));
- Resize(newCap);
- }
-
- idx = FirstFreeIdx;
-
- if (idx >= 0)
- {
- FirstFreeIdx = Next[idx];
- }
- else
- {
- idx = AllocatedIndex++;
- }
-
- CheckIndexOutOfBounds(idx);
-
- UnsafeUtility.WriteArrayElement(Keys, idx, key);
- var bucket = GetBucket(key);
-
- // Add the index to the hash-map
- next = Next;
- next[idx] = Buckets[bucket];
- Buckets[bucket] = idx;
- Count++;
-
- return idx;
- }
- return -1;
- }
-
- internal int Find(TKey key)
- {
- if (AllocatedIndex > 0)
- {
- // First find the slot based on the hash
- var bucket = GetBucket(key);
- var entryIdx = Buckets[bucket];
-
- if ((uint)entryIdx < (uint)Capacity)
- {
- var nextPtrs = Next;
- while (!UnsafeUtility.ReadArrayElement<TKey>(Keys, entryIdx).Equals(key))
- {
- entryIdx = nextPtrs[entryIdx];
- if ((uint)entryIdx >= (uint)Capacity)
- {
- return -1;
- }
- }
-
- return entryIdx;
- }
- }
-
- return -1;
- }
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- internal bool TryGetValue<TValue>(TKey key, out TValue item)
- where TValue : unmanaged
- {
- var idx = Find(key);
-
- if (-1 != idx)
- {
- item = UnsafeUtility.ReadArrayElement<TValue>(Ptr, idx);
- return true;
- }
-
- item = default;
- return false;
- }
-
- internal int TryRemove(TKey key)
- {
- if (Capacity != 0)
- {
- var removed = 0;
-
- // First find the slot based on the hash
- var bucket = GetBucket(key);
-
- var prevEntry = -1;
- var entryIdx = Buckets[bucket];
-
- while (entryIdx >= 0 && entryIdx < Capacity)
- {
- if (UnsafeUtility.ReadArrayElement<TKey>(Keys, entryIdx).Equals(key))
- {
- ++removed;
-
- // Found matching element, remove it
- if (prevEntry < 0)
- {
- Buckets[bucket] = Next[entryIdx];
- }
- else
- {
- Next[prevEntry] = Next[entryIdx];
- }
-
- // And free the index
- int nextIdx = Next[entryIdx];
- Next[entryIdx] = FirstFreeIdx;
- FirstFreeIdx = entryIdx;
- entryIdx = nextIdx;
-
- break;
- }
- else
- {
- prevEntry = entryIdx;
- entryIdx = Next[entryIdx];
- }
- }
-
- Count -= removed;
- return 0 != removed ? removed : -1;
- }
-
- return -1;
- }
-
- internal bool MoveNextSearch(ref int bucketIndex, ref int nextIndex, out int index)
- {
- for (int i = bucketIndex, num = BucketCapacity; i < num; ++i)
- {
- var idx = Buckets[i];
-
- if (idx != -1)
- {
- index = idx;
- bucketIndex = i + 1;
- nextIndex = Next[idx];
-
- return true;
- }
- }
-
- index = -1;
- bucketIndex = BucketCapacity;
- nextIndex = -1;
- return false;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool MoveNext(ref int bucketIndex, ref int nextIndex, out int index)
- {
- if (nextIndex != -1)
- {
- index = nextIndex;
- nextIndex = Next[nextIndex];
- return true;
- }
-
- return MoveNextSearch(ref bucketIndex, ref nextIndex, out index);
- }
-
- internal NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator)
- {
- var result = CollectionHelper.CreateNativeArray<TKey>(Count, allocator, NativeArrayOptions.UninitializedMemory);
-
- for (int i = 0, count = 0, max = result.Length, capacity = BucketCapacity
- ; i < capacity && count < max
- ; ++i
- )
- {
- int bucket = Buckets[i];
-
- while (bucket != -1)
- {
- result[count++] = UnsafeUtility.ReadArrayElement<TKey>(Keys, bucket);
- bucket = Next[bucket];
- }
- }
-
- return result;
- }
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- internal NativeArray<TValue> GetValueArray<TValue>(AllocatorManager.AllocatorHandle allocator)
- where TValue : unmanaged
- {
- var result = CollectionHelper.CreateNativeArray<TValue>(Count, allocator, NativeArrayOptions.UninitializedMemory);
-
- for (int i = 0, count = 0, max = result.Length, capacity = BucketCapacity
- ; i < capacity && count < max
- ; ++i
- )
- {
- int bucket = Buckets[i];
-
- while (bucket != -1)
- {
- result[count++] = UnsafeUtility.ReadArrayElement<TValue>(Ptr, bucket);
- bucket = Next[bucket];
- }
- }
-
- return result;
- }
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- internal NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays<TValue>(AllocatorManager.AllocatorHandle allocator)
- where TValue : unmanaged
- {
- var result = new NativeKeyValueArrays<TKey, TValue>(Count, allocator, NativeArrayOptions.UninitializedMemory);
-
- for (int i = 0, count = 0, max = result.Length, capacity = BucketCapacity
- ; i < capacity && count < max
- ; ++i
- )
- {
- int bucket = Buckets[i];
-
- while (bucket != -1)
- {
- result.Keys[count] = UnsafeUtility.ReadArrayElement<TKey>(Keys, bucket);
- result.Values[count] = UnsafeUtility.ReadArrayElement<TValue>(Ptr, bucket);
- count++;
- bucket = Next[bucket];
- }
- }
-
- return result;
- }
-
- internal unsafe struct Enumerator
- {
- [NativeDisableUnsafePtrRestriction]
- internal HashMapHelper<TKey>* m_Data;
- internal int m_Index;
- internal int m_BucketIndex;
- internal int m_NextIndex;
-
- internal unsafe Enumerator(HashMapHelper<TKey>* data)
- {
- m_Data = data;
- m_Index = -1;
- m_BucketIndex = 0;
- m_NextIndex = -1;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal bool MoveNext()
- {
- return m_Data->MoveNext(ref m_BucketIndex, ref m_NextIndex, out m_Index);
- }
-
- internal void Reset()
- {
- m_Index = -1;
- m_BucketIndex = 0;
- m_NextIndex = -1;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal KVPair<TKey, TValue> GetCurrent<TValue>()
- where TValue : unmanaged
- {
- return new KVPair<TKey, TValue> { m_Data = m_Data, m_Index = m_Index };
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal TKey GetCurrentKey()
- {
- if (m_Index != -1)
- {
- return m_Data->Keys[m_Index];
- }
-
- return default;
- }
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- void CheckIndexOutOfBounds(int idx)
- {
- if ((uint)idx >= (uint)Capacity)
- {
- throw new InvalidOperationException($"Internal HashMap error. idx {idx}");
- }
- }
- }
-
- /// <summary>
- /// An unordered, expandable associative array.
- /// </summary>
- /// <typeparam name="TKey">The type of the keys.</typeparam>
- /// <typeparam name="TValue">The type of the values.</typeparam>
- [StructLayout(LayoutKind.Sequential)]
- [DebuggerTypeProxy(typeof(UnsafeHashMapDebuggerTypeProxy<,>))]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
- public unsafe struct UnsafeHashMap<TKey, TValue>
- : INativeDisposable
- , IEnumerable<KVPair<TKey, TValue>> // Used by collection initializers.
- where TKey : unmanaged, IEquatable<TKey>
- where TValue : unmanaged
- {
- [NativeDisableUnsafePtrRestriction]
- internal HashMapHelper<TKey> m_Data;
-
- /// <summary>
- /// Initializes and returns an instance of UnsafeHashMap.
- /// </summary>
- /// <param name="initialCapacity">The number of key-value pairs that should fit in the initial allocation.</param>
- /// <param name="allocator">The allocator to use.</param>
- public UnsafeHashMap(int initialCapacity, AllocatorManager.AllocatorHandle allocator)
- {
- m_Data = default;
- m_Data.Init(initialCapacity, sizeof(TValue), HashMapHelper<TKey>.kMinimumCapacity, allocator);
- }
-
- /// <summary>
- /// Releases all resources (memory).
- /// </summary>
- public void Dispose()
- {
- if (!IsCreated)
- {
- return;
- }
-
- m_Data.Dispose();
- }
-
-
- /// <summary>
- /// Creates and schedules a job that will dispose this hash map.
- /// </summary>
- /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param>
- /// <returns>The handle of a new job that will dispose this hash map.</returns>
- public JobHandle Dispose(JobHandle inputDeps)
- {
- if (!IsCreated)
- {
- return inputDeps;
- }
-
- var jobHandle = new UnsafeDisposeJob { Ptr = m_Data.Ptr, Allocator = m_Data.Allocator }.Schedule(inputDeps);
- m_Data = default;
-
- return jobHandle;
- }
-
- /// <summary>
- /// Whether this hash map has been allocated (and not yet deallocated).
- /// </summary>
- /// <value>True if this hash map has been allocated (and not yet deallocated).</value>
- public readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.IsCreated;
- }
-
- /// <summary>
- /// Whether this hash map is empty.
- /// </summary>
- /// <value>True if this hash map is empty or if the map has not been constructed.</value>
- public readonly bool IsEmpty
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.IsEmpty;
- }
-
- /// <summary>
- /// The current number of key-value pairs in this hash map.
- /// </summary>
- /// <returns>The current number of key-value pairs in this hash map.</returns>
- public readonly int Count
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.Count;
- }
-
- /// <summary>
- /// The number of key-value pairs that fit in the current allocation.
- /// </summary>
- /// <value>The number of key-value pairs that fit in the current allocation.</value>
- /// <param name="value">A new capacity. Must be larger than the current capacity.</param>
- public int Capacity
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- readonly get => m_Data.Capacity;
- set => m_Data.Resize(value);
- }
-
- /// <summary>
- /// Removes all key-value pairs.
- /// </summary>
- /// <remarks>Does not change the capacity.</remarks>
- public void Clear()
- {
- m_Data.Clear();
- }
-
- /// <summary>
- /// Adds a new key-value pair.
- /// </summary>
- /// <remarks>If the key is already present, this method returns false without modifying the hash map.</remarks>
- /// <param name="key">The key to add.</param>
- /// <param name="item">The value to add.</param>
- /// <returns>True if the key-value pair was added.</returns>
- public bool TryAdd(TKey key, TValue item)
- {
- var idx = m_Data.TryAdd(key);
- if (-1 != idx)
- {
- UnsafeUtility.WriteArrayElement(m_Data.Ptr, idx, item);
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Adds a new key-value pair.
- /// </summary>
- /// <remarks>If the key is already present, this method throws without modifying the hash map.</remarks>
- /// <param name="key">The key to add.</param>
- /// <param name="item">The value to add.</param>
- /// <exception cref="ArgumentException">Thrown if the key was already present.</exception>
- public void Add(TKey key, TValue item)
- {
- var result = TryAdd(key, item);
-
- if (!result)
- {
- ThrowKeyAlreadyAdded(key);
- }
- }
-
- /// <summary>
- /// Removes a key-value pair.
- /// </summary>
- /// <param name="key">The key to remove.</param>
- /// <returns>True if a key-value pair was removed.</returns>
- public bool Remove(TKey key)
- {
- return -1 != m_Data.TryRemove(key);
- }
-
- /// <summary>
- /// Returns the value associated with a key.
- /// </summary>
- /// <param name="key">The key to look up.</param>
- /// <param name="item">Outputs the value associated with the key. Outputs default if the key was not present.</param>
- /// <returns>True if the key was present.</returns>
- public bool TryGetValue(TKey key, out TValue item)
- {
- return m_Data.TryGetValue(key, out item);
- }
-
- /// <summary>
- /// Returns true if a given key is present in this hash map.
- /// </summary>
- /// <param name="key">The key to look up.</param>
- /// <returns>True if the key was present.</returns>
- public bool ContainsKey(TKey key)
- {
- return -1 != m_Data.Find(key);
- }
-
- /// <summary>
- /// Sets the capacity to match what it would be if it had been originally initialized with all its entries.
- /// </summary>
- public void TrimExcess() => m_Data.TrimExcess();
-
- /// <summary>
- /// Gets and sets values by key.
- /// </summary>
- /// <remarks>Getting a key that is not present will throw. Setting a key that is not already present will add the key.</remarks>
- /// <param name="key">The key to look up.</param>
- /// <value>The value associated with the key.</value>
- /// <exception cref="ArgumentException">For getting, thrown if the key was not present.</exception>
- public TValue this[TKey key]
- {
- get
- {
- TValue result;
- if (!m_Data.TryGetValue(key, out result))
- {
- ThrowKeyNotPresent(key);
- }
-
- return result;
- }
-
- set
- {
- var idx = m_Data.Find(key);
- if (-1 != idx)
- {
- UnsafeUtility.WriteArrayElement(m_Data.Ptr, idx, value);
- return;
- }
-
- TryAdd(key, value);
- }
- }
-
- /// <summary>
- /// Returns an array with a copy of all this hash map's keys (in no particular order).
- /// </summary>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>An array with a copy of all this hash map's keys (in no particular order).</returns>
- public NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator) => m_Data.GetKeyArray(allocator);
-
- /// <summary>
- /// Returns an array with a copy of all this hash map's values (in no particular order).
- /// </summary>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>An array with a copy of all this hash map's values (in no particular order).</returns>
- public NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator) => m_Data.GetValueArray<TValue>(allocator);
-
- /// <summary>
- /// Returns a NativeKeyValueArrays with a copy of all this hash map's keys and values.
- /// </summary>
- /// <remarks>The key-value pairs are copied in no particular order. For all `i`, `Values[i]` will be the value associated with `Keys[i]`.</remarks>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>A NativeKeyValueArrays with a copy of all this hash map's keys and values.</returns>
- public NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator) => m_Data.GetKeyValueArrays<TValue>(allocator);
-
- /// <summary>
- /// Returns an enumerator over the key-value pairs of this hash map.
- /// </summary>
- /// <returns>An enumerator over the key-value pairs of this hash map.</returns>
- public Enumerator GetEnumerator()
- {
- // return new Enumerator { Data = m_Data, Index = -1 };
- fixed (HashMapHelper<TKey>* data = &m_Data)
- {
- return new Enumerator { m_Enumerator = new HashMapHelper<TKey>.Enumerator(data) };
- }
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator<KVPair<TKey, TValue>> IEnumerable<KVPair<TKey, TValue>>.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator IEnumerable.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// An enumerator over the key-value pairs of a container.
- /// </summary>
- /// <remarks>
- /// In an enumerator's initial state, <see cref="Current"/> is not valid to read.
- /// From this state, the first <see cref="MoveNext"/> call advances the enumerator to the first key-value pair.
- /// </remarks>
- public struct Enumerator : IEnumerator<KVPair<TKey, TValue>>
- {
- internal HashMapHelper<TKey>.Enumerator m_Enumerator;
-
- /// <summary>
- /// Does nothing.
- /// </summary>
- public void Dispose() { }
-
- /// <summary>
- /// Advances the enumerator to the next key-value pair.
- /// </summary>
- /// <returns>True if <see cref="Current"/> is valid to read after the call.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool MoveNext() => m_Enumerator.MoveNext();
-
- /// <summary>
- /// Resets the enumerator to its initial state.
- /// </summary>
- public void Reset() => m_Enumerator.Reset();
-
- /// <summary>
- /// The current key-value pair.
- /// </summary>
- /// <value>The current key-value pair.</value>
- public KVPair<TKey, TValue> Current
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Enumerator.GetCurrent<TValue>();
- }
-
- /// <summary>
- /// Gets the element at the current position of the enumerator in the container.
- /// </summary>
- object IEnumerator.Current => Current;
- }
-
- /// <summary>
- /// Returns a readonly version of this UnsafeHashMap instance.
- /// </summary>
- /// <remarks>ReadOnly containers point to the same underlying data as the UnsafeHashMap it is made from.</remarks>
- /// <returns>ReadOnly instance for this.</returns>
- public ReadOnly AsReadOnly()
- {
- return new ReadOnly(ref m_Data);
- }
-
- /// <summary>
- /// A read-only alias for the value of a UnsafeHashMap. Does not have its own allocated storage.
- /// </summary>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
- public struct ReadOnly
- : IEnumerable<KVPair<TKey, TValue>>
- {
- [NativeDisableUnsafePtrRestriction]
- internal HashMapHelper<TKey> m_Data;
-
- internal ReadOnly(ref HashMapHelper<TKey> data)
- {
- m_Data = data;
- }
-
- /// <summary>
- /// Whether this hash map has been allocated (and not yet deallocated).
- /// </summary>
- /// <value>True if this hash map has been allocated (and not yet deallocated).</value>
- public readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.IsCreated;
- }
-
- /// <summary>
- /// Whether this hash map is empty.
- /// </summary>
- /// <value>True if this hash map is empty or if the map has not been constructed.</value>
- public readonly bool IsEmpty
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.IsEmpty;
- }
-
- /// <summary>
- /// The current number of key-value pairs in this hash map.
- /// </summary>
- /// <returns>The current number of key-value pairs in this hash map.</returns>
- public readonly int Count
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.Count;
- }
-
- /// <summary>
- /// The number of key-value pairs that fit in the current allocation.
- /// </summary>
- /// <value>The number of key-value pairs that fit in the current allocation.</value>
- public readonly int Capacity
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Data.Capacity;
- }
-
- /// <summary>
- /// Returns the value associated with a key.
- /// </summary>
- /// <param name="key">The key to look up.</param>
- /// <param name="item">Outputs the value associated with the key. Outputs default if the key was not present.</param>
- /// <returns>True if the key was present.</returns>
- public readonly bool TryGetValue(TKey key, out TValue item) => m_Data.TryGetValue(key, out item);
-
- /// <summary>
- /// Returns true if a given key is present in this hash map.
- /// </summary>
- /// <param name="key">The key to look up.</param>
- /// <returns>True if the key was present.</returns>
- public readonly bool ContainsKey(TKey key)
- {
- return -1 != m_Data.Find(key);
- }
-
- /// <summary>
- /// Gets values by key.
- /// </summary>
- /// <remarks>Getting a key that is not present will throw.</remarks>
- /// <param name="key">The key to look up.</param>
- /// <value>The value associated with the key.</value>
- /// <exception cref="ArgumentException">For getting, thrown if the key was not present.</exception>
- public readonly TValue this[TKey key]
- {
- get
- {
- TValue result;
- m_Data.TryGetValue(key, out result);
- return result;
- }
- }
-
- /// <summary>
- /// Returns an array with a copy of all this hash map's keys (in no particular order).
- /// </summary>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>An array with a copy of all this hash map's keys (in no particular order).</returns>
- public readonly NativeArray<TKey> GetKeyArray(AllocatorManager.AllocatorHandle allocator) => m_Data.GetKeyArray(allocator);
-
- /// <summary>
- /// Returns an array with a copy of all this hash map's values (in no particular order).
- /// </summary>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>An array with a copy of all this hash map's values (in no particular order).</returns>
- public readonly NativeArray<TValue> GetValueArray(AllocatorManager.AllocatorHandle allocator) => m_Data.GetValueArray<TValue>(allocator);
-
- /// <summary>
- /// Returns a NativeKeyValueArrays with a copy of all this hash map's keys and values.
- /// </summary>
- /// <remarks>The key-value pairs are copied in no particular order. For all `i`, `Values[i]` will be the value associated with `Keys[i]`.</remarks>
- /// <param name="allocator">The allocator to use.</param>
- /// <returns>A NativeKeyValueArrays with a copy of all this hash map's keys and values.</returns>
- public readonly NativeKeyValueArrays<TKey, TValue> GetKeyValueArrays(AllocatorManager.AllocatorHandle allocator) => m_Data.GetKeyValueArrays<TValue>(allocator);
-
- /// <summary>
- /// Returns an enumerator over the key-value pairs of this hash map.
- /// </summary>
- /// <returns>An enumerator over the key-value pairs of this hash map.</returns>
- public readonly Enumerator GetEnumerator()
- {
- fixed (HashMapHelper<TKey>* data = &m_Data)
- {
- return new Enumerator { m_Enumerator = new HashMapHelper<TKey>.Enumerator(data) };
- }
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator<KVPair<TKey, TValue>> IEnumerable<KVPair<TKey, TValue>>.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator IEnumerable.GetEnumerator()
- {
- throw new NotImplementedException();
- }
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void ThrowKeyNotPresent(TKey key)
- {
- throw new ArgumentException($"Key: {key} is not present.");
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void ThrowKeyAlreadyAdded(TKey key)
- {
- throw new ArgumentException($"An item with the same key has already been added: {key}");
- }
- }
-
- internal sealed class UnsafeHashMapDebuggerTypeProxy<TKey, TValue>
- where TKey : unmanaged, IEquatable<TKey>
- where TValue : unmanaged
- {
- HashMapHelper<TKey> Data;
-
- public UnsafeHashMapDebuggerTypeProxy(UnsafeHashMap<TKey, TValue> target)
- {
- Data = target.m_Data;
- }
-
- public UnsafeHashMapDebuggerTypeProxy(UnsafeHashMap<TKey, TValue>.ReadOnly target)
- {
- Data = target.m_Data;
- }
-
- public List<Pair<TKey, TValue>> Items
- {
- get
- {
- var result = new List<Pair<TKey, TValue>>();
- using (var kva = Data.GetKeyValueArrays<TValue>(Allocator.Temp))
- {
- for (var i = 0; i < kva.Length; ++i)
- {
- result.Add(new Pair<TKey, TValue>(kva.Keys[i], kva.Values[i]));
- }
- }
- return result;
- }
- }
- }
- }
|