go-mru/mru.go

129 lines
2.2 KiB
Go
Raw Permalink Normal View History

2022-02-09 04:05:49 +00:00
package mru
import (
"errors"
"fmt"
"sort"
"github.com/benbjohnson/clock"
)
type Item struct {
V interface{}
access int64
}
type Cache struct {
store map[string]*Item
access *timestamps
cap int
clock clock.Clock
}
func New(cap int) *Cache {
return &Cache{
store: map[string]*Item{},
access: newTimestamps(cap),
cap: cap,
clock: clock.New(),
}
}
func (c *Cache) Len() int {
return len(c.store)
}
// evict should remove the least-recently-used cache item.
func (c *Cache) evict() {
k := c.access.K(0)
c.evictKey(k)
}
// evictKey should remove the entry given by the key item.
func (c *Cache) evictKey(k string) {
delete(c.store, k)
i, ok := c.access.Find(k)
if !ok {
return
}
c.access.Delete(i)
}
func (c *Cache) sanityCheck() {
if len(c.store) != c.access.Len() {
panic(fmt.Sprintf("MRU cache is out of sync; store len = %d, access len = %d",
len(c.store), c.access.Len()))
}
}
func (c *Cache) ConsistencyCheck() error {
if err := c.access.ConsistencyCheck(); err != nil {
return err
}
if len(c.store) != c.access.Len() {
return fmt.Errorf("mru: cache is out of sync; store len = %d, access len = %d",
len(c.store), c.access.Len())
}
for i := range c.access.ts {
itm, ok := c.store[c.access.K(i)]
if !ok {
return errors.New("mru: key in access is not in store")
}
if c.access.T(i) != itm.access {
return fmt.Errorf("timestamps are out of sync (%d != %d)",
itm.access, c.access.T(i))
}
}
if !sort.IsSorted(c.access) {
return errors.New("mru: timestamps aren't sorted")
}
return nil
}
func (c *Cache) Store(k string, v interface{}) {
c.sanityCheck()
if len(c.store) == c.cap {
c.evict()
}
if _, ok := c.store[k]; ok {
c.evictKey(k)
}
itm := &Item{
V: v,
access: c.clock.Now().UnixNano(),
}
c.store[k] = itm
c.access.Update(k, itm.access)
}
func (c *Cache) Get(k string) (interface{}, bool) {
c.sanityCheck()
itm, ok := c.store[k]
if !ok {
return nil, false
}
c.store[k].access = c.clock.Now().UnixNano()
c.access.Update(k, itm.access)
return itm.V, true
}
// Has will not update the timestamp on the item.
func (c *Cache) Has(k string) bool {
c.sanityCheck()
_, ok := c.store[k]
return ok
}