Files
LeetCode-Go/template/CLRUCache.go
2022-09-10 19:20:34 -07:00

177 lines
3.0 KiB
Go

package template
import (
"container/list"
"hash/fnv"
"sync"
)
type command int
const (
// MoveToFront define
MoveToFront command = iota
// PushFront define
PushFront
// Delete define
Delete
)
type clear struct {
done chan struct{}
}
// CLRUCache define: High Concurrency LRUCache
type CLRUCache struct {
sync.RWMutex
cap int
list *list.List
buckets []*bucket
bucketMask uint32
deletePairs chan *list.Element
movePairs chan *list.Element
control chan interface{}
}
// Pair define
type Pair struct {
key string
value interface{}
cmd command
}
// New define
func New(capacity int) *CLRUCache {
c := &CLRUCache{
cap: capacity,
list: list.New(),
bucketMask: uint32(1024) - 1,
buckets: make([]*bucket, 1024),
}
for i := 0; i < 1024; i++ {
c.buckets[i] = &bucket{
keys: make(map[string]*list.Element),
}
}
c.restart()
return c
}
// Get define
func (c *CLRUCache) Get(key string) interface{} {
el := c.bucket(key).get(key)
if el == nil {
return nil
}
c.move(el)
return el.Value.(Pair).value
}
// Put define
func (c *CLRUCache) Put(key string, value interface{}) {
el, exist := c.bucket(key).set(key, value)
if exist != nil {
c.deletePairs <- exist
}
c.move(el)
}
func (c *CLRUCache) move(el *list.Element) {
select {
case c.movePairs <- el:
default:
}
}
// Delete define
func (c *CLRUCache) Delete(key string) bool {
el := c.bucket(key).delete(key)
if el != nil {
c.deletePairs <- el
return true
}
return false
}
// Clear define
func (c *CLRUCache) Clear() {
done := make(chan struct{})
c.control <- clear{done: done}
<-done
}
// Count define
func (c *CLRUCache) Count() int {
count := 0
for _, b := range c.buckets {
count += b.pairCount()
}
return count
}
func (c *CLRUCache) bucket(key string) *bucket {
h := fnv.New32a()
h.Write([]byte(key))
return c.buckets[h.Sum32()&c.bucketMask]
}
func (c *CLRUCache) stop() {
close(c.movePairs)
<-c.control
}
func (c *CLRUCache) restart() {
c.deletePairs = make(chan *list.Element, 128)
c.movePairs = make(chan *list.Element, 128)
c.control = make(chan interface{})
go c.worker()
}
func (c *CLRUCache) worker() {
defer close(c.control)
for {
select {
case el, ok := <-c.movePairs:
if !ok {
goto clean
}
if c.doMove(el) && c.list.Len() > c.cap {
el := c.list.Back()
c.list.Remove(el)
c.bucket(el.Value.(Pair).key).delete(el.Value.(Pair).key)
}
case el := <-c.deletePairs:
c.list.Remove(el)
case control := <-c.control:
switch msg := control.(type) {
case clear:
for _, bucket := range c.buckets {
bucket.clear()
}
c.list = list.New()
msg.done <- struct{}{}
}
}
}
clean:
for {
select {
case el := <-c.deletePairs:
c.list.Remove(el)
default:
close(c.deletePairs)
return
}
}
}
func (c *CLRUCache) doMove(el *list.Element) bool {
if el.Value.(Pair).cmd == MoveToFront {
c.list.MoveToFront(el)
return false
}
newel := c.list.PushFront(el.Value.(Pair))
c.bucket(el.Value.(Pair).key).update(el.Value.(Pair).key, newel)
return true
}