MruDictionary.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. namespace JsUtil
  7. {
  8. // - Maintains an MRU linked list of fixed maximum length and a dictionary that contains all added values
  9. // - RemoveRecentlyUnusedItems should be called to remove values that are not in the MRU list from the dictionary
  10. // - Added values are linked to the beginning of the MRU list (most recently used) as part of an MruListEntry, and when the
  11. // list is full, entries from the end of the list (least recently used) are reused for the new values
  12. // - TryGetValue, if the key exists in the dictionary, adds the value to the MRU list as the most recently used value, and
  13. // removes the least recently used value as necessary
  14. template<
  15. class TKey,
  16. class TValue,
  17. class TAllocator = Recycler,
  18. class TSizePolicy = PowerOf2SizePolicy,
  19. template<class ValueOrKey> class TComparer = DefaultComparer,
  20. template<class K, class V> class TDictionaryEntry = SimpleDictionaryEntry>
  21. class MruDictionary
  22. {
  23. private:
  24. struct MruListEntry : public DoublyLinkedListElement<MruListEntry, TAllocator>
  25. {
  26. Field(TValue, TAllocator) value;
  27. Field(TKey, TAllocator) key;
  28. Field(int) dictionaryDataIndex;
  29. MruListEntry(const TKey &key, const TValue &value) : key(key), value(value), dictionaryDataIndex(0)
  30. {
  31. }
  32. PREVENT_COPY(MruListEntry);
  33. };
  34. public:
  35. class MruDictionaryData
  36. {
  37. private:
  38. Field(MruListEntry *, TAllocator) entry;
  39. Field(TValue, TAllocator) value;
  40. public:
  41. MruDictionaryData() : entry(nullptr)
  42. {
  43. }
  44. MruDictionaryData &operator =(const void *const nullValue)
  45. {
  46. // Needed to support KeyValueEntry::Clear for dictionaries
  47. Assert(!nullValue);
  48. entry = nullptr;
  49. value = nullptr; // TValue must also support this for the same reason
  50. return *this;
  51. }
  52. MruListEntry *Entry() const
  53. {
  54. return entry;
  55. }
  56. const TValue &Value() const
  57. {
  58. Assert(!entry);
  59. return value;
  60. }
  61. void OnAddedToMruList(MruListEntry *const entry)
  62. {
  63. Assert(!this->entry);
  64. this->entry = entry;
  65. }
  66. void OnRemovedFromMruList()
  67. {
  68. Assert(entry);
  69. value = entry->value;
  70. entry = nullptr;
  71. }
  72. };
  73. private:
  74. Field(const int) mruListCapacity;
  75. Field(int) mruListCount;
  76. typedef DoublyLinkedList<MruListEntry, TAllocator> EntriesType;
  77. Field(EntriesType) entries;
  78. typedef
  79. BaseDictionary<
  80. TKey,
  81. MruDictionaryData,
  82. // MruDictionaryData always has pointer to GC pointer (MruEntry)
  83. typename ForceNonLeafAllocator<TAllocator>::AllocatorType,
  84. TSizePolicy,
  85. TComparer,
  86. TDictionaryEntry>
  87. TDictionary;
  88. TDictionary dictionary;
  89. typedef typename TDictionary::AllocatorType AllocatorType;
  90. public:
  91. MruDictionary(AllocatorType *const allocator, const int mruListCapacity)
  92. : mruListCapacity(mruListCapacity), mruListCount(0), dictionary(allocator)
  93. {
  94. Assert(allocator);
  95. Assert(mruListCapacity > 0);
  96. }
  97. static MruDictionary *New(TAllocator *const allocator, DECLSPEC_GUARD_OVERFLOW const int mruListCapacity)
  98. {
  99. return AllocatorNew(TAllocator, allocator, MruDictionary, allocator, mruListCapacity);
  100. }
  101. private:
  102. void AddToDictionary(MruListEntry *const entry)
  103. {
  104. const auto dictionaryDataIndex = dictionary.Add(entry->key, MruDictionaryData());
  105. dictionary.GetReferenceAt(dictionaryDataIndex)->OnAddedToMruList(entry);
  106. entry->dictionaryDataIndex = dictionaryDataIndex;
  107. }
  108. void ReuseLeastRecentlyUsedEntry(const TKey &key, const TValue &value, const int dictionaryDataIndex)
  109. {
  110. Assert(mruListCount == mruListCapacity);
  111. // Reuse the least recently used entry for this key/value pair and make it the most recently used
  112. const auto entry = entries.Tail();
  113. dictionary.GetReferenceAt(dictionaryDataIndex)->OnAddedToMruList(entry);
  114. dictionary.GetReferenceAt(entry->dictionaryDataIndex)->OnRemovedFromMruList();
  115. entries.MoveToBeginning(entry);
  116. entry->key = key;
  117. entry->value = value;
  118. entry->dictionaryDataIndex = dictionaryDataIndex;
  119. }
  120. public:
  121. bool TryGetValue(const TKey &key, TValue *const value)
  122. {
  123. MruDictionaryData *dictionaryData;
  124. int dictionaryDataIndex;
  125. if(!dictionary.TryGetReference(key, &dictionaryData, &dictionaryDataIndex))
  126. return false;
  127. const auto entry = dictionaryData->Entry();
  128. if(entry)
  129. {
  130. // Make this the most recently used entry
  131. entries.MoveToBeginning(entry);
  132. *value = entry->value;
  133. return true;
  134. }
  135. *value = dictionaryData->Value();
  136. // The key passed into this function may be temporary, and should not be placed in the MRU list or dictionary. Get
  137. // the proper key to be used from the dictionary. That key should have the necessary lifetime.
  138. ReuseLeastRecentlyUsedEntry(dictionary.GetKeyAt(dictionaryDataIndex), dictionaryData->Value(), dictionaryDataIndex);
  139. return true;
  140. }
  141. void Add(const TKey &key, const TValue &value)
  142. {
  143. Assert(!dictionary.ContainsKey(key));
  144. Assert(mruListCount <= mruListCapacity);
  145. if(mruListCount == mruListCapacity)
  146. {
  147. ReuseLeastRecentlyUsedEntry(key, value, dictionary.Add(key, MruDictionaryData()));
  148. return;
  149. }
  150. const auto entry = AllocatorNew(TAllocator, dictionary.GetAllocator(), MruListEntry, key, value);
  151. AddToDictionary(entry);
  152. entries.LinkToBeginning(entry);
  153. ++mruListCount;
  154. }
  155. void RemoveRecentlyUnusedItems()
  156. {
  157. if(dictionary.Count() == mruListCount)
  158. return;
  159. if(dictionary.Count() / 2 <= mruListCount)
  160. {
  161. dictionary.MapAndRemoveIf(
  162. [](const typename TDictionary::EntryType &dictionaryEntry) -> bool
  163. {
  164. return !dictionaryEntry.Value().Entry();
  165. });
  166. return;
  167. }
  168. dictionary.Clear();
  169. for(auto entry = entries.Head(); entry; entry = entry->Next())
  170. AddToDictionary(entry);
  171. }
  172. PREVENT_COPY(MruDictionary);
  173. };
  174. }