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  }