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 }