github.com/andy2046/gopie@v0.7.0/pkg/lru/lru.go (about)

     1  // Package lru implements a LRU cache.
     2  package lru
     3  
     4  import (
     5  	"container/list"
     6  	"sync"
     7  )
     8  
     9  // Cache is a LRU cache.
    10  type Cache struct {
    11  	// MaxEntries is the maximum number of cache entries
    12  	// before an item is purged. Zero means no limit.
    13  	MaxEntries int
    14  
    15  	// OnPurged specifies a function to be executed
    16  	// when an entry is purged from the cache.
    17  	OnPurged func(key interface{}, value interface{})
    18  
    19  	ll    *list.List
    20  	cache map[interface{}]*list.Element
    21  	mu    sync.RWMutex
    22  }
    23  
    24  type entry struct {
    25  	key   interface{}
    26  	value interface{}
    27  }
    28  
    29  // New creates a new cache, if maxEntries is zero, the cache has no limit.
    30  func New(maxEntries int) *Cache {
    31  	if maxEntries < 0 {
    32  		panic("maxEntries can not be less than zero")
    33  	}
    34  	return &Cache{
    35  		MaxEntries: maxEntries,
    36  		ll:         list.New(),
    37  		cache:      make(map[interface{}]*list.Element),
    38  	}
    39  }
    40  
    41  // Add adds value to the cache.
    42  func (c *Cache) Add(key interface{}, value interface{}) {
    43  	c.mu.Lock()
    44  	defer c.mu.Unlock()
    45  
    46  	if c.cache == nil {
    47  		c.cache = make(map[interface{}]*list.Element)
    48  		c.ll = list.New()
    49  	}
    50  	if e, ok := c.cache[key]; ok {
    51  		c.ll.MoveToFront(e)
    52  		e.Value.(*entry).value = value
    53  		return
    54  	}
    55  	ele := c.ll.PushFront(&entry{key, value})
    56  	c.cache[key] = ele
    57  	if c.MaxEntries > 0 && c.ll.Len() > c.MaxEntries {
    58  		c.removeOldest(false)
    59  	}
    60  }
    61  
    62  // Get looks up value by key from the cache.
    63  func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
    64  	c.mu.Lock()
    65  	defer c.mu.Unlock()
    66  
    67  	if c.cache == nil {
    68  		return
    69  	}
    70  	if ele, hit := c.cache[key]; hit {
    71  		c.ll.MoveToFront(ele)
    72  		return ele.Value.(*entry).value, true
    73  	}
    74  	return
    75  }
    76  
    77  // Remove removes the provided key from the cache.
    78  func (c *Cache) Remove(key interface{}) {
    79  	c.mu.Lock()
    80  	defer c.mu.Unlock()
    81  
    82  	if c.cache == nil {
    83  		return
    84  	}
    85  	if ele, hit := c.cache[key]; hit {
    86  		c.removeElement(ele)
    87  	}
    88  }
    89  
    90  // RemoveOldest removes the oldest item from the cache.
    91  func (c *Cache) RemoveOldest() {
    92  	c.removeOldest(true)
    93  }
    94  
    95  func (c *Cache) removeOldest(toLock bool) {
    96  	if toLock {
    97  		c.mu.Lock()
    98  		defer c.mu.Unlock()
    99  	}
   100  
   101  	if c.cache == nil {
   102  		return
   103  	}
   104  	ele := c.ll.Back()
   105  	if ele != nil {
   106  		c.removeElement(ele)
   107  	}
   108  }
   109  
   110  func (c *Cache) removeElement(e *list.Element) {
   111  	c.ll.Remove(e)
   112  	kv := e.Value.(*entry)
   113  	delete(c.cache, kv.key)
   114  	if c.OnPurged != nil {
   115  		c.OnPurged(kv.key, kv.value)
   116  	}
   117  }
   118  
   119  // Len returns the number of items in the cache.
   120  func (c *Cache) Len() int {
   121  	c.mu.RLock()
   122  	defer c.mu.RUnlock()
   123  
   124  	if c.cache == nil {
   125  		return 0
   126  	}
   127  	return c.ll.Len()
   128  }
   129  
   130  // Clear purges all items from the cache.
   131  func (c *Cache) Clear() {
   132  	c.mu.Lock()
   133  	defer c.mu.Unlock()
   134  
   135  	if c.OnPurged != nil {
   136  		for _, e := range c.cache {
   137  			kv := e.Value.(*entry)
   138  			c.OnPurged(kv.key, kv.value)
   139  		}
   140  	}
   141  	c.ll = nil
   142  	c.cache = nil
   143  }