object_lru.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package cache
  2. import (
  3. "container/list"
  4. "sync"
  5. "gopkg.in/src-d/go-git.v4/plumbing"
  6. )
  7. // ObjectLRU implements an object cache with an LRU eviction policy and a
  8. // maximum size (measured in object size).
  9. type ObjectLRU struct {
  10. MaxSize FileSize
  11. actualSize FileSize
  12. ll *list.List
  13. cache map[interface{}]*list.Element
  14. mut sync.Mutex
  15. }
  16. // NewObjectLRU creates a new ObjectLRU with the given maximum size. The maximum
  17. // size will never be exceeded.
  18. func NewObjectLRU(maxSize FileSize) *ObjectLRU {
  19. return &ObjectLRU{MaxSize: maxSize}
  20. }
  21. // NewObjectLRUDefault creates a new ObjectLRU with the default cache size.
  22. func NewObjectLRUDefault() *ObjectLRU {
  23. return &ObjectLRU{MaxSize: DefaultMaxSize}
  24. }
  25. // Put puts an object into the cache. If the object is already in the cache, it
  26. // will be marked as used. Otherwise, it will be inserted. A single object might
  27. // be evicted to make room for the new object.
  28. func (c *ObjectLRU) Put(obj plumbing.EncodedObject) {
  29. c.mut.Lock()
  30. defer c.mut.Unlock()
  31. if c.cache == nil {
  32. c.actualSize = 0
  33. c.cache = make(map[interface{}]*list.Element, 1000)
  34. c.ll = list.New()
  35. }
  36. objSize := FileSize(obj.Size())
  37. key := obj.Hash()
  38. if ee, ok := c.cache[key]; ok {
  39. oldObj := ee.Value.(plumbing.EncodedObject)
  40. // in this case objSize is a delta: new size - old size
  41. objSize -= FileSize(oldObj.Size())
  42. c.ll.MoveToFront(ee)
  43. ee.Value = obj
  44. } else {
  45. if objSize > c.MaxSize {
  46. return
  47. }
  48. ee := c.ll.PushFront(obj)
  49. c.cache[key] = ee
  50. }
  51. c.actualSize += objSize
  52. for c.actualSize > c.MaxSize {
  53. last := c.ll.Back()
  54. lastObj := last.Value.(plumbing.EncodedObject)
  55. lastSize := FileSize(lastObj.Size())
  56. c.ll.Remove(last)
  57. delete(c.cache, lastObj.Hash())
  58. c.actualSize -= lastSize
  59. }
  60. }
  61. // Get returns an object by its hash. It marks the object as used. If the object
  62. // is not in the cache, (nil, false) will be returned.
  63. func (c *ObjectLRU) Get(k plumbing.Hash) (plumbing.EncodedObject, bool) {
  64. c.mut.Lock()
  65. defer c.mut.Unlock()
  66. ee, ok := c.cache[k]
  67. if !ok {
  68. return nil, false
  69. }
  70. c.ll.MoveToFront(ee)
  71. return ee.Value.(plumbing.EncodedObject), true
  72. }
  73. // Clear the content of this object cache.
  74. func (c *ObjectLRU) Clear() {
  75. c.mut.Lock()
  76. defer c.mut.Unlock()
  77. c.ll = nil
  78. c.cache = nil
  79. c.actualSize = 0
  80. }