Skip to content

Commit

Permalink
Add support for generics
Browse files Browse the repository at this point in the history
  • Loading branch information
Milad Taghavi committed Mar 16, 2022
1 parent 05b1760 commit 6cd15ce
Showing 1 changed file with 35 additions and 35 deletions.
70 changes: 35 additions & 35 deletions lru.go
Expand Up @@ -10,47 +10,47 @@ import "sync"
// get automatically evicted.
const DefaultSize = 256

type lruItem struct {
key interface{} // user-defined key
value interface{} // user-defined value
prev *lruItem // prev item in list. More recently used
next *lruItem // next item in list. Less recently used
type lruItem[Key comparable, Value any] struct {
key Key // user-defined key
value Value // user-defined value
prev *lruItem[Key, Value] // prev item in list. More recently used
next *lruItem[Key, Value] // next item in list. Less recently used
}

// LRU implements an LRU cache
type LRU struct {
mu sync.RWMutex // protect all things
size int // max number of items.
items map[interface{}]*lruItem // active items
head *lruItem // head of list
tail *lruItem // tail of list
type LRU[Key comparable, Value any] struct {
mu sync.RWMutex // protect all things
size int // max number of items.
items map[Key]*lruItem[Key, Value] // active items
head *lruItem[Key, Value] // head of list
tail *lruItem[Key, Value] // tail of list
}

//go:noinline
func (lru *LRU) init() {
lru.items = make(map[interface{}]*lruItem)
lru.head = new(lruItem)
lru.tail = new(lruItem)
func (lru *LRU[Key, Value]) init() {
lru.items = make(map[Key]*lruItem[Key, Value])
lru.head = new(lruItem[Key, Value])
lru.tail = new(lruItem[Key, Value])
lru.head.next = lru.tail
lru.tail.prev = lru.head
if lru.size == 0 {
lru.size = DefaultSize
}
}

func (lru *LRU) evict() *lruItem {
func (lru *LRU[Key, Value]) evict() *lruItem[Key, Value] {
item := lru.tail.prev
lru.pop(item)
delete(lru.items, item.key)
return item
}

func (lru *LRU) pop(item *lruItem) {
func (lru *LRU[Key, Value]) pop(item *lruItem[Key, Value]) {
item.prev.next = item.next
item.next.prev = item.prev
}

func (lru *LRU) push(item *lruItem) {
func (lru *LRU[Key, Value]) push(item *lruItem[Key, Value]) {
lru.head.next.prev = item
item.next = lru.head.next
item.prev = lru.head
Expand All @@ -61,8 +61,8 @@ func (lru *LRU) push(item *lruItem) {
// the number of items currently in the cache, then items will be evicted.
// Returns evicted items.
// This operation will panic if the size is less than one.
func (lru *LRU) Resize(size int) (evictedKeys []interface{},
evictedValues []interface{}) {
func (lru *LRU[Key, Value]) Resize(size int) (evictedKeys []Key,
evictedValues []Value) {
if size <= 0 {
panic("invalid size")
}
Expand All @@ -79,17 +79,17 @@ func (lru *LRU) Resize(size int) (evictedKeys []interface{},
}

// Len returns the length of the lru cache
func (lru *LRU) Len() int {
func (lru *LRU[Key, Value]) Len() int {
lru.mu.RLock()
defer lru.mu.RUnlock()
return len(lru.items)
}

// SetEvicted sets or replaces a value for a key. If this operation causes an
// eviction then the evicted item is returned.
func (lru *LRU) SetEvicted(key interface{}, value interface{}) (
prev interface{}, replaced bool, evictedKey interface{},
evictedValue interface{}, evicted bool) {
func (lru *LRU[Key, Value]) SetEvicted(key Key, value Value) (
prev Value, replaced bool, evictedKey Key,
evictedValue Value, evicted bool) {
lru.mu.Lock()
defer lru.mu.Unlock()
if lru.items == nil {
Expand All @@ -101,7 +101,7 @@ func (lru *LRU) SetEvicted(key interface{}, value interface{}) (
item = lru.evict()
evictedKey, evictedValue, evicted = item.key, item.value, true
} else {
item = new(lruItem)
item = new(lruItem[Key, Value])
}
item.key = key
item.value = value
Expand All @@ -119,19 +119,19 @@ func (lru *LRU) SetEvicted(key interface{}, value interface{}) (
}

// Set or replace a value for a key.
func (lru *LRU) Set(key interface{}, value interface{}) (prev interface{},
func (lru *LRU[Key, Value]) Set(key Key, value Value) (prev Value,
replaced bool) {
prev, replaced, _, _, _ = lru.SetEvicted(key, value)
return prev, replaced
}

// Get a value for key
func (lru *LRU) Get(key interface{}) (value interface{}, ok bool) {
func (lru *LRU[Key, Value]) Get(key Key) (value Value, ok bool) {
lru.mu.Lock()
defer lru.mu.Unlock()
item := lru.items[key]
if item == nil {
return nil, false
return
}
if lru.head.next != item {
lru.pop(item)
Expand All @@ -141,7 +141,7 @@ func (lru *LRU) Get(key interface{}) (value interface{}, ok bool) {
}

// Contains returns true if the key exists.
func (lru *LRU) Contains(key interface{}) bool {
func (lru *LRU[Key, Value]) Contains(key Key) bool {
lru.mu.RLock()
defer lru.mu.RUnlock()
_, ok := lru.items[key]
Expand All @@ -150,23 +150,23 @@ func (lru *LRU) Contains(key interface{}) bool {

// Peek returns the value for key value without updating
// the recently used status.
func (lru *LRU) Peek(key interface{}) (value interface{}, ok bool) {
func (lru *LRU[Key, Value]) Peek(key Key) (value Value, ok bool) {
lru.mu.RLock()
defer lru.mu.RUnlock()

if item := lru.items[key]; item != nil {
return item.value, true
}
return nil, false
return
}

// Delete a value for a key
func (lru *LRU) Delete(key interface{}) (prev interface{}, deleted bool) {
func (lru *LRU[Key, Value]) Delete(key Key) (prev Value, deleted bool) {
lru.mu.Lock()
defer lru.mu.Unlock()
item := lru.items[key]
if item == nil {
return nil, false
return
}
delete(lru.items, key)
lru.pop(item)
Expand All @@ -176,7 +176,7 @@ func (lru *LRU) Delete(key interface{}) (prev interface{}, deleted bool) {
// Range iterates over all key/values in the order of most recently to
// least recently used items.
// It's not safe to call other LRU operations while ranging.
func (lru *LRU) Range(iter func(key interface{}, value interface{}) bool) {
func (lru *LRU[Key, Value]) Range(iter func(key Key, value Value) bool) {
lru.mu.RLock()
defer lru.mu.RUnlock()
if head := lru.head; head != nil {
Expand All @@ -193,7 +193,7 @@ func (lru *LRU) Range(iter func(key interface{}, value interface{}) bool) {
// Reverse iterates over all key/values in the order of least recently to
// most recently used items.
// It's not safe to call other LRU operations while ranging.
func (lru *LRU) Reverse(iter func(key interface{}, value interface{}) bool) {
func (lru *LRU[Key, Value]) Reverse(iter func(key Key, value Value) bool) {
lru.mu.RLock()
defer lru.mu.RUnlock()
if tail := lru.tail; tail != nil {
Expand Down

0 comments on commit 6cd15ce

Please sign in to comment.