MruCache.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.Runtime
  5. {
  6. using System.Collections.Generic;
  7. class MruCache<TKey, TValue>
  8. where TKey : class
  9. where TValue : class
  10. {
  11. LinkedList<TKey> mruList;
  12. Dictionary<TKey, CacheEntry> items;
  13. int lowWatermark;
  14. int highWatermark;
  15. CacheEntry mruEntry;
  16. public MruCache(int watermark)
  17. : this(watermark * 4 / 5, watermark)
  18. {
  19. }
  20. //
  21. // The cache will grow until the high watermark. At which point, the least recently used items
  22. // will be purge until the cache's size is reduced to low watermark
  23. //
  24. public MruCache(int lowWatermark, int highWatermark)
  25. : this(lowWatermark, highWatermark, null)
  26. {
  27. }
  28. public MruCache(int lowWatermark, int highWatermark, IEqualityComparer<TKey> comparer)
  29. {
  30. Fx.Assert(lowWatermark < highWatermark, "");
  31. Fx.Assert(lowWatermark >= 0, "");
  32. this.lowWatermark = lowWatermark;
  33. this.highWatermark = highWatermark;
  34. this.mruList = new LinkedList<TKey>();
  35. if (comparer == null)
  36. {
  37. this.items = new Dictionary<TKey, CacheEntry>();
  38. }
  39. else
  40. {
  41. this.items = new Dictionary<TKey, CacheEntry>(comparer);
  42. }
  43. }
  44. public int Count
  45. {
  46. get
  47. {
  48. return this.items.Count;
  49. }
  50. }
  51. public void Add(TKey key, TValue value)
  52. {
  53. Fx.Assert(null != key, "");
  54. // if anything goes wrong (duplicate entry, etc) we should
  55. // clear our caches so that we don't get out of [....]
  56. bool success = false;
  57. try
  58. {
  59. if (this.items.Count == this.highWatermark)
  60. {
  61. // If the cache is full, purge enough LRU items to shrink the
  62. // cache down to the low watermark
  63. int countToPurge = this.highWatermark - this.lowWatermark;
  64. for (int i = 0; i < countToPurge; i++)
  65. {
  66. TKey keyRemove = this.mruList.Last.Value;
  67. this.mruList.RemoveLast();
  68. TValue item = this.items[keyRemove].value;
  69. this.items.Remove(keyRemove);
  70. OnSingleItemRemoved(item);
  71. OnItemAgedOutOfCache(item);
  72. }
  73. }
  74. // Add the new entry to the cache and make it the MRU element
  75. CacheEntry entry;
  76. entry.node = this.mruList.AddFirst(key);
  77. entry.value = value;
  78. this.items.Add(key, entry);
  79. this.mruEntry = entry;
  80. success = true;
  81. }
  82. finally
  83. {
  84. if (!success)
  85. {
  86. this.Clear();
  87. }
  88. }
  89. }
  90. public void Clear()
  91. {
  92. this.mruList.Clear();
  93. this.items.Clear();
  94. this.mruEntry.value = null;
  95. this.mruEntry.node = null;
  96. }
  97. public bool Remove(TKey key)
  98. {
  99. Fx.Assert(null != key, "");
  100. CacheEntry entry;
  101. if (this.items.TryGetValue(key, out entry))
  102. {
  103. this.items.Remove(key);
  104. OnSingleItemRemoved(entry.value);
  105. this.mruList.Remove(entry.node);
  106. if (object.ReferenceEquals(this.mruEntry.node, entry.node))
  107. {
  108. this.mruEntry.value = null;
  109. this.mruEntry.node = null;
  110. }
  111. return true;
  112. }
  113. return false;
  114. }
  115. protected virtual void OnSingleItemRemoved(TValue item)
  116. {
  117. }
  118. protected virtual void OnItemAgedOutOfCache(TValue item)
  119. {
  120. }
  121. //
  122. // If found, make the entry most recently used
  123. //
  124. public bool TryGetValue(TKey key, out TValue value)
  125. {
  126. // first check our MRU item
  127. if (this.mruEntry.node != null && key != null && key.Equals(this.mruEntry.node.Value))
  128. {
  129. value = this.mruEntry.value;
  130. return true;
  131. }
  132. CacheEntry entry;
  133. bool found = this.items.TryGetValue(key, out entry);
  134. value = entry.value;
  135. // Move the node to the head of the MRU list if it's not already there
  136. if (found && this.mruList.Count > 1
  137. && !object.ReferenceEquals(this.mruList.First, entry.node))
  138. {
  139. this.mruList.Remove(entry.node);
  140. this.mruList.AddFirst(entry.node);
  141. this.mruEntry = entry;
  142. }
  143. return found;
  144. }
  145. struct CacheEntry
  146. {
  147. internal TValue value;
  148. internal LinkedListNode<TKey> node;
  149. }
  150. }
  151. }