internal/lru/lru.go (49 lines of code) (raw):
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package lru implements a simple LRU cache.
package lru
import (
"container/list"
"sync"
)
// dataNode is a node in the LRU cache.
type dataNode struct {
Data any
KeyPtr *list.Element
}
// Handle is the actual LRU cache.
type Handle[K comparable] struct {
// mu protects the map and the ring.
mu sync.Mutex
// lookupTable is the map of key to node.
lookupTable map[K]*dataNode
// ring is the ring of elements in the cache.
ring *list.List
// capacity is the maximum number of elements in the cache.
capacity uint
}
// New creates a new LRU cache.
func New[K comparable](capacity uint) *Handle[K] {
return &Handle[K]{lookupTable: make(map[K]*dataNode), ring: list.New(), capacity: capacity}
}
// Get returns the value associated with the key and a boolean indicating if the
// key was found. If found the node is promoted to the front.
func (l *Handle[K]) Get(key K) (any, bool) {
l.mu.Lock()
defer l.mu.Unlock()
if node, ok := l.lookupTable[key]; ok {
l.ring.MoveToFront(node.KeyPtr)
return node.Data, true
}
var defaultVal any
return defaultVal, false
}
// Put puts the value associated with the key.
func (l *Handle[K]) Put(key K, value any) {
l.mu.Lock()
defer l.mu.Unlock()
if node, found := l.lookupTable[key]; found {
node.Data = value
l.lookupTable[key] = node
l.ring.MoveToFront(node.KeyPtr)
} else {
if l.capacity == uint(len(l.lookupTable)) {
back := l.ring.Back()
l.ring.Remove(back)
delete(l.lookupTable, back.Value.(K))
}
l.lookupTable[key] = &dataNode{Data: value, KeyPtr: l.ring.PushFront(key)}
}
}
// Len returns the number of elements in the cache.
func (l *Handle[K]) Len() uint {
l.mu.Lock()
defer l.mu.Unlock()
return uint(len(l.lookupTable))
}