github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/utils/lru/lru.go (about) 1 package lru 2 3 import ( 4 "reflect" 5 "time" 6 7 "github.com/Asutorufa/yuhaiin/pkg/utils/synclist" 8 "github.com/Asutorufa/yuhaiin/pkg/utils/syncmap" 9 ) 10 11 type lruEntry[K, V any] struct { 12 key K 13 data V 14 expire time.Time 15 } 16 17 // LRU Least Recently Used 18 type LRU[K comparable, V any] struct { 19 capacity uint 20 list *synclist.SyncList[*lruEntry[K, V]] 21 mapping syncmap.SyncMap[K, *synclist.Element[*lruEntry[K, V]]] 22 reverseMapping syncmap.SyncMap[V, *synclist.Element[*lruEntry[K, V]]] 23 valueComparable bool 24 timeout time.Duration 25 26 lastPopEntry *lruEntry[K, V] 27 onRemove func(K, V) 28 } 29 type Option[K comparable, V any] func(*LRU[K, V]) 30 31 func WithOnRemove[K comparable, V any](f func(K, V)) func(*LRU[K, V]) { 32 return func(l *LRU[K, V]) { 33 l.onRemove = f 34 } 35 } 36 37 func WithExpireTimeout[K comparable, V any](t time.Duration) func(*LRU[K, V]) { 38 return func(l *LRU[K, V]) { 39 l.timeout = t 40 } 41 } 42 43 func WithCapacity[K comparable, V any](capacity uint) func(*LRU[K, V]) { 44 return func(l *LRU[K, V]) { 45 l.capacity = capacity 46 } 47 } 48 49 // New create new lru cache 50 func New[K comparable, V any](options ...Option[K, V]) *LRU[K, V] { 51 l := &LRU[K, V]{ 52 list: synclist.New[*lruEntry[K, V]](), 53 } 54 55 for _, o := range options { 56 o(l) 57 } 58 59 var t V 60 if tp := reflect.TypeOf(t); tp != nil { 61 l.valueComparable = tp.Comparable() 62 } 63 64 return l 65 } 66 67 func (l *LRU[K, V]) store(v *lruEntry[K, V], le *synclist.Element[*lruEntry[K, V]]) { 68 l.mapping.Store(v.key, le) 69 if l.valueComparable { 70 l.reverseMapping.Store(v.data, le) 71 } 72 } 73 74 func (l *LRU[K, V]) delete(v *lruEntry[K, V]) { 75 l.mapping.Delete(v.key) 76 if l.valueComparable { 77 l.reverseMapping.Delete(v.data) 78 } 79 if l.onRemove != nil { 80 l.onRemove(v.key, v.data) 81 } 82 } 83 84 type addOptions struct { 85 expireTime time.Time 86 } 87 88 type AddOption func(*addOptions) 89 90 func WithExpireTimeUnix(t time.Time) AddOption { 91 return func(o *addOptions) { 92 o.expireTime = t 93 } 94 } 95 96 func (l *LRU[K, V]) Add(key K, value V, opts ...AddOption) { 97 o := &addOptions{} 98 for _, z := range opts { 99 z(o) 100 } 101 102 if l.timeout != 0 && o.expireTime.IsZero() { 103 o.expireTime = time.Now().Add(l.timeout) 104 } 105 106 entry := &lruEntry[K, V]{key, value, o.expireTime} 107 108 if elem, ok := l.mapping.Load(key); ok { 109 l.list.MoveToFront(elem.SetValue(entry)) 110 return 111 } 112 113 var elem *synclist.Element[*lruEntry[K, V]] 114 115 if l.capacity == 0 || uint(l.list.Len()) < l.capacity { 116 elem = l.list.PushFront(entry) 117 } else { 118 elem = l.list.Back() 119 l.lastPopEntry = &lruEntry[K, V]{ 120 key: elem.Value.key, 121 data: elem.Value.data, 122 expire: elem.Value.expire, 123 } 124 l.delete(elem.Value) 125 l.list.MoveToFront(elem.SetValue(entry)) 126 } 127 128 l.store(entry, elem) 129 } 130 131 // Delete delete a key from cache 132 func (l *LRU[K, V]) Delete(key K) { 133 v, ok := l.mapping.LoadAndDelete(key) 134 if ok { 135 l.delete(v.Value) 136 } 137 } 138 139 func (l *LRU[K, V]) load(e *synclist.Element[*lruEntry[K, V]]) *lruEntry[K, V] { 140 if l.timeout != 0 && time.Now().After(e.Value.expire) { 141 l.delete(e.Value) 142 l.list.Remove(e) 143 return nil 144 } 145 146 l.list.MoveToFront(e) 147 return e.Value 148 } 149 150 func (l *LRU[K, V]) Load(key K) (v V, ok bool) { 151 v, _, ok = l.LoadExpireTime(key) 152 return 153 } 154 155 func (l *LRU[K, V]) LoadExpireTime(key K) (v V, expireTime time.Time, ok bool) { 156 node, ok := l.mapping.Load(key) 157 if !ok { 158 return v, expireTime, false 159 } 160 161 if z := l.load(node); z != nil { 162 return z.data, z.expire, true 163 } 164 165 return v, expireTime, false 166 } 167 168 func (l *LRU[K, V]) ReverseLoad(v V) (k K, ok bool) { 169 if !l.valueComparable { 170 return k, false 171 } 172 173 node, ok := l.reverseMapping.Load(v) 174 if !ok { 175 return k, false 176 } 177 178 if z := l.load(node); z != nil { 179 return z.key, true 180 } 181 182 return k, false 183 } 184 185 func (l *LRU[K, V]) ValueExist(key V) bool { 186 if !l.valueComparable { 187 return false 188 } 189 _, ok := l.reverseMapping.Load(key) 190 return ok 191 } 192 193 func (l *LRU[K, V]) LastPopValue() (v V, _ bool) { 194 if l.lastPopEntry == nil { 195 return v, false 196 } 197 198 return l.lastPopEntry.data, true 199 } 200 201 func (l *LRU[K, V]) Range(ranger func(K, V)) { 202 l.mapping.Range(func(key K, value *synclist.Element[*lruEntry[K, V]]) bool { 203 ranger(key, value.Value.data) 204 return true 205 }) 206 }