104 lines
1.4 KiB
Go
104 lines
1.4 KiB
Go
package xslices
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
func NewLRU[K comparable, T any](capacity int) *lru[K, T] {
|
|
return &lru[K, T]{
|
|
items: make(map[K]*lruNode[K, T], capacity),
|
|
capacity: capacity,
|
|
}
|
|
}
|
|
|
|
type lruNode[K comparable, T any] struct {
|
|
key K
|
|
value T
|
|
|
|
next *lruNode[K, T]
|
|
prev *lruNode[K, T]
|
|
}
|
|
|
|
type lru[K comparable, T any] struct {
|
|
items map[K]*lruNode[K, T]
|
|
length int
|
|
capacity int
|
|
|
|
first *lruNode[K, T]
|
|
last *lruNode[K, T]
|
|
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func (l *lru[K, T]) Push(key K, value T) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
node, ok := l.items[key]
|
|
if ok {
|
|
l.bumpUnsafe(node)
|
|
return
|
|
}
|
|
|
|
node = &lruNode[K, T]{
|
|
key: key,
|
|
value: value,
|
|
next: l.first,
|
|
}
|
|
|
|
if l.first != nil {
|
|
l.first.prev = node
|
|
}
|
|
if l.last == nil {
|
|
l.last = node
|
|
}
|
|
|
|
l.first = node
|
|
l.items[key] = node
|
|
|
|
if l.length == l.capacity && l.last != nil {
|
|
deletedNode := l.last
|
|
delete(l.items, deletedNode.key)
|
|
|
|
l.last = l.last.prev
|
|
return
|
|
}
|
|
|
|
l.length++
|
|
}
|
|
|
|
func (l *lru[K, T]) Get(key K) (T, bool) {
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
node, ok := l.items[key]
|
|
if !ok {
|
|
var t T
|
|
return t, false
|
|
}
|
|
|
|
out := node.value
|
|
l.bumpUnsafe(node)
|
|
|
|
return out, true
|
|
}
|
|
|
|
func (l *lru[K, T]) bumpUnsafe(node *lruNode[K, T]) {
|
|
if l.first == node {
|
|
return
|
|
}
|
|
|
|
if node.next != nil {
|
|
node.next.prev = node.prev
|
|
}
|
|
if node.prev != nil {
|
|
node.prev.next = node.next
|
|
}
|
|
|
|
node.next = l.first
|
|
l.first.prev = node
|
|
|
|
l.first = node
|
|
node.prev = nil
|
|
}
|