MruDictionary.h 7.1 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>
  25. {
  26. TValue value;
  27. TKey key;
  28. 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. MruListEntry *entry;
  39. TValue 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. const int mruListCapacity;
  75. int mruListCount;
  76. DoublyLinkedList<MruListEntry> entries;
  77. typedef
  78. BaseDictionary<
  79. TKey,
  80. MruDictionaryData,
  81. // MruDictionaryData always has pointer to GC pointer (MruEntry)
  82. typename ForceNonLeafAllocator<TAllocator>::AllocatorType,
  83. TSizePolicy,
  84. TComparer,
  85. TDictionaryEntry>
  86. TDictionary;
  87. TDictionary dictionary;
  88. typedef typename TDictionary::AllocatorType AllocatorType;
  89. public:
  90. MruDictionary(AllocatorType *const allocator, const int mruListCapacity)
  91. : mruListCapacity(mruListCapacity), mruListCount(0), dictionary(allocator)
  92. {
  93. Assert(allocator);
  94. Assert(mruListCapacity > 0);
  95. }
  96. static MruDictionary *New(TAllocator *const allocator, const int mruListCapacity)
  97. {
  98. return AllocatorNew(TAllocator, allocator, MruDictionary, allocator, mruListCapacity);
  99. }
  100. private:
  101. void AddToDictionary(MruListEntry *const entry)
  102. {
  103. const auto dictionaryDataIndex = dictionary.Add(entry->key, MruDictionaryData());
  104. dictionary.GetReferenceAt(dictionaryDataIndex)->OnAddedToMruList(entry);
  105. entry->dictionaryDataIndex = dictionaryDataIndex;
  106. }
  107. void ReuseLeastRecentlyUsedEntry(const TKey &key, const TValue &value, const int dictionaryDataIndex)
  108. {
  109. Assert(mruListCount == mruListCapacity);
  110. // Reuse the least recently used entry for this key/value pair and make it the most recently used
  111. const auto entry = entries.Tail();
  112. dictionary.GetReferenceAt(dictionaryDataIndex)->OnAddedToMruList(entry);
  113. dictionary.GetReferenceAt(entry->dictionaryDataIndex)->OnRemovedFromMruList();
  114. entries.MoveToBeginning(entry);
  115. entry->key = key;
  116. entry->value = value;
  117. entry->dictionaryDataIndex = dictionaryDataIndex;
  118. }
  119. public:
  120. bool TryGetValue(const TKey &key, TValue *const value)
  121. {
  122. MruDictionaryData *dictionaryData;
  123. int dictionaryDataIndex;
  124. if(!dictionary.TryGetReference(key, &dictionaryData, &dictionaryDataIndex))
  125. return false;
  126. const auto entry = dictionaryData->Entry();
  127. if(entry)
  128. {
  129. // Make this the most recently used entry
  130. entries.MoveToBeginning(entry);
  131. *value = entry->value;
  132. return true;
  133. }
  134. *value = dictionaryData->Value();
  135. // The key passed into this function may be temporary, and should not be placed in the MRU list or dictionary. Get
  136. // the proper key to be used from the dictionary. That key should have the necessary lifetime.
  137. ReuseLeastRecentlyUsedEntry(dictionary.GetKeyAt(dictionaryDataIndex), dictionaryData->Value(), dictionaryDataIndex);
  138. return true;
  139. }
  140. void Add(const TKey &key, const TValue &value)
  141. {
  142. Assert(!dictionary.ContainsKey(key));
  143. Assert(mruListCount <= mruListCapacity);
  144. if(mruListCount == mruListCapacity)
  145. {
  146. ReuseLeastRecentlyUsedEntry(key, value, dictionary.Add(key, MruDictionaryData()));
  147. return;
  148. }
  149. const auto entry = AllocatorNew(TAllocator, dictionary.GetAllocator(), MruListEntry, key, value);
  150. AddToDictionary(entry);
  151. entries.LinkToBeginning(entry);
  152. ++mruListCount;
  153. }
  154. void RemoveRecentlyUnusedItems()
  155. {
  156. if(dictionary.Count() == mruListCount)
  157. return;
  158. if(dictionary.Count() / 2 <= mruListCount)
  159. {
  160. dictionary.MapAndRemoveIf(
  161. [](const TDictionary::EntryType &dictionaryEntry) -> bool
  162. {
  163. return !dictionaryEntry.Value().Entry();
  164. });
  165. return;
  166. }
  167. dictionary.Clear();
  168. for(auto entry = entries.Head(); entry; entry = entry->Next())
  169. AddToDictionary(entry);
  170. }
  171. PREVENT_COPY(MruDictionary);
  172. };
  173. }