simple_lru.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package lru
  2. import (
  3. "container/list"
  4. "fmt"
  5. )
  6. // EvictCallback is used to get a callback when a cache entry is evicted
  7. type EvictCallback func(key any, value any)
  8. // SimpleLRU implements a non-thread safe fixed size LRU cache
  9. type SimpleLRU struct {
  10. size int
  11. evictList *list.List
  12. items map[any]*list.Element
  13. onEvict EvictCallback
  14. }
  15. // entry is used to hold a value in the evictList
  16. type entry struct {
  17. key any
  18. value any
  19. }
  20. func New(size int) LRU {
  21. return NewSimple(size, nil)
  22. }
  23. // NewSimple constructs an LRU of the given size
  24. func NewSimple(size int, onEvict EvictCallback) LRU {
  25. if size < 1 {
  26. panic(fmt.Errorf("must provide a positive size. size = %d", size))
  27. }
  28. c := &SimpleLRU{
  29. size: size,
  30. evictList: list.New(),
  31. items: make(map[any]*list.Element),
  32. onEvict: onEvict,
  33. }
  34. return c
  35. }
  36. // Purge is used to completely clear the cache.
  37. func (c *SimpleLRU) Purge() {
  38. for k, v := range c.items {
  39. if c.onEvict != nil {
  40. c.onEvict(k, v.Value.(*entry).value)
  41. }
  42. delete(c.items, k)
  43. }
  44. c.evictList.Init()
  45. }
  46. // Add adds a value to the cache. Returns true if an eviction occurred.
  47. func (c *SimpleLRU) Add(key, value any) (evicted bool) {
  48. // Check for existing item
  49. if ent, ok := c.items[key]; ok {
  50. c.evictList.MoveToFront(ent)
  51. ent.Value.(*entry).value = value
  52. return false
  53. }
  54. // NewActor new item
  55. ent := &entry{key, value}
  56. frontEntry := c.evictList.PushFront(ent)
  57. c.items[key] = frontEntry
  58. evict := c.evictList.Len() > c.size
  59. // Verify size not exceeded
  60. if evict {
  61. c.removeOldest()
  62. }
  63. return evict
  64. }
  65. // Get looks up a key's value from the cache.
  66. func (c *SimpleLRU) Get(key any) (value any, ok bool) {
  67. if ent, ok := c.items[key]; ok {
  68. c.evictList.MoveToFront(ent)
  69. if ent.Value.(*entry) == nil {
  70. return nil, false
  71. }
  72. return ent.Value.(*entry).value, true
  73. }
  74. return
  75. }
  76. // Contains checks if a key is in the cache, without updating the recent-ness
  77. // or deleting it for being stale.
  78. func (c *SimpleLRU) Contains(key any) (ok bool) {
  79. _, ok = c.items[key]
  80. return ok
  81. }
  82. // Peek returns the key value (or undefined if not found) without updating
  83. // the "recently used"-ness of the key.
  84. func (c *SimpleLRU) Peek(key any) (value any, ok bool) {
  85. var ent *list.Element
  86. if ent, ok = c.items[key]; ok {
  87. return ent.Value.(*entry).value, true
  88. }
  89. return nil, ok
  90. }
  91. // Remove removes the provided key from the cache, returning if the
  92. // key was contained.
  93. func (c *SimpleLRU) Remove(key any) (present bool) {
  94. if ent, ok := c.items[key]; ok {
  95. c.removeElement(ent)
  96. return true
  97. }
  98. return false
  99. }
  100. // RemoveOldest removes the oldest item from the cache.
  101. func (c *SimpleLRU) RemoveOldest() (key any, value any, ok bool) {
  102. ent := c.evictList.Back()
  103. if ent != nil {
  104. c.removeElement(ent)
  105. kv := ent.Value.(*entry)
  106. return kv.key, kv.value, true
  107. }
  108. return nil, nil, false
  109. }
  110. // GetOldest returns the oldest entry
  111. func (c *SimpleLRU) GetOldest() (key any, value any, ok bool) {
  112. ent := c.evictList.Back()
  113. if ent != nil {
  114. kv := ent.Value.(*entry)
  115. return kv.key, kv.value, true
  116. }
  117. return nil, nil, false
  118. }
  119. // Keys returns a slice of the keys in the cache, from oldest to newest.
  120. func (c *SimpleLRU) Keys() []any {
  121. keys := make([]any, len(c.items))
  122. i := 0
  123. for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
  124. keys[i] = ent.Value.(*entry).key
  125. i++
  126. }
  127. return keys
  128. }
  129. func (c *SimpleLRU) Values() []any {
  130. values := make([]any, len(c.items))
  131. i := 0
  132. for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
  133. values[i] = ent.Value.(*entry).value
  134. i++
  135. }
  136. return values
  137. }
  138. // Len returns the number of items in the cache.
  139. func (c *SimpleLRU) Len() int {
  140. return c.evictList.Len()
  141. }
  142. // Resize changes the cache size.
  143. func (c *SimpleLRU) Resize(size int) (evicted int) {
  144. diff := c.Len() - size
  145. if diff < 0 {
  146. diff = 0
  147. }
  148. for i := 0; i < diff; i++ {
  149. c.removeOldest()
  150. }
  151. c.size = size
  152. return diff
  153. }
  154. // removeOldest removes the oldest item from the cache.
  155. func (c *SimpleLRU) removeOldest() {
  156. ent := c.evictList.Back()
  157. if ent != nil {
  158. c.removeElement(ent)
  159. }
  160. }
  161. // removeElement is used to remove a given list element from the cache
  162. func (c *SimpleLRU) removeElement(e *list.Element) {
  163. c.evictList.Remove(e)
  164. kv := e.Value.(*entry)
  165. delete(c.items, kv.key)
  166. if c.onEvict != nil {
  167. c.onEvict(kv.key, kv.value)
  168. }
  169. }