| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- namespace JsUtil
- {
- template <class TKey, class TValue> class WeakRefDictionaryEntry
- {
- public:
- static const int INVALID_HASH_VALUE = 0;
- hash_t hash; // Lower 31 bits of hash code << 1 | 1, 0 if unused
- int next; // Index of next entry, -1 if last
- Field(const RecyclerWeakReference<TKey>*) key; // Key of entry- this entry holds a weak reference to the key
- TValue value; // Value of entry
- };
- // TODO: convert to BaseDictionary- easier now to have custom dictionary since this does compacting
- // and weak reference resolution
- template <class TKey, class TValue, class KeyComparer = DefaultComparer<const TKey*>, bool cleanOnInsert = true> class WeaklyReferencedKeyDictionary
- {
- public:
- typedef WeakRefDictionaryEntry<TKey, Field(TValue)> EntryType;
- typedef TKey KeyType;
- typedef TValue ValueType;
- typedef void (*EntryRemovalCallbackMethodType)(const EntryType& e, void* cookie);
- struct EntryRemovalCallback
- {
- FieldNoBarrier(EntryRemovalCallbackMethodType) fnCallback;
- Field(void*) cookie;
- };
- private:
- Field(int) size;
- Field(int*) buckets;
- Field(EntryType *) entries;
- Field(int) count;
- Field(int) version;
- Field(int) freeList;
- Field(int) freeCount;
- FieldNoBarrier(Recycler*) recycler;
- FieldNoBarrier(EntryRemovalCallback) entryRemovalCallback;
- Field(uint) lastWeakReferenceCleanupId;
- Field(bool) disableCleanup;
- Field(int) modFunctionIndex;
- public:
- // Allow WeaklyReferencedKeyDictionary field to be inlined in classes with DEFINE_VTABLE_CTOR_MEMBER_INIT
- WeaklyReferencedKeyDictionary(VirtualTableInfoCtorEnum) { }
- WeaklyReferencedKeyDictionary(Recycler* recycler, int capacity = 0, EntryRemovalCallback* pEntryRemovalCallback = nullptr):
- buckets(nullptr),
- size(0),
- entries(nullptr),
- count(0),
- version(0),
- freeList(0),
- freeCount(0),
- recycler(recycler),
- lastWeakReferenceCleanupId(recycler->GetWeakReferenceCleanupId()),
- disableCleanup(false),
- modFunctionIndex(UNKNOWN_MOD_INDEX)
- {
- if (pEntryRemovalCallback != nullptr)
- {
- this->entryRemovalCallback.fnCallback = pEntryRemovalCallback->fnCallback;
- this->entryRemovalCallback.cookie = pEntryRemovalCallback->cookie;
- }
- else
- {
- this->entryRemovalCallback.fnCallback = nullptr;
- }
- if (capacity > 0) { Initialize(capacity); }
- }
- ~WeaklyReferencedKeyDictionary()
- {
- }
- inline int Count()
- {
- return count - freeCount;
- }
- TValue Item(TKey* key)
- {
- int i = FindEntry(key);
- if (i >= 0) return entries[i].value;
- Js::Throw::FatalInternalError();
- }
- void Item(TKey* key, const TValue value)
- {
- Insert(key, value, false);
- }
- const TValue& GetValueAt(const int& index) const
- {
- if (index >= 0 && index < count)
- {
- return entries[index].value;
- }
- Js::Throw::FatalInternalError();
- }
- bool TryGetValue(const TKey* key, TValue* value)
- {
- int i = FindEntry<TKey>(key);
- if (i >= 0)
- {
- *value = entries[i].value;
- return true;
- }
- return false;
- }
- bool TryGetValueAndRemove(const TKey* key, TValue* value)
- {
- if (buckets == nullptr) return false;
- hash_t hash = GetHashCode(key);
- uint targetBucket = PrimePolicy::GetBucket(hash, size, modFunctionIndex);
- int last = -1;
- int i = 0;
- if ((i = FindEntry<TKey>(key, hash, targetBucket, last)) != -1)
- {
- *value = entries[i].value;
- RemoveEntry(i, last, targetBucket);
- return true;
- }
- return false;
- }
- template <typename TLookup>
- inline TValue Lookup(const TLookup* key, TValue defaultValue, __out TKey const** pKeyOut)
- {
- int i = FindEntry(key);
- if (i >= 0)
- {
- (*pKeyOut) = entries[i].key->Get();
- return entries[i].value;
- }
- (*pKeyOut) = nullptr;
- return defaultValue;
- }
- inline TValue Lookup(const TKey* key, TValue defaultValue)
- {
- int i = FindEntry(key);
- if (i >= 0)
- {
- return entries[i].value;
- }
- return defaultValue;
- }
- const RecyclerWeakReference<TKey>* Add(TKey* key, TValue value)
- {
- return Insert(key, value, true);
- }
- const RecyclerWeakReference<TKey>* UncheckedAdd(TKey* key, TValue value)
- {
- return Insert(key, value, true, false);
- }
- const RecyclerWeakReference<TKey>* UncheckedAdd(const RecyclerWeakReference<TKey>* weakRef, TValue value)
- {
- return UncheckedInsert(weakRef, value);
- }
- template<class Fn>
- void Map(Fn fn)
- {
- for(int i = 0; i < size; i++)
- {
- if(buckets[i] != -1)
- {
- for(int previousIndex = -1, currentIndex = buckets[i]; currentIndex != -1;)
- {
- EntryType ¤tEntry = entries[currentIndex];
- TKey * key = currentEntry.key->Get();
- if(key != nullptr)
- {
- fn(key, currentEntry.value, currentEntry.key);
- // Keep the entry
- previousIndex = currentIndex;
- currentIndex = currentEntry.next;
- }
- else
- {
- // Remove the entry
- const int nextIndex = currentEntry.next;
- RemoveEntry(currentIndex, previousIndex, i);
- currentIndex = nextIndex;
- }
- }
- }
- }
- }
- void SetDisableCleanup(bool disableCleanup)
- {
- this->disableCleanup = disableCleanup;
- }
- bool GetDisableCleanup()
- {
- return this->disableCleanup;
- }
- bool Clean()
- {
- if (!disableCleanup && recycler->GetWeakReferenceCleanupId() != this->lastWeakReferenceCleanupId)
- {
- Map([](TKey * key, TValue value, const RecyclerWeakReference<TKey>* weakRef) {});
- this->lastWeakReferenceCleanupId = recycler->GetWeakReferenceCleanupId();
- }
- return freeCount > 0;
- }
- void Clear()
- {
- if (count > 0)
- {
- for (int i = 0; i < size; i++) buckets[i] = -1;
- ClearArray(entries, size);
- freeList = -1;
- count = 0;
- freeCount = 0;
- modFunctionIndex = UNKNOWN_MOD_INDEX;
- }
- }
- void EnsureCapacity()
- {
- if (freeCount == 0 && count == size)
- {
- if (cleanOnInsert && Clean())
- {
- Assert(freeCount > 0);
- }
- else
- {
- Resize();
- }
- }
- }
- private:
- const RecyclerWeakReference<TKey>* UncheckedInsert(const RecyclerWeakReference<TKey>* weakRef, TValue value)
- {
- if (buckets == nullptr) Initialize(0);
- int hash = GetHashCode(weakRef->FastGet());
- uint bucket = PrimePolicy::GetBucket(hash, size, modFunctionIndex);
- Assert(FindEntry(weakRef->FastGet()) == -1);
- return Insert(weakRef, value, hash, bucket);
- }
- const RecyclerWeakReference<TKey>* Insert(TKey* key, TValue value, bool add, bool checkForExisting = true)
- {
- if (buckets == nullptr) Initialize(0);
- hash_t hash = GetHashCode(key);
- uint bucket = PrimePolicy::GetBucket(hash, size, modFunctionIndex);
- if (checkForExisting)
- {
- int previous = -1;
- int i = FindEntry(key, hash, bucket, previous);
- if (i != -1)
- {
- if (add)
- {
- Js::Throw::FatalInternalError();
- }
- entries[i].value = value;
- version++;
- return entries[i].key;
- }
- }
- // We know we need to insert- so first try creating the weak reference, before adding it to
- // the dictionary. If we OOM here, we still leave the dictionary as we found it.
- const RecyclerWeakReference<TKey>* weakRef = recycler->CreateWeakReferenceHandle<TKey>(key);
- return Insert(weakRef, value, hash, bucket);
- }
- const RecyclerWeakReference<TKey>* Insert(const RecyclerWeakReference<TKey>* weakRef, TValue value, int hash, uint bucket)
- {
- int index;
- if (freeCount > 0)
- {
- index = freeList;
- freeList = entries[index].next;
- freeCount--;
- }
- else
- {
- if (count == size)
- {
- if (cleanOnInsert && Clean())
- {
- index = freeList;
- freeList = entries[index].next;
- freeCount--;
- }
- else
- {
- Resize();
- bucket = PrimePolicy::GetBucket(hash, size, modFunctionIndex);
- index = count;
- count++;
- }
- }
- else
- {
- index = count;
- count++;
- }
- }
- entries[index].next = buckets[bucket];
- entries[index].key = weakRef;
- entries[index].hash = hash;
- entries[index].value = value;
- buckets[bucket] = index;
- version++;
- return entries[index].key;
- }
- void Resize()
- {
- int modIndex = UNKNOWN_MOD_INDEX;
- int newSize = PrimePolicy::GetSize(count * 2, &modIndex);
- if (newSize <= count)
- {
- // throw OOM if we can't increase the dictionary size
- Js::Throw::OutOfMemory();
- }
- int* newBuckets = RecyclerNewArrayLeaf(recycler, int, newSize);
- for (int i = 0; i < newSize; i++) newBuckets[i] = -1;
- EntryType* newEntries = RecyclerNewArray(recycler, EntryType, newSize);
- CopyArray<EntryType, Field(const RecyclerWeakReference<TKey>*)>(newEntries, newSize, entries, count);
- AnalysisAssert(count < newSize);
- modFunctionIndex = modIndex;
- for (int i = 0; i < count; i++)
- {
- uint bucket = PrimePolicy::GetBucket(newEntries[i].hash, newSize, modFunctionIndex);
- newEntries[i].next = newBuckets[bucket];
- newBuckets[bucket] = i;
- }
- buckets = newBuckets;
- size = newSize;
- entries = newEntries;
- }
- template <typename TLookup>
- inline hash_t GetHashCode(const TLookup* key)
- {
- return TAGHASH(KeyComparer::GetHashCode(key));
- }
- template <typename TLookup>
- inline int FindEntry(const TLookup* key)
- {
- if (buckets != nullptr)
- {
- hash_t hash = GetHashCode(key);
- uint bucket = PrimePolicy::GetBucket(hash, size, modFunctionIndex);
- int previous = -1;
- return FindEntry(key, hash, bucket, previous);
- }
- return -1;
- }
- template <typename TLookup>
- inline int FindEntry(const TLookup* key, hash_t const hash, uint& bucket, int& previous)
- {
- if (buckets != nullptr)
- {
- BOOL inSweep = this->recycler->IsSweeping();
- previous = -1;
- for (int i = buckets[bucket]; i >= 0; )
- {
- if (entries[i].hash == hash)
- {
- TKey* strongRef = nullptr;
- if (!inSweep)
- {
- // Quickly check for null if we're not in sweep- if it's null, it's definitely been collected
- // so remove
- strongRef = entries[i].key->FastGet();
- }
- else
- {
- // If we're in sweep, use the slower Get which checks if the object is getting collected
- // This could return null too but we won't clean it up now, we'll clean it up later
- strongRef = entries[i].key->Get();
- }
- if (strongRef == nullptr)
- {
- i = RemoveEntry(i, previous, bucket);
- continue;
- }
- else
- {
- // if we get here, strongRef is not null
- if (KeyComparer::Equals(strongRef, key))
- return i;
- }
- }
- previous = i;
- i = entries[i].next;
- }
- }
- return -1;
- }
- void Initialize(int capacity)
- {
- int modIndex = UNKNOWN_MOD_INDEX;
- int size = PrimePolicy::GetSize(capacity, &modIndex);
- int* buckets = RecyclerNewArrayLeaf(recycler, int, size);
- EntryType * entries = RecyclerNewArray(recycler, EntryType, size);
- // No need for auto pointers here since these are both recycler
- // allocated objects
- if (buckets != nullptr && entries != nullptr)
- {
- this->size = size;
- this->buckets = buckets;
- for (int i = 0; i < size; i++) buckets[i] = -1;
- this->entries = entries;
- this->freeList = -1;
- this->modFunctionIndex = modIndex;
- }
- }
- int RemoveEntry(int i, int previous, uint bucket)
- {
- int next = entries[i].next;
- if (previous < 0) // Previous < 0 => first node
- {
- buckets[bucket] = entries[i].next;
- }
- else
- {
- entries[previous].next = entries[i].next;
- }
- if (this->entryRemovalCallback.fnCallback != nullptr)
- {
- this->entryRemovalCallback.fnCallback(entries[i], this->entryRemovalCallback.cookie);
- }
- entries[i].next = freeList;
- entries[i].key = nullptr;
- entries[i].hash = EntryType::INVALID_HASH_VALUE;
- // Hold onto the pid here so that we can reuse it
- // entries[i].value = nullptr;
- freeList = i;
- freeCount++;
- version++;
- return next;
- }
- };
- }
|