public static void Free(UnsafeHashCollection *collection)
        {
            Assert.Check(collection->Entries.Dynamic);

            AllocHelper.Free(collection->Buckets);
            AllocHelper.Free(collection->Entries.Ptr);
        }
        public static void Free(UnsafeHashCollection *collection)
        {
            Assert.Check(collection->Entries.Dynamic == 1);

            Native.Free(collection->Buckets);
            Native.Free(collection->Entries.Ptr);
        }
            public Iterator(UnsafeHashCollection *collection)
            {
                _index = -1;

                //
                Current    = null;
                Collection = collection;
            }
        public static Entry *Insert <T>(UnsafeHashCollection *collection, T value, int valueHash) where T : unmanaged
        {
            Entry *entry;

            if (collection->FreeHead != null)
            {
                Assert.Check(collection->FreeCount > 0);

                // entry we're adding
                entry = collection->FreeHead;

                // update free list
                collection->FreeHead  = entry->Next;
                collection->FreeCount = collection->FreeCount - 1;

                // this HAS to be a FREE state entry, or something is seriously wrong
                Assert.Check(entry->State == EntryState.Free);
            }
            else
            {
                if (collection->UsedCount == collection->Entries.Length)
                {
                    // !! IMPORTANT !!
                    // when this happens, it's very important to be
                    // aware of the fact that all pointers to to buckets
                    // or entries etc. are not valid anymore as we have
                    // re-allocated all of the memory
                    Expand(collection);
                }

                // grab 'next' element maintained by _count
                entry = (Entry *)UnsafeBuffer.Element(collection->Entries.Ptr, collection->UsedCount, collection->Entries.Stride);

                // step up used count
                collection->UsedCount = collection->UsedCount + 1;

                // this HAS to be a NONE state entry, or something is seriously wrong
                Assert.Check(entry->State == EntryState.None);
            }

            // compute bucket hash
            var bucketHash = valueHash % collection->Entries.Length;

            // hook up entry
            entry->Hash  = valueHash;
            entry->Next  = collection->Buckets[bucketHash];
            entry->State = EntryState.Used;

            // store value
            *(T *)((byte *)entry + collection->KeyOffset) = value;

            // store as head on bucket
            collection->Buckets[bucketHash] = entry;

            // done!
            return(entry);
        }
        public static void Clear(UnsafeHashCollection *collection)
        {
            collection->FreeCount = 0;
            collection->UsedCount = 0;

            var length = collection->Entries.Length;

            AllocHelper.MemClear(collection->Buckets, length * sizeof(Entry * *));
            UnsafeBuffer.Clear(&collection->Entries);
        }
        public static void Clear(UnsafeHashCollection *collection)
        {
            collection->FreeHead  = null;
            collection->FreeCount = 0;
            collection->UsedCount = 0;

            var length = collection->Entries.Length;

            Memory.ZeroMem(collection->Buckets, length * sizeof(Entry * *));
            UnsafeBuffer.Clear(&collection->Entries);
        }
        static void Expand(UnsafeHashCollection *collection)
        {
            UDebug.Assert(collection->Entries.Dynamic == 1);

            // We need to grab the pext prime, so at least current length + 1
            var capacity = GetNextPrime(collection->Entries.Length + 1);

            UDebug.Assert(capacity >= collection->Entries.Length);

            var newBuckets = (Entry **)Memory.MallocAndZero(capacity * sizeof(Entry * *), sizeof(Entry * *));
            var newEntries = default(UnsafeBuffer);

            UnsafeBuffer.InitDynamic(&newEntries, capacity, collection->Entries.Stride);
            UnsafeBuffer.Copy(collection->Entries, 0, newEntries, 0, collection->Entries.Length);

            collection->FreeHead  = null;
            collection->FreeCount = 0;

            for (int i = collection->Entries.Length - 1; i >= 0; --i)
            {
                var entry = (Entry *)((byte *)newEntries.Ptr + (i * newEntries.Stride));
                if (entry->State == EntryState.Used)
                {
                    var bucketHash = entry->Hash % capacity;

                    // assign current entry in buckets as next
                    entry->Next = newBuckets[bucketHash];

                    // assign entry as new bucket head
                    newBuckets[bucketHash] = entry;
                }

                // entry is in free list
                else if (entry->State == EntryState.Free)
                {
                    // assign free list as next
                    entry->Next = collection->FreeHead;

                    // re-assign free list to entry
                    collection->FreeHead  = entry;
                    collection->FreeCount = collection->FreeCount + 1;
                }
            }

            // free old memory
            Memory.Free(collection->Buckets);
            UnsafeBuffer.Free(&collection->Entries);

            // new storage
            collection->Buckets = newBuckets;
            collection->Entries = newEntries;
        }
        private static void Expand(UnsafeHashCollection *collection)
        {
            Assert.Check(collection->Entries.Dynamic);

            var capacity = GetNextPrime(collection->Entries.Length);

            Assert.Check(capacity >= collection->Entries.Length);

            var newBuckets = (Entry **)AllocHelper.MallocAndClear(capacity * sizeof(Entry * *), sizeof(Entry * *));
            var newEntries = default(UnsafeBuffer);

            UnsafeBuffer.InitDynamic(&newEntries, capacity, collection->Entries.Stride);
            UnsafeBuffer.Copy(collection->Entries, 0, newEntries, 0, collection->Entries.Length);

            collection->FreeHead  = null;
            collection->FreeCount = 0;

            for (var i = collection->Entries.Length - 1; i >= 0; --i)
            {
                var entry = (Entry *)((byte *)newEntries.Ptr + i * newEntries.Stride);
                if (entry->State == EntryState.Used)
                {
                    var bucketHash = entry->Hash % capacity;

                    // assign current entry in buckets as next
                    entry->Next = newBuckets[bucketHash];

                    // assign entry as new bucket head
                    newBuckets[bucketHash] = entry;
                }

                // entry is in free list
                else if (entry->State == EntryState.Free)
                {
                    // assign free list as next
                    entry->Next = collection->FreeHead;

                    // re-assign free list to entry
                    collection->FreeHead  = entry;
                    collection->FreeCount = collection->FreeCount + 1;
                }
            }

            // free old memory
            AllocHelper.Free(collection->Buckets);
            UnsafeBuffer.Free(&collection->Entries);

            // new storage
            collection->Buckets = newBuckets;
            collection->Entries = newEntries;
        }
        public static void Free(UnsafeHashCollection *collection)
        {
            if (collection == null)
            {
                return;
            }

            UDebug.Assert(collection->Entries.Dynamic == 1);

            Memory.Free(collection->Buckets);
            Memory.Free(collection->Entries.Ptr);

            *collection = default;
        }
        public static Entry *Find <T>(UnsafeHashCollection *collection, T value, int valueHash) where T : unmanaged, IEquatable <T>
        {
            var bucketHead = collection->Buckets[valueHash % collection->Entries.Length];

            while (bucketHead != null)
            {
                if (bucketHead->Hash == valueHash && value.Equals(*(T *)((byte *)bucketHead + collection->KeyOffset)))
                {
                    return(bucketHead);
                }
                else
                {
                    bucketHead = bucketHead->Next;
                }
            }

            return(null);
        }
        public static bool Remove <T>(UnsafeHashCollection *collection, T value, int valueHash) where T : unmanaged, IEquatable <T>
        {
            var bucketHash = valueHash % collection->Entries.Length;
            var bucketHead = collection->Buckets[valueHash % collection->Entries.Length];
            var bucketPrev = default(Entry *);

            while (bucketHead != null)
            {
                if (bucketHead->Hash == valueHash && value.Equals(*(T *)((byte *)bucketHead + collection->KeyOffset)))
                {
                    // if previous was null, this means we're at the head of the list
                    if (bucketPrev == null)
                    {
                        collection->Buckets[bucketHash] = bucketHead->Next;
                    }

                    // previous was not null, it means we're in the middle
                    // of the list so stitch the elements together
                    else
                    {
                        bucketPrev->Next = bucketHead->Next;
                    }

                    Assert.Check(bucketHead->State == EntryState.Used);

                    // next for the node we removed becomes current free list head
                    bucketHead->Next  = collection->FreeHead;
                    bucketHead->State = EntryState.Free;

                    // set it as free list head, and increment count
                    collection->FreeHead  = bucketHead;
                    collection->FreeCount = collection->FreeCount + 1;
                    return(true);
                }
                else
                {
                    bucketPrev = bucketHead;
                    bucketHead = bucketHead->Next;
                }
            }

            return(false);
        }
 public static T GetKey <T>(UnsafeHashCollection *collection, Entry *entry) where T : unmanaged
 {
     return(*(T *)((byte *)entry + collection->KeyOffset));
 }
 public static Entry *GetEntry(UnsafeHashCollection *collection, int index)
 {
     return((Entry *)UnsafeBuffer.Element(collection->Entries.Ptr, index, collection->Entries.Stride));
 }
        public static Entry *Insert <T>(UnsafeHashCollection *collection, T key, int valueHash) where T : unmanaged
        {
            Entry *entry;

            if (collection->FreeHead != null)
            {
                UDebug.Assert(collection->FreeCount > 0);

                // entry we're adding
                entry = collection->FreeHead;

                // update free list
                collection->FreeHead  = entry->Next;
                collection->FreeCount = collection->FreeCount - 1;

                // this HAS to be a FREE state entry, or something is seriously wrong
                UDebug.Assert(entry->State == EntryState.Free);
            }
            else
            {
                if (collection->UsedCount == collection->Entries.Length)
                {
                    // Cannot expand fixed-size HashCollection
                    if (collection->Entries.Dynamic == 0)
                    {
                        throw new InvalidOperationException(ThrowHelper.InvalidOperation_CollectionFull);
                    }

                    // !! IMPORTANT !!
                    // when this happens, it's very important to be
                    // aware of the fact that all pointers to to buckets
                    // or entries etc. are not valid anymore as we have
                    // re-allocated all of the memory
                    Expand(collection);
                }

                // grab 'next' element maintained by _count
                entry = collection->Entries.Element <Entry>(collection->UsedCount);

                // step up used count
                collection->UsedCount = collection->UsedCount + 1;

                // this HAS to be a NONE state entry, or something is seriously wrong
                UDebug.Assert(entry->State == EntryState.None);
            }

            // compute bucket hash
            var bucketHash = valueHash % collection->Entries.Length;

            // hook up entry
            entry->Hash  = valueHash;
            entry->Next  = collection->Buckets[bucketHash];
            entry->State = EntryState.Used;

            // store value
            *(T *)((byte *)entry + collection->KeyOffset) = key;

            // store as head on bucket
            collection->Buckets[bucketHash] = entry;

            // done!
            return(entry);
        }
 public static Entry *GetEntry(UnsafeHashCollection *collection, int index)
 {
     return(collection->Entries.Element <Entry>(index));
 }