mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-23 12:16:38 +00:00
154 lines
3.5 KiB
Go
154 lines
3.5 KiB
Go
|
package maps
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
// LRU provides an ordered hashmap implementation that keeps elements ordered according to last recently used (hence, LRU).
|
||
|
type LRUMap[K comparable, V any] struct {
|
||
|
ordered[K, V]
|
||
|
size int
|
||
|
}
|
||
|
|
||
|
// NewLRU returns a new instance of LRUMap with given initializing length and maximum capacity.
|
||
|
func NewLRU[K comparable, V any](len, cap int) *LRUMap[K, V] {
|
||
|
m := new(LRUMap[K, V])
|
||
|
m.Init(len, cap)
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// Init will initialize this map with initial length and maximum capacity.
|
||
|
func (m *LRUMap[K, V]) Init(len, cap int) {
|
||
|
if cap <= 0 {
|
||
|
panic("lru cap must be greater than zero")
|
||
|
} else if m.pool != nil {
|
||
|
panic("lru map already initialized")
|
||
|
}
|
||
|
m.ordered.hmap = make(map[K]*elem[K, V], len)
|
||
|
m.ordered.pool = allocElems[K, V](len)
|
||
|
m.size = cap
|
||
|
}
|
||
|
|
||
|
// Get will fetch value for given key from map, in the process pushing it to the front of the map. Returns false if not found.
|
||
|
func (m *LRUMap[K, V]) Get(key K) (V, bool) {
|
||
|
if elem, ok := m.hmap[key]; ok {
|
||
|
// Ensure safe
|
||
|
m.write_check()
|
||
|
|
||
|
// Unlink elem from list
|
||
|
m.list.Unlink(elem)
|
||
|
|
||
|
// Push to front of list
|
||
|
m.list.PushFront(elem)
|
||
|
|
||
|
return elem.V, true
|
||
|
}
|
||
|
var z V // zero value
|
||
|
return z, false
|
||
|
}
|
||
|
|
||
|
// Add will add the given key-value pair to the map, pushing them to the front of the map. Returns false if already exists. Evicts old at maximum capacity.
|
||
|
func (m *LRUMap[K, V]) Add(key K, value V) bool {
|
||
|
return m.AddWithHook(key, value, nil)
|
||
|
}
|
||
|
|
||
|
// AddWithHook performs .Add() but passing any evicted entry to given hook function.
|
||
|
func (m *LRUMap[K, V]) AddWithHook(key K, value V, evict func(K, V)) bool {
|
||
|
// Ensure safe
|
||
|
m.write_check()
|
||
|
|
||
|
// Look for existing elem
|
||
|
elem, ok := m.hmap[key]
|
||
|
if ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if m.list.len >= m.size {
|
||
|
// We're at capacity, sir!
|
||
|
// Pop current tail elem
|
||
|
elem = m.list.PopTail()
|
||
|
|
||
|
if evict != nil {
|
||
|
// Pass to evict hook
|
||
|
evict(elem.K, elem.V)
|
||
|
}
|
||
|
|
||
|
// Delete key from map
|
||
|
delete(m.hmap, elem.K)
|
||
|
} else {
|
||
|
// Allocate elem
|
||
|
elem = m.alloc()
|
||
|
}
|
||
|
|
||
|
// Set elem
|
||
|
elem.K = key
|
||
|
elem.V = value
|
||
|
|
||
|
// Add element map entry
|
||
|
m.hmap[key] = elem
|
||
|
|
||
|
// Push to front of list
|
||
|
m.list.PushFront(elem)
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Set will ensure that given key-value pair exists in the map, by either adding new or updating existing, pushing them to the front of the map. Evicts old at maximum capacity.
|
||
|
func (m *LRUMap[K, V]) Set(key K, value V) {
|
||
|
m.SetWithHook(key, value, nil)
|
||
|
}
|
||
|
|
||
|
// SetWithHook performs .Set() but passing any evicted entry to given hook function.
|
||
|
func (m *LRUMap[K, V]) SetWithHook(key K, value V, evict func(K, V)) {
|
||
|
// Ensure safe
|
||
|
m.write_check()
|
||
|
|
||
|
// Look for existing elem
|
||
|
elem, ok := m.hmap[key]
|
||
|
|
||
|
if ok {
|
||
|
// Unlink elem from list
|
||
|
m.list.Unlink(elem)
|
||
|
|
||
|
// Update existing
|
||
|
elem.V = value
|
||
|
} else {
|
||
|
if m.list.len >= m.size {
|
||
|
// We're at capacity, sir!
|
||
|
// Pop current tail elem
|
||
|
elem = m.list.PopTail()
|
||
|
|
||
|
if evict != nil {
|
||
|
// Pass to evict hook
|
||
|
evict(elem.K, elem.V)
|
||
|
}
|
||
|
|
||
|
// Delete key from map
|
||
|
delete(m.hmap, elem.K)
|
||
|
} else {
|
||
|
// Allocate elem
|
||
|
elem = m.alloc()
|
||
|
}
|
||
|
|
||
|
// Set elem
|
||
|
elem.K = key
|
||
|
elem.V = value
|
||
|
|
||
|
// Add element map entry
|
||
|
m.hmap[key] = elem
|
||
|
}
|
||
|
|
||
|
// Push to front of list
|
||
|
m.list.PushFront(elem)
|
||
|
}
|
||
|
|
||
|
// Cap returns the maximum capacity of this LRU map.
|
||
|
func (m *LRUMap[K, V]) Cap() int {
|
||
|
return m.size
|
||
|
}
|
||
|
|
||
|
// Format implements fmt.Formatter, allowing performant string formatting of map.
|
||
|
func (m *LRUMap[K, V]) Format(state fmt.State, verb rune) {
|
||
|
m.format(reflect.TypeOf(m), state, verb)
|
||
|
}
|