2018-11-27 16:52:20 -05:00
|
|
|
package cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"container/list"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ObjectLRU implements an object cache with an LRU eviction policy and a
|
|
|
|
// maximum size (measured in object size).
|
|
|
|
type ObjectLRU struct {
|
|
|
|
MaxSize FileSize
|
|
|
|
|
|
|
|
actualSize FileSize
|
|
|
|
ll *list.List
|
|
|
|
cache map[interface{}]*list.Element
|
|
|
|
mut sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewObjectLRU creates a new ObjectLRU with the given maximum size. The maximum
|
|
|
|
// size will never be exceeded.
|
|
|
|
func NewObjectLRU(maxSize FileSize) *ObjectLRU {
|
|
|
|
return &ObjectLRU{MaxSize: maxSize}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewObjectLRUDefault creates a new ObjectLRU with the default cache size.
|
|
|
|
func NewObjectLRUDefault() *ObjectLRU {
|
|
|
|
return &ObjectLRU{MaxSize: DefaultMaxSize}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put puts an object into the cache. If the object is already in the cache, it
|
|
|
|
// will be marked as used. Otherwise, it will be inserted. A single object might
|
|
|
|
// be evicted to make room for the new object.
|
|
|
|
func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
|
|
|
|
c.mut.Lock()
|
|
|
|
defer c.mut.Unlock()
|
|
|
|
|
|
|
|
if c.cache == nil {
|
|
|
|
c.actualSize = 0
|
|
|
|
c.cache = make(map[interface{}]*list.Element, 1000)
|
|
|
|
c.ll = list.New()
|
|
|
|
}
|
|
|
|
|
|
|
|
objSize := FileSize(obj.Size())
|
|
|
|
key := obj.Hash()
|
|
|
|
if ee, ok := c.cache[key]; ok {
|
|
|
|
oldObj := ee.Value.(plumbing.EncodedObject)
|
|
|
|
// in this case objSize is a delta: new size - old size
|
|
|
|
objSize -= FileSize(oldObj.Size())
|
|
|
|
c.ll.MoveToFront(ee)
|
|
|
|
ee.Value = obj
|
|
|
|
} else {
|
|
|
|
if objSize > c.MaxSize {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ee := c.ll.PushFront(obj)
|
|
|
|
c.cache[key] = ee
|
|
|
|
}
|
|
|
|
|
|
|
|
c.actualSize += objSize
|
|
|
|
for c.actualSize > c.MaxSize {
|
|
|
|
last := c.ll.Back()
|
2019-05-31 15:34:46 -04:00
|
|
|
if last == nil {
|
|
|
|
c.actualSize = 0
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2018-11-27 16:52:20 -05:00
|
|
|
lastObj := last.Value.(plumbing.EncodedObject)
|
|
|
|
lastSize := FileSize(lastObj.Size())
|
|
|
|
|
|
|
|
c.ll.Remove(last)
|
|
|
|
delete(c.cache, lastObj.Hash())
|
|
|
|
c.actualSize -= lastSize
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns an object by its hash. It marks the object as used. If the object
|
|
|
|
// is not in the cache, (nil, false) will be returned.
|
|
|
|
func (c *ObjectLRU) Get(k plumbing.Hash) (plumbing.EncodedObject, bool) {
|
|
|
|
c.mut.Lock()
|
|
|
|
defer c.mut.Unlock()
|
|
|
|
|
|
|
|
ee, ok := c.cache[k]
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
c.ll.MoveToFront(ee)
|
|
|
|
return ee.Value.(plumbing.EncodedObject), true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the content of this object cache.
|
|
|
|
func (c *ObjectLRU) Clear() {
|
|
|
|
c.mut.Lock()
|
|
|
|
defer c.mut.Unlock()
|
|
|
|
|
|
|
|
c.ll = nil
|
|
|
|
c.cache = nil
|
|
|
|
c.actualSize = 0
|
|
|
|
}
|