This commit is contained in:
世界 2022-05-03 16:23:34 +08:00
parent fb8de18b9d
commit d42354ab19
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
19 changed files with 159 additions and 293 deletions

View file

@ -9,19 +9,58 @@ import (
"github.com/sagernet/sing/common/list"
)
type Option[K comparable, V any] func(*LruCache[K, V])
type EvictCallback[K comparable, V any] func(key K, value V)
func WithEvict[K comparable, V any](cb EvictCallback[K, V]) Option[K, V] {
return func(l *LruCache[K, V]) {
l.onEvict = cb
}
}
func WithUpdateAgeOnGet[K comparable, V any]() Option[K, V] {
return func(l *LruCache[K, V]) {
l.updateAgeOnGet = true
}
}
func WithAge[K comparable, V any](maxAge int64) Option[K, V] {
return func(l *LruCache[K, V]) {
l.maxAge = maxAge
}
}
func WithSize[K comparable, V any](maxSize int) Option[K, V] {
return func(l *LruCache[K, V]) {
l.maxSize = maxSize
}
}
func WithStale[K comparable, V any](stale bool) Option[K, V] {
return func(l *LruCache[K, V]) {
l.staleReturn = stale
}
}
type LruCache[K comparable, V any] struct {
maxAge int64
maxSize int
mu sync.Mutex
cache map[K]*list.Element[*entry[K, V]]
lru list.List[*entry[K, V]] // Front is least-recent
updateAgeOnGet bool
staleReturn bool
onEvict EvictCallback[K, V]
}
func NewLRU[K comparable, V any](maxAge int64, updateAgeOnGet bool) LruCache[K, V] {
lc := LruCache[K, V]{
maxAge: maxAge,
updateAgeOnGet: updateAgeOnGet,
cache: make(map[K]*list.Element[*entry[K, V]]),
func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
lc := &LruCache[K, V]{
cache: make(map[K]*list.Element[*entry[K, V]]),
}
for _, option := range options {
option(lc)
}
return lc
@ -111,6 +150,12 @@ func (c *LruCache[K, V]) StoreWithExpire(key K, value V, expires time.Time) {
} else {
e := &entry[K, V]{key: key, value: value, expires: expires.Unix()}
c.cache[key] = c.lru.PushBack(e)
if c.maxSize > 0 {
if len := c.lru.Len(); len > c.maxSize {
c.deleteElement(c.lru.Front())
}
}
}
c.maybeDeleteOldest()
@ -141,7 +186,7 @@ func (c *LruCache[K, V]) get(key K) *entry[K, V] {
return nil
}
if c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {
if !c.staleReturn && c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {
c.deleteElement(le)
c.maybeDeleteOldest()
@ -168,9 +213,11 @@ func (c *LruCache[K, V]) Delete(key K) {
}
func (c *LruCache[K, V]) maybeDeleteOldest() {
now := time.Now().Unix()
for le := c.lru.Front(); le != nil && le.Value.expires <= now; le = c.lru.Front() {
c.deleteElement(le)
if !c.staleReturn && c.maxAge > 0 {
now := time.Now().Unix()
for le := c.lru.Front(); le != nil && le.Value.expires <= now; le = c.lru.Front() {
c.deleteElement(le)
}
}
}
@ -178,6 +225,9 @@ func (c *LruCache[K, V]) deleteElement(le *list.Element[*entry[K, V]]) {
c.lru.Remove(le)
e := le.Value
delete(c.cache, e.key)
if c.onEvict != nil {
c.onEvict(e.key, e.value)
}
}
type entry[K comparable, V any] struct {