text: //-------------------------------------------------------------------------------------------------------
  // 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
  {
      interface IWeakReferenceDictionary
      {
          virtual void Cleanup() = 0;
      };
  
      template <
          class TKey,
          class TValue,
          class SizePolicy = PowerOf2SizePolicy,
          template <typename ValueOrKey> class Comparer = DefaultComparer
      >
      class WeakReferenceDictionary: public BaseDictionary<TKey, RecyclerWeakReference<TValue>*, RecyclerNonLeafAllocator, SizePolicy, Comparer, WeakRefValueDictionaryEntry>,
                                     public IWeakReferenceDictionary
      {
          typedef BaseDictionary<TKey, RecyclerWeakReference<TValue>*, RecyclerNonLeafAllocator, SizePolicy, Comparer, WeakRefValueDictionaryEntry> Base;
          typedef typename Base::EntryType EntryType;
  
      public:
          WeakReferenceDictionary(Recycler* recycler, int capacity = 0):
            Base(recycler, capacity)
          {
              Assert(reinterpret_cast<void*>(this) == reinterpret_cast<void*>((IWeakReferenceDictionary*) this));
          }
  
          virtual void Cleanup() override
          {
              this->MapAndRemoveIf([](typename Base::EntryType &entry)
              {
                  return (Base::EntryType::NeedsCleanup(entry));
              });
          }
  
      private:
          using Base::Clone;
          using Base::Copy;
  
          PREVENT_COPY(WeakReferenceDictionary);
      };
  
  #if ENABLE_WEAK_REFERENCE_REGIONS
  
      template<bool, typename T = void>
      struct enable_if {};
  
      template<typename T>
      struct enable_if<true, T>
      {
          typedef T type;
      };
  
      template <
          class TEntry,
          class TWeak,
          bool keyIsWeak,
          class SizePolicy = PowerOf2SizePolicy,
          template <typename ValueOrKey> class Comparer = DefaultComparer,
          typename Lock = NoResizeLock,
          class AllocType = Recycler // Should always be recycler; this is to sufficiently confuse the RecyclerChecker
      >
      class SplitWeakDictionary : protected Lock, public IWeakReferenceDictionary
      {
      private:
          template <typename T1, typename T2, bool second>
          struct Select;
          
          template <typename T1, typename T2>
          struct Select<T1, T2, false>
          {
              typedef T1 type;
          };
  
          template <typename T1, typename T2>
          struct Select<T1, T2, true>
          {
              typedef T2 type;
          };
  
          typedef typename Select<TEntry, TWeak, keyIsWeak>::type TBKey;
          typedef typename Select<TEntry, TWeak, !keyIsWeak>::type TBValue;
  
          typedef typename AllocatorInfo<Recycler, TEntry>::AllocatorFunc EntryAllocatorFuncType;
  
          enum InsertOperations
          {
              Insert_Add,          // FatalInternalError if the item already exist in debug build
              Insert_AddNew,          // Ignore add if the item already exist
              Insert_Item             // Replace the item if it already exist
          };
  
      protected:
  
          typedef ValueEntry<TEntry> EntryType;
          typedef RecyclerWeakReferenceRegionItem<TWeak> WeakType;
  
          Field(int*) buckets;
          Field(EntryType*) entries;
          Field(WeakType*) weakRefs;
          FieldNoBarrier(Recycler*) alloc;
          Field(int) size;
          Field(uint) bucketCount;
          Field(int) count;
          Field(int) freeList;
          Field(int) freeCount;
          Field(int) modFunctionIndex;
  
      private:
          static const int FreeListSentinel = -2;
  
          PREVENT_COPY(SplitWeakDictionary);
  
  
          class AutoDoResize
          {
          public:
              AutoDoResize(Lock& lock) : lock(lock) { lock.BeginResize(); };
              ~AutoDoResize() { lock.EndResize(); };
          private:
              Lock & lock;
          };
  
      public:
          // Allow SplitWeakDictionary field to be inlined in classes with DEFINE_VTABLE_CTOR_MEMBER_INIT
          SplitWeakDictionary(VirtualTableInfoCtorEnum) { }
  
          SplitWeakDictionary(Recycler* allocator, int capacity = 0)
              : buckets(nullptr),
              entries(nullptr),
              weakRefs(nullptr),
              alloc(allocator),
              size(0),
              bucketCount(0),
              count(0),
              freeList(0),
              freeCount(0),
              modFunctionIndex(UNKNOWN_MOD_INDEX)
          {
              Assert(allocator);
              Assert(reinterpret_cast<void*>(this) == reinterpret_cast<void*>((IWeakReferenceDictionary*)this));
  
              // If initial capacity is negative or 0, lazy initialization on
              // the first insert operation is performed.
              if (capacity > 0)
              {
                  Initialize(capacity);
              }
          }
  
          virtual void Cleanup() override
          {
              this->MapAndRemoveIf([](EntryType& entry, WeakType& weakRef)
              {
                  return weakRef == nullptr;
              });
          }
  
          inline int Capacity() const
          {
              return size;
          }
  
          inline int Count() const
          {
              return count - freeCount;
          }
  
          void Clear()
          {
              if (count > 0)
              {
                  memset(buckets, -1, bucketCount * sizeof(buckets[0]));
                  memset(entries, 0, sizeof(EntryType) * size);
                  memset(weakRefs, 0, sizeof(WeakType) * size); // TODO: issues with background threads?
                  count = 0;
                  freeCount = 0;
              }
          }
  
          void ResetNoDelete()
          {
              this->size = 0;
              this->bucketCount = 0;
              this->buckets = nullptr;
              this->entries = nullptr;
              this->weakRefs = nullptr;
              this->count = 0;
              this->freeCount = 0;
              this->modFunctionIndex = UNKNOWN_MOD_INDEX;
          }
  
          void Reset()
          {
              ResetNoDelete();
          }
  
          // Returns whether the dictionary was resized or not
          bool EnsureCapacity()
          {
              if (freeCount == 0 && count == size)
              {
                  Resize();
                  return true;
              }
  
              return false;
          }
  
          int GetNextIndex()
          {
              if (freeCount != 0)
              {
                  Assert(freeCount > 0);
                  Assert(freeList >= 0);
                  Assert(freeList < count);
                  return freeList;
              }
  
              return count;
          }
  
          TBValue Item(const TBKey& key)
          {
              int i = FindEntry(key);
              Assert(i >= 0);
              return values[i];
          }
  
          const TBValue Item(const TBKey& key) const
          {
              int i = FindEntry(key);
              Assert(i >= 0);
              return values[i];
          }
  
          int Add(const TBKey& key, const TBValue& value)
          {
              return Insert<Insert_Add>(key, value);
          }
  
          int AddNew(const TBKey& key, const TBValue& value)
          {
              return Insert<Insert_AddNew>(key, value);
          }
  
          int Item(const TBKey& key, const TBValue& value)
          {
              return Insert<Insert_Item>(key, value);
          }
  
          bool Contains(KeyValuePair<TBKey, TBValue> keyValuePair)
          {
              int i = FindEntry(keyValuePair.Key());
              TBValue val = this->GetValue(i);
              if (i >= 0 && Comparer<TBValue>::Equals(val, keyValuePair.Value()))
              {
                  return true;
              }
              return false;
          }
  
          bool ContainsKey(const TBKey& key) const
          {
              return FindEntry(key) >= 0;
          }
  
          template <typename TLookup>
          inline const TBValue& LookupWithKey(const TLookup& key, const TBValue& defaultValue) 
          {
              int i = FindEntryWithKey(key);
              if (i >= 0)
              {
                  return this->GetValue(i);
              }
              return defaultValue;
          }
  
          inline const TBValue& Lookup(const TBKey& key, const TBValue& defaultValue) 
          {
              return LookupWithKey<TBKey>(key, defaultValue);
          }
  
          template <typename TLookup>
          bool TryGetValue(const TLookup& key, TBValue* value) 
          {
              int i = FindEntryWithKey(key);
              if (i >= 0)
              {
                  *value = this->GetValue(i);
                  return true;
              }
              return false;
          }
  
  
          bool TryGetValueAndRemove(const TBKey& key, TBValue* value)
          {
              int i, last;
              uint targetBucket;
              if (FindEntryWithKey(key, &i, &last, &targetBucket))
              {
                  *value = this->GetValue(i);
                  RemoveAt(i, last, targetBucket);
                  return true;
              }
              return false;
          }
  
          bool Remove(const TBKey& key)
          {
              int i, last;
              uint targetBucket;
              if (FindEntryWithKey(key, &i, &last, &targetBucket))
              {
                  RemoveAt(i, last, targetBucket);
                  return true;
              }
              return false;
          }
  
          bool Remove(KeyValuePair<TBKey, TBValue> keyValuePair)
          {
              int i, last;
              uint targetBucket;
              if (FindEntryWithKey(keyValuePair.Key(), &i, &last, &targetBucket))
              {
                  const TBValue& value = this->GetValue(i);
                  if (Comparer<TBValue>::Equals(value, keyValuePair.Value()))
                  {
                      RemoveAt(i, last, targetBucket);
                      return true;
                  }
              }
              return false;
          }
  
          int GetLastIndex()
          {
              return count - 1;
          }
  
          void LockResize()
          {
              __super::LockResize();
          }
  
          void UnlockResize()
          {
              __super::UnlockResize();
          }
  
      protected:
  
          template<class Fn>
          void MapAndRemoveIf(Fn fn)
          {
              for (uint i = 0; i < bucketCount; i++)
              {
                  if (buckets[i] != -1)
                  {
                      for (int currentIndex = buckets[i], lastIndex = -1; currentIndex != -1;)
                      {
                          // If the predicate says we should remove this item
                          if (fn(entries[currentIndex], weakRefs[currentIndex]) == true)
                          {
                              const int nextIndex = entries[currentIndex].next;
                              RemoveAt(currentIndex, lastIndex, i);
                              currentIndex = nextIndex;
                          }
                          else
                          {
                              lastIndex = currentIndex;
                              currentIndex = entries[currentIndex].next;
                          }
                      }
                  }
              }
          }
  
          template <typename TLookup>
          static hash_t GetHashCodeWithKey(const TLookup& key)
          {
              // set last bit to 1 to avoid false positive to make hash appears to be a valid recycler address.
              // In the same line, 0 should be use to indicate a non-existing entry.
              return TAGHASH(Comparer<TLookup>::GetHashCode(key));
          }
  
          static uint GetBucket(hash_t hashCode, int bucketCount, int modFunctionIndex)
          {
              return SizePolicy::GetBucket(UNTAGHASH(hashCode), bucketCount, modFunctionIndex);
          }
  
          uint GetBucket(hash_t hashCode) const
          {
              return GetBucket(hashCode, this->bucketCount, modFunctionIndex);
          }
  
          static bool IsFreeEntry(const EntryType &entry)
          {
              // A free entry's next index will be (FreeListSentinel - nextIndex), such that it is always <= FreeListSentinel, for fast entry iteration
              // allowing for skipping over free entries. -1 is reserved for the end-of-chain marker for a used entry.
              return entry.next <= FreeListSentinel;
          }
  
  
          void SetNextFreeEntryIndex(EntryType &freeEntry, const int nextFreeEntryIndex)
          {
              Assert(!IsFreeEntry(freeEntry));
              Assert(nextFreeEntryIndex >= -1);
              Assert(nextFreeEntryIndex < count);
  
              // The last entry in the free list chain will have a next of FreeListSentinel to indicate that it is a free entry. The end of the
              // free list chain is identified using freeCount.
              freeEntry.next = nextFreeEntryIndex >= 0 ? FreeListSentinel - nextFreeEntryIndex : FreeListSentinel;
          }
  
          static int GetNextFreeEntryIndex(const EntryType &freeEntry)
          {
              Assert(IsFreeEntry(freeEntry));
              return FreeListSentinel - freeEntry.next;
          }
  
          void Initialize(int capacity)
          {
              // minimum capacity is 4
              int initSize = max(capacity, 4);
              int modIndex = UNKNOWN_MOD_INDEX;
              uint initBucketCount = SizePolicy::GetBucketSize(initSize, &modIndex);
              AssertMsg(initBucketCount > 0, "Size returned by policy should be greater than 0");
  
              int* newBuckets = nullptr;
              EntryType* newEntries = nullptr;
              WeakType* newWeakRefs = nullptr;
              Allocate(&newBuckets, &newEntries, &newWeakRefs, initBucketCount, initSize);
  
              // Allocation can throw - assign only after allocation has succeeded.
              this->buckets = newBuckets;
              this->entries = newEntries;
              this->weakRefs = newWeakRefs;
              this->bucketCount = initBucketCount;
              this->size = initSize;
              this->modFunctionIndex = modIndex;
              Assert(this->freeCount == 0);
          }
  
          inline void RemoveAt(const int i, const int last, const uint targetBucket)
          {
              if (last < 0)
              {
                  buckets[targetBucket] = entries[i].next;
              }
              else
              {
                  entries[last].next = entries[i].next;
              }
              entries[i].Clear();
              weakRefs[i].Clear();
              SetNextFreeEntryIndex(entries[i], freeCount == 0 ? -1 : freeList);
              freeList = i;
              freeCount++;
          }
  
          inline int FindEntry(const TBKey& key)
          {
              return FindEntryWithKey<TBKey>(key);
          }
  
          template <typename LookupType>
          inline int FindEntryWithKey(const LookupType& key)
          {
              int * localBuckets = buckets;
              if (localBuckets != nullptr)
              {
                  hash_t hashCode = GetHashCodeWithKey<LookupType>(key);
                  uint targetBucket = this->GetBucket(hashCode);
                  EntryType * localEntries = entries;
                  for (int i = localBuckets[targetBucket], last = -1; i >= 0;)
                  {
                      TBKey k = this->GetKey(i);
                      if (this->RemoveIfCollected(k, &i, last, targetBucket))
                      {
                          continue;
                      }
                      if (Comparer<TBKey>::Equals(k, key))
                      {
                          return i;
                      }
  
                      last = i;
                      i = localEntries[i].next;
                  }
              }
              return -1;
          }
  
          template <typename LookupType>
          inline bool FindEntryWithKey(const LookupType& key, int *const i, int *const last, uint *const targetBucket)
          {
              int * localBuckets = buckets;
              if (localBuckets != nullptr)
              {
                  hash_t hashCode = GetHashCodeWithKey<LookupType>(key);
                  *targetBucket = this->GetBucket(hashCode);
                  *last = -1;
                  EntryType * localEntries = entries;
                  for (*i = localBuckets[*targetBucket]; *i >= 0;)
                  {
                      TBKey k = this->GetKey(*i);
                      if (this->RemoveIfCollected(k, i, *last, *targetBucket))
                      {
                          continue;
                      }
                      if (Comparer<TBKey>::Equals(k, key))
                      {
                          return true;
                      }
  
                      *last = *i;
                      *i = localEntries[*i].next;
                  }
              }
              return false;
          }
  
      private:
  
          void Resize()
          {
              AutoDoResize autoDoResize(*this);
              __analysis_assert(count > 1);
  
              int newSize = SizePolicy::GetNextSize(count);
              int modIndex = UNKNOWN_MOD_INDEX;
              uint newBucketCount = SizePolicy::GetBucketSize(newSize, &modIndex);
  
              __analysis_assume(newSize > count);
              int* newBuckets = nullptr;
              EntryType* newEntries = nullptr;
              WeakType* newWeakRefs = nullptr;
              if (newBucketCount == bucketCount)
              {
                  // no need to rehash
                  newEntries = AllocateEntries(newSize);
                  newWeakRefs = AllocateWeakRefs(newSize);
                  CopyArray<EntryType>(newEntries, newSize, entries, count);
                  CopyArray<WeakType>(newWeakRefs, newSize, weakRefs, count); // TODO: concurrency issues?
  
                  this->entries = newEntries;
                  this->weakRefs = newWeakRefs;
                  this->size = newSize;
                  this->modFunctionIndex = modIndex;
                  return;
              }
  
              Allocate(&newBuckets, &newEntries, &newWeakRefs, newBucketCount, newSize);
  
              this->modFunctionIndex = modIndex;
  
              // Need to re-bucket the entries
              // Also need to account for whether the weak refs have been collected
              // Have to go in order so we can remove entries as appropriate
              this->count = 0;
              for (uint i = 0; i < this->bucketCount; ++i)
              {
                  if (this->buckets[i] < 0)
                  {
                      continue;
                  }
  
                  for (int currentEntry = this->buckets[i]; currentEntry != -1; )
                  {
                      if (IsFreeEntry(entries[currentEntry]))
                      {
                          // Entry is free; this shouldn't happen, but stop following the chain
                          AssertMsg(false, "Following bucket chains should not result in free entries");
                          break;
                      }
                      if (this->weakRefs[currentEntry] == nullptr)
                      {
                          // The weak ref has been collected; don't bother bringing it to the new collection
                          currentEntry = this->entries[currentEntry].next;
                          continue;
                      }
  
                      hash_t hashCode = GetHashCodeWithKey<TBKey>(this->GetKey(currentEntry));
                      int bucket = GetBucket(hashCode, newBucketCount, modFunctionIndex);
                      newEntries[count].next = newBuckets[bucket];
                      newEntries[count].SetValue(this->entries[currentEntry].Value());
                      newWeakRefs[count] = this->weakRefs[currentEntry];
                      newBuckets[bucket] = count;
                      ++count;
  
                      currentEntry = this->entries[currentEntry].next;
                  }
              }
              this->freeCount = 0;
              this->freeList = 0;
              this->buckets = newBuckets;
              this->entries = newEntries;
              this->weakRefs = newWeakRefs;
              this->bucketCount = newBucketCount;
              this->size = newSize;
  
          }
  
          template <InsertOperations op>
          int Insert(const TBKey& key, const TBValue& value)
          {
              int * localBuckets = buckets;
              if (localBuckets == nullptr)
              {
                  Initialize(0);
                  localBuckets = buckets;
              }
  
  #if DBG
              // Always search and verify
              const bool needSearch = true;
  #else
              const bool needSearch = (op != Insert_Add);
  #endif
              hash_t hashCode = GetHashCodeWithKey<TBKey>(key);
              uint targetBucket = this->GetBucket(hashCode);
              if (needSearch)
              {
                  EntryType * localEntries = entries;
                  for (int i = localBuckets[targetBucket], last = -1; i >= 0;)
                  {
                      TBKey k = this->GetKey(i);
                      if (this->RemoveIfCollected(k, &i, last, targetBucket))
                      {
                          continue;
                      }
  
                      if (Comparer<TBKey>::Equals(k, key))
                      {
                          Assert(op != Insert_Add);
                          if (op == Insert_Item)
                          {
                              SetValue(value, i);
                              return i;
                          }
                          return -1;
                      }
  
                      last = i;
                      i = localEntries[i].next;
                  }
              }
  
              // Ideally we'd do cleanup only if weak references have been collected since the last resize
              // but that would require us to use an additional field to store the last recycler cleanup id
              // that we saw
              // We can add that optimization later if we have to.
              if (freeCount == 0 && count == size)
              {
                  this->Cleanup();
              }
  
              int index;
              if (freeCount != 0)
              {
                  Assert(freeCount > 0);
                  Assert(freeList >= 0);
                  Assert(freeList < count);
                  index = freeList;
                  freeCount--;
                  if (freeCount != 0)
                  {
                      freeList = GetNextFreeEntryIndex(entries[index]);
                  }
              }
              else
              {
                  // If there's nothing free, then in general, we set index to count, and increment count
                  // If we resize, we also need to recalculate the target
                  // However, if cleanup is supported, then before resize, we should try and clean up and see
                  // if something got freed, and if it did, reuse that index
                  if (count == size)
                  {
                      Resize();
                      targetBucket = this->GetBucket(hashCode);
                      index = count;
                      count++;
                  }
                  else
                  {
                      index = count;
                      count++;
                  }
  
                  Assert(count <= size);
                  Assert(index < size);
              }
  
              SetKeyValue(key, value, index);
              entries[index].next = buckets[targetBucket];
              buckets[targetBucket] = index;
  
              return index;
          }
  
          __ecount(bucketCount) int *AllocateBuckets(DECLSPEC_GUARD_OVERFLOW const uint bucketCount)
          {
              return
                  AllocateArray<AllocType, int, false>(
                      TRACK_ALLOC_INFO(alloc, int, AllocType, 0, bucketCount),
                      TypeAllocatorFunc<AllocType, int>::GetAllocFunc(),
                      bucketCount);
          }
  
          __ecount(size) EntryType * AllocateEntries(DECLSPEC_GUARD_OVERFLOW int size, const bool zeroAllocate = true)
          {
              // Note that the choice of leaf/non-leaf node is decided for the EntryType on the basis of TValue. By default, if
              // TValue is a pointer, a non-leaf allocation is done. This behavior can be overridden by specializing
              // TypeAllocatorFunc for TValue.
              return
                  AllocateArray<Recycler, EntryType, false>(
                      TRACK_ALLOC_INFO(alloc, EntryType, Recycler, 0, size),
                      zeroAllocate ? EntryAllocatorFuncType::GetAllocZeroFunc() : EntryAllocatorFuncType::GetAllocFunc(),
                      size);
          }
  
          __ecount(size) WeakType * AllocateWeakRefs(DECLSPEC_GUARD_OVERFLOW int size)
          {
              return alloc->CreateWeakReferenceRegion<TWeak>(size);
          }
  
  
          void Allocate(__deref_out_ecount(bucketCount) int** ppBuckets, __deref_out_ecount(size) EntryType** ppEntries, __deref_out_ecount(size) WeakType** ppWeakRefs, DECLSPEC_GUARD_OVERFLOW uint bucketCount, DECLSPEC_GUARD_OVERFLOW int size)
          {
              int *const newBuckets = AllocateBuckets(bucketCount);
              Assert(newBuckets); // no-throw allocators are currently not supported
  
              EntryType *newEntries = AllocateEntries(size);
              Assert(newEntries); // no-throw allocators are currently not supported
  
              WeakType * newWeakRefs = AllocateWeakRefs(size);
  
              memset(newBuckets, -1, bucketCount * sizeof(newBuckets[0]));
  
              *ppBuckets = newBuckets;
              *ppEntries = newEntries;
              *ppWeakRefs = newWeakRefs;
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<weakKey, TBKey>::type GetKey(int index) const
          {
              return this->weakRefs[index];
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<!weakKey, TBKey>::type GetKey(int index) const
          {
              return this->entries[index].Value();
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<weakKey, TBValue>::type GetValue(int index) const
          {
              return this->entries[index].Value();
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<!weakKey, TBValue>::type GetValue(int index) const
          {
              return this->weakRefs[index];
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<weakKey, bool>::type RemoveIfCollected(TBKey const key, int* i, int last, int targetBucket)
          {
              if (key == nullptr)
              {
                  // Key has been collected
                  int next = entries[*i].next;
                  RemoveAt(*i, last, targetBucket);
                  *i = next;
                  return true;
              }
              return false;
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<!weakKey, bool>::type RemoveIfCollected(TBKey const key, int* i, int last, int targetBucket)
          {
              return false;
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<weakKey, void>::type SetKeyValue(const TBKey& key, const TBValue& value, int index)
          {
              weakRefs[index] = key;
              entries[index].SetValue(value);
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<!weakKey, void>::type SetKeyValue(const TBKey& key, const TBValue& value, int index)
          {
              entries[index].SetValue(key);
              weakRefs[index] = value;
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<weakKey, void>::type SetValue(const TBValue& value, int index)
          {
              this->entries[index].SetValue(value);
          }
  
          template <bool weakKey = keyIsWeak>
          inline typename enable_if<!weakKey, void>::type SetValue(const TBValue& value, int index)
          {
              this->weakRefs[index] = value;
          }
      };
  
      template <
          class TKey,
          class TValue,
          class SizePolicy = PowerOf2SizePolicy,
          template <typename ValueOrKey> class Comparer = DefaultComparer,
          typename Lock = NoResizeLock
      >
      class WeakReferenceRegionDictionary : public SplitWeakDictionary<TKey, TValue, false, SizePolicy, Comparer, Lock, Recycler>
      {
          typedef SplitWeakDictionary<TKey, TValue, false, SizePolicy, Comparer, Lock, Recycler> Base;
          typedef typename Base::EntryType EntryType;
          typedef typename Base::WeakType ValueType;
  
      public:
  
          WeakReferenceRegionDictionary(Recycler* allocator, int capacity = 0)
              : Base(allocator, capacity)
          {
          }
      };
  
      template <
          class TKey,
          class TValue,
          template <typename ValueOrKey> class Comparer = DefaultComparer,
          class SizePolicy = PrimeSizePolicy,
          typename Lock = NoResizeLock
      >
      class WeakReferenceRegionKeyDictionary : public SplitWeakDictionary<TValue, TKey, true, SizePolicy, Comparer, Lock, Recycler>
      {
          typedef SplitWeakDictionary<TValue, TKey, true, SizePolicy, Comparer, Lock, Recycler> Base;
  
          typedef typename Base::EntryType EntryType;
          typedef typename Base::WeakType KeyType;
  
      public:
          // Allow WeakReferenceRegionKeyDictionary field to be inlined in classes with DEFINE_VTABLE_CTOR_MEMBER_INIT
          WeakReferenceRegionKeyDictionary(VirtualTableInfoCtorEnum v): Base(v) { }
  
          WeakReferenceRegionKeyDictionary(Recycler* allocator, int capacity = 0)
              : Base(allocator, capacity)
          {
          }
  
          template <class Fn>
          void Map(Fn fn)
          {
              this->MapAndRemoveIf([=](EntryType& entry, KeyType& weakRef)
              {
                  if (weakRef == nullptr)
                  {
                      return true;
                  }
                  fn(weakRef, entry.Value(), weakRef);
                  return false;
              });
          }
  
          bool Clean()
          {
              this->Cleanup();
              return this->freeCount > 0;
          }
      };
  #endif // ENABLE_WEAK_REFERENCE_REGIONS
  };
  