99 lines
2.2 KiB
Go
99 lines
2.2 KiB
Go
|
package cache
|
||
|
|
||
|
import (
|
||
|
"container/list"
|
||
|
"sync"
|
||
|
)
|
||
|
|
||
|
// BufferLRU implements an object cache with an LRU eviction policy and a
|
||
|
// maximum size (measured in object size).
|
||
|
type BufferLRU struct {
|
||
|
MaxSize FileSize
|
||
|
|
||
|
actualSize FileSize
|
||
|
ll *list.List
|
||
|
cache map[int64]*list.Element
|
||
|
mut sync.Mutex
|
||
|
}
|
||
|
|
||
|
// NewBufferLRU creates a new BufferLRU with the given maximum size. The maximum
|
||
|
// size will never be exceeded.
|
||
|
func NewBufferLRU(maxSize FileSize) *BufferLRU {
|
||
|
return &BufferLRU{MaxSize: maxSize}
|
||
|
}
|
||
|
|
||
|
// NewBufferLRUDefault creates a new BufferLRU with the default cache size.
|
||
|
func NewBufferLRUDefault() *BufferLRU {
|
||
|
return &BufferLRU{MaxSize: DefaultMaxSize}
|
||
|
}
|
||
|
|
||
|
type buffer struct {
|
||
|
Key int64
|
||
|
Slice []byte
|
||
|
}
|
||
|
|
||
|
// Put puts a buffer into the cache. If the buffer is already in the cache, it
|
||
|
// will be marked as used. Otherwise, it will be inserted. A buffers might
|
||
|
// be evicted to make room for the new one.
|
||
|
func (c *BufferLRU) Put(key int64, slice []byte) {
|
||
|
c.mut.Lock()
|
||
|
defer c.mut.Unlock()
|
||
|
|
||
|
if c.cache == nil {
|
||
|
c.actualSize = 0
|
||
|
c.cache = make(map[int64]*list.Element, 1000)
|
||
|
c.ll = list.New()
|
||
|
}
|
||
|
|
||
|
bufSize := FileSize(len(slice))
|
||
|
if ee, ok := c.cache[key]; ok {
|
||
|
oldBuf := ee.Value.(buffer)
|
||
|
// in this case bufSize is a delta: new size - old size
|
||
|
bufSize -= FileSize(len(oldBuf.Slice))
|
||
|
c.ll.MoveToFront(ee)
|
||
|
ee.Value = buffer{key, slice}
|
||
|
} else {
|
||
|
if bufSize > c.MaxSize {
|
||
|
return
|
||
|
}
|
||
|
ee := c.ll.PushFront(buffer{key, slice})
|
||
|
c.cache[key] = ee
|
||
|
}
|
||
|
|
||
|
c.actualSize += bufSize
|
||
|
for c.actualSize > c.MaxSize {
|
||
|
last := c.ll.Back()
|
||
|
lastObj := last.Value.(buffer)
|
||
|
lastSize := FileSize(len(lastObj.Slice))
|
||
|
|
||
|
c.ll.Remove(last)
|
||
|
delete(c.cache, lastObj.Key)
|
||
|
c.actualSize -= lastSize
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get returns a buffer by its key. It marks the buffer as used. If the buffer
|
||
|
// is not in the cache, (nil, false) will be returned.
|
||
|
func (c *BufferLRU) Get(key int64) ([]byte, bool) {
|
||
|
c.mut.Lock()
|
||
|
defer c.mut.Unlock()
|
||
|
|
||
|
ee, ok := c.cache[key]
|
||
|
if !ok {
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
c.ll.MoveToFront(ee)
|
||
|
return ee.Value.(buffer).Slice, true
|
||
|
}
|
||
|
|
||
|
// Clear the content of this buffer cache.
|
||
|
func (c *BufferLRU) Clear() {
|
||
|
c.mut.Lock()
|
||
|
defer c.mut.Unlock()
|
||
|
|
||
|
c.ll = nil
|
||
|
c.cache = nil
|
||
|
c.actualSize = 0
|
||
|
}
|