Example #1
0
        public static unsafe bool TryGetNextValueAtomic(NativeHashMapData *data, out TValue item,
                                                        ref NativeMultiHashMapIterator <TKey> it)
        {
            int entryIdx = it.NextEntryIndex;

            it.NextEntryIndex = -1;
            it.EntryIndex     = -1;
            item = default(TValue);
            if (entryIdx < 0 || entryIdx >= data->capacity)
            {
                return(false);
            }
            int *nextPtrs = (int *)data->next;

            while (!UnsafeUtility.ReadArrayElement <TKey>(data->keys, entryIdx).Equals(it.key))
            {
                entryIdx = nextPtrs[entryIdx];
                if (entryIdx < 0 || entryIdx >= data->capacity)
                {
                    return(false);
                }
            }
            it.NextEntryIndex = nextPtrs[entryIdx];
            it.EntryIndex     = entryIdx;

            // Read the value
            item = UnsafeUtility.ReadArrayElement <TValue>(data->values, entryIdx);

            return(true);
        }
        public static void AllocateHashMap <TKey, TValue>(int length, int bucketLength, Allocator label, out NativeHashMapData *outBuf)
            where TKey : struct
            where TValue : struct
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (!UnsafeUtility.IsBlittable <TKey>())
            {
                throw new ArgumentException(string.Format("{0} used in NativeHashMap<{0},{1}> must be blittable", typeof(TKey), typeof(TValue)));
            }
            if (!UnsafeUtility.IsBlittable <TKey>())
            {
                throw new ArgumentException(string.Format("{1} used in NativeHashMap<{0},{1}> must be blittable", typeof(TKey), typeof(TValue)));
            }
#endif

            NativeHashMapData *data = (NativeHashMapData *)UnsafeUtility.Malloc(sizeof(NativeHashMapData), UnsafeUtility.AlignOf <NativeHashMapData>(), label);

            bucketLength = math.ceil_pow2(bucketLength);

            data->capacity           = length;
            data->bucketCapacityMask = bucketLength - 1;

            int keyOffset, nextOffset, bucketOffset;
            int totalSize = CalculateDataSize <TKey, TValue>(length, bucketLength, out keyOffset, out nextOffset, out bucketOffset);

            data->values  = (byte *)UnsafeUtility.Malloc(totalSize, JobsUtility.CacheLineSize, label);
            data->keys    = data->values + keyOffset;
            data->next    = data->values + nextOffset;
            data->buckets = data->values + bucketOffset;

            outBuf = data;
        }
Example #3
0
        public static unsafe void Remove(NativeHashMapData *data, NativeMultiHashMapIterator <TKey> it)
        {
            // First find the slot based on the hash
            int *buckets  = (int *)data->buckets;
            int *nextPtrs = (int *)data->next;
            int  bucket   = it.key.GetHashCode() & data->bucketCapacityMask;

            int entryIdx = buckets[bucket];

            if (entryIdx == it.EntryIndex)
            {
                buckets[bucket] = nextPtrs[entryIdx];
            }
            else
            {
                while (entryIdx >= 0 && nextPtrs[entryIdx] != it.EntryIndex)
                {
                    entryIdx = nextPtrs[entryIdx];
                }
                if (entryIdx < 0)
                {
                    throw new InvalidOperationException("Invalid iterator passed to HashMap remove");
                }
                nextPtrs[entryIdx] = nextPtrs[it.EntryIndex];
            }
            // And free the index
            nextPtrs[it.EntryIndex] = data->firstFreeTLS[0];
            data->firstFreeTLS[0]   = it.EntryIndex;
        }
Example #4
0
        static unsafe public bool TryAddAtomicMulti(NativeHashMapData *data, TKey key, TValue item, int threadIndex)
        {
            // Allocate an entry from the free list
            int idx = AllocEntry(data, threadIndex);

            // Write the new value to the entry
            UnsafeUtility.WriteArrayElement(data->keys, idx, key);
            UnsafeUtility.WriteArrayElement(data->values, idx, item);

            int bucket = key.GetHashCode() & data->bucketCapacityMask;
            // Add the index to the hash-map
            int *buckets = (int *)data->buckets;

            int  nextPtr;
            int *nextPtrs = (int *)data->next;

            do
            {
                nextPtr       = buckets[bucket];
                nextPtrs[idx] = nextPtr;
            }while (Interlocked.CompareExchange(ref buckets[bucket], idx, nextPtr) != nextPtr);


            return(true);
        }
Example #5
0
 public static void DeallocateHashMap(NativeHashMapData *data, Allocator allocation)
 {
     UnsafeUtility.Free(data->values, allocation);
     data->values  = null;
     data->keys    = null;
     data->next    = null;
     data->buckets = null;
     UnsafeUtility.Free(data, allocation);
 }
        public void Dispose()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
#endif

            NativeHashMapData.DeallocateHashMap(m_Buffer, m_AllocatorLabel);
            m_Buffer = null;
        }
        public static unsafe bool SetValue(NativeHashMapData *data, ref NativeMultiHashMapIterator <TKey> it, ref TValue item)
        {
            int entryIdx = it.EntryIndex;

            if (entryIdx < 0 || entryIdx >= data->capacity)
            {
                return(false);
            }

            UnsafeUtility.WriteArrayElement(data->values, entryIdx, item);
            return(true);
        }
        public static unsafe bool TryGetFirstValueAtomic(NativeHashMapData *data, TKey key, out TValue item, out NativeMultiHashMapIterator <TKey> it)
        {
            it.key = key;
            if (data->allocatedIndexLength <= 0)
            {
                it.EntryIndex = it.NextEntryIndex = -1;
                item          = default(TValue);
                return(false);
            }
            // First find the slot based on the hash
            int *buckets = (int *)data->buckets;
            int  bucket  = key.GetHashCode() & data->bucketCapacityMask;

            it.EntryIndex = it.NextEntryIndex = buckets[bucket];
            return(TryGetNextValueAtomic(data, out item, ref it));
        }
Example #9
0
        public static unsafe bool TryAddAtomic(NativeHashMapData *data, TKey key, TValue item, int threadIndex)
        {
            TValue tempItem;
            NativeMultiHashMapIterator <TKey> tempIt;

            if (TryGetFirstValueAtomic(data, key, out tempItem, out tempIt))
            {
                return(false);
            }
            // Allocate an entry from the free list
            int idx = AllocEntry(data, threadIndex);

            // Write the new value to the entry
            UnsafeUtility.WriteArrayElement(data->keys, idx, key);
            UnsafeUtility.WriteArrayElement(data->values, idx, item);

            int bucket = key.GetHashCode() & data->bucketCapacityMask;
            // Add the index to the hash-map
            int *buckets = (int *)data->buckets;

            if (Interlocked.CompareExchange(ref buckets[bucket], idx, -1) != -1)
            {
                int *nextPtrs = (int *)data->next;
                do
                {
                    nextPtrs[idx] = buckets[bucket];
                    if (TryGetFirstValueAtomic(data, key, out tempItem, out tempIt))
                    {
                        // Put back the entry in the free list if someone else added it while trying to add
                        do
                        {
                            nextPtrs[idx] = data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine];
                        } while (Interlocked.CompareExchange(
                                     ref data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine], idx,
                                     nextPtrs[idx]) != nextPtrs[idx]);

                        return(false);
                    }
                } while (Interlocked.CompareExchange(ref buckets[bucket], idx, nextPtrs[idx]) != nextPtrs[idx]);
            }
            return(true);
        }
Example #10
0
        public static unsafe void Clear(NativeHashMapData *data)
        {
            int *buckets = (int *)data->buckets;

            for (int i = 0; i <= data->bucketCapacityMask; ++i)
            {
                buckets[i] = -1;
            }
            int *nextPtrs = (int *)data->next;

            for (int i = 0; i < data->capacity; ++i)
            {
                nextPtrs[i] = -1;
            }
            for (int tls = 0; tls < JobsUtility.MaxJobThreadCount; ++tls)
            {
                data->firstFreeTLS[tls * NativeHashMapData.IntsPerCacheLine] = -1;
            }
            data->allocatedIndexLength = 0;
        }
Example #11
0
        static unsafe public void Remove(NativeHashMapData *data, TKey key, bool isMultiHashMap)
        {
            // First find the slot based on the hash
            int *buckets  = (int *)data->buckets;
            int *nextPtrs = (int *)data->next;
            int  bucket   = key.GetHashCode() & data->bucketCapacityMask;

            int prevEntry = -1;
            int entryIdx  = buckets[bucket];

            while (entryIdx >= 0 && entryIdx < data->capacity)
            {
                if (UnsafeUtility.ReadArrayElement <TKey> (data->keys, entryIdx).Equals(key))
                {
                    // Found matching element, remove it
                    if (prevEntry < 0)
                    {
                        buckets[bucket] = nextPtrs[entryIdx];
                    }
                    else
                    {
                        nextPtrs[prevEntry] = nextPtrs[entryIdx];
                    }
                    // And free the index
                    int nextIdx = nextPtrs[entryIdx];
                    nextPtrs[entryIdx]    = data->firstFreeTLS[0];
                    data->firstFreeTLS[0] = entryIdx;
                    entryIdx = nextIdx;
                    // Can only be one hit in regular hashmaps, so return
                    if (!isMultiHashMap)
                    {
                        return;
                    }
                }
                else
                {
                    prevEntry = entryIdx;
                    entryIdx  = nextPtrs[entryIdx];
                }
            }
        }
Example #12
0
        public static unsafe void GetKeyArray <TKey>(NativeHashMapData *data, NativeArray <TKey> result)
            where TKey : struct
        {
            var bucketArray = (int *)data->buckets;
            var bucketNext  = (int *)data->next;

            int o = 0;

            for (int i = 0; i <= data->bucketCapacityMask; ++i)
            {
                int b = bucketArray[i];

                while (b != -1)
                {
                    result[o++] = UnsafeUtility.ReadArrayElement <TKey>(data->keys, b);
                    b           = bucketNext[b];
                }
            }

            Assert.AreEqual(result.Length, o);
        }
Example #13
0
        public static void ReallocateHashMap <TKey, TValue>(NativeHashMapData *data, int newCapacity,
                                                            int newBucketCapacity, Allocator label)
            where TKey : struct
            where TValue : struct
        {
            newBucketCapacity = CollectionHelper.CeilPow2(newBucketCapacity);

            if (data->capacity == newCapacity && (data->bucketCapacityMask + 1) == newBucketCapacity)
            {
                return;
            }

            if (data->capacity > newCapacity)
            {
                throw new Exception("Shrinking a hash map is not supported");
            }

            int keyOffset, nextOffset, bucketOffset;
            int totalSize = CalculateDataSize <TKey, TValue>(newCapacity, newBucketCapacity, out keyOffset,
                                                             out nextOffset, out bucketOffset);

            byte *newData    = (byte *)UnsafeUtility.Malloc(totalSize, JobsUtility.CacheLineSize, label);
            byte *newKeys    = newData + keyOffset;
            byte *newNext    = newData + nextOffset;
            byte *newBuckets = newData + bucketOffset;

            // The items are taken from a free-list and might not be tightly packed, copy all of the old capcity
            UnsafeUtility.MemCpy(newData, data->values, data->capacity * UnsafeUtility.SizeOf <TValue>());
            UnsafeUtility.MemCpy(newKeys, data->keys, data->capacity * UnsafeUtility.SizeOf <TKey>());
            UnsafeUtility.MemCpy(newNext, data->next, data->capacity * UnsafeUtility.SizeOf <int>());
            for (int emptyNext = data->capacity; emptyNext < newCapacity; ++emptyNext)
            {
                ((int *)newNext)[emptyNext] = -1;
            }

            // re-hash the buckets, first clear the new bucket list, then insert all values from the old list
            for (int bucket = 0; bucket < newBucketCapacity; ++bucket)
            {
                ((int *)newBuckets)[bucket] = -1;
            }
            for (int bucket = 0; bucket <= data->bucketCapacityMask; ++bucket)
            {
                int *buckets  = (int *)data->buckets;
                int *nextPtrs = (int *)newNext;
                while (buckets[bucket] >= 0)
                {
                    int curEntry = buckets[bucket];
                    buckets[bucket] = nextPtrs[curEntry];
                    int newBucket = UnsafeUtility.ReadArrayElement <TKey>(data->keys, curEntry).GetHashCode() &
                                    (newBucketCapacity - 1);
                    nextPtrs[curEntry]             = ((int *)newBuckets)[newBucket];
                    ((int *)newBuckets)[newBucket] = curEntry;
                }
            }

            UnsafeUtility.Free(data->values, label);
            if (data->allocatedIndexLength > data->capacity)
            {
                data->allocatedIndexLength = data->capacity;
            }
            data->values             = newData;
            data->keys               = newKeys;
            data->next               = newNext;
            data->buckets            = newBuckets;
            data->capacity           = newCapacity;
            data->bucketCapacityMask = newBucketCapacity - 1;
        }
Example #14
0
        public static unsafe bool TryAdd(NativeHashMapData *data, TKey key, TValue item, bool isMultiHashMap,
                                         Allocator allocation)
        {
            TValue tempItem;
            NativeMultiHashMapIterator <TKey> tempIt;

            if (!isMultiHashMap && TryGetFirstValueAtomic(data, key, out tempItem, out tempIt))
            {
                return(false);
            }
            // Allocate an entry from the free list
            int  idx;
            int *nextPtrs;

            if (data->allocatedIndexLength >= data->capacity && data->firstFreeTLS[0] < 0)
            {
                for (int tls = 1; tls < JobsUtility.MaxJobThreadCount; ++tls)
                {
                    if (data->firstFreeTLS[tls * NativeHashMapData.IntsPerCacheLine] >= 0)
                    {
                        idx      = data->firstFreeTLS[tls * NativeHashMapData.IntsPerCacheLine];
                        nextPtrs = (int *)data->next;
                        data->firstFreeTLS[tls * NativeHashMapData.IntsPerCacheLine] = nextPtrs[idx];
                        nextPtrs[idx]         = -1;
                        data->firstFreeTLS[0] = idx;
                        break;
                    }
                }
                if (data->firstFreeTLS[0] < 0)
                {
                    int newCap = NativeHashMapData.GrowCapacity(data->capacity);
                    NativeHashMapData.ReallocateHashMap <TKey, TValue>(data, newCap,
                                                                       NativeHashMapData.GetBucketSize(newCap), allocation);
                }
            }
            idx = data->firstFreeTLS[0];
            if (idx >= 0)
            {
                data->firstFreeTLS[0] = ((int *)data->next)[idx];
            }
            else
            {
                idx = data->allocatedIndexLength++;
            }

            if (idx < 0 || idx >= data->capacity)
            {
                throw new InvalidOperationException("Internal HashMap error");
            }

            // Write the new value to the entry
            UnsafeUtility.WriteArrayElement(data->keys, idx, key);
            UnsafeUtility.WriteArrayElement(data->values, idx, item);

            int bucket = key.GetHashCode() & data->bucketCapacityMask;
            // Add the index to the hash-map
            int *buckets = (int *)data->buckets;

            nextPtrs = (int *)data->next;

            nextPtrs[idx]   = buckets[bucket];
            buckets[bucket] = idx;

            return(true);
        }
Example #15
0
        private static unsafe int AllocEntry(NativeHashMapData *data, int threadIndex)
        {
            int  idx;
            int *nextPtrs = (int *)data->next;

            do
            {
                idx = data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine];
                if (idx < 0)
                {
                    // Try to refill local cache
                    Interlocked.Exchange(ref data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine], -2);
                    // If it failed try to get one from the never-allocated array
                    if (data->allocatedIndexLength < data->capacity)
                    {
                        idx = Interlocked.Add(ref data->allocatedIndexLength, 16) - 16;
                        if (idx < data->capacity - 1)
                        {
                            int count = Math.Min(16, data->capacity - idx);
                            for (int i = 1; i < count; ++i)
                            {
                                nextPtrs[idx + i] = idx + i + 1;
                            }
                            nextPtrs[idx + count - 1] = -1;
                            nextPtrs[idx]             = -1;
                            Interlocked.Exchange(
                                ref data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine], idx + 1);
                            return(idx);
                        }

                        if (idx == data->capacity - 1)
                        {
                            Interlocked.Exchange(
                                ref data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine], -1);
                            return(idx);
                        }
                    }
                    Interlocked.Exchange(ref data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine], -1);
                    // Failed to get any, try to get one from another free list
                    bool again = true;
                    while (again)
                    {
                        again = false;
                        for (int other = (threadIndex + 1) % JobsUtility.MaxJobThreadCount;
                             other != threadIndex;
                             other = (other + 1) % JobsUtility.MaxJobThreadCount)
                        {
                            do
                            {
                                idx = data->firstFreeTLS[other * NativeHashMapData.IntsPerCacheLine];
                                if (idx < 0)
                                {
                                    break;
                                }
                            } while (Interlocked.CompareExchange(
                                         ref data->firstFreeTLS[other * NativeHashMapData.IntsPerCacheLine],
                                         nextPtrs[idx], idx) != idx);
                            if (idx == -2)
                            {
                                again = true;
                            }
                            else if (idx >= 0)
                            {
                                nextPtrs[idx] = -1;
                                return(idx);
                            }
                        }
                    }
                    throw new InvalidOperationException("HashMap is full");
                }
                if (idx >= data->capacity)
                {
                    throw new InvalidOperationException(string.Format("nextPtr idx {0} beyond capacity {1}", idx,
                                                                      data->capacity));
                }
            } while (Interlocked.CompareExchange(
                         ref data->firstFreeTLS[threadIndex * NativeHashMapData.IntsPerCacheLine], nextPtrs[idx],
                         idx) != idx);
            nextPtrs[idx] = -1;
            return(idx);
        }