github.com/XiaoMi/Gaea@v1.2.5/util/cache/lru_cache.go (about)

     1  /*
     2  Copyright 2017 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package cache implements a LRU cache.
    18  //
    19  // The implementation borrows heavily from SmallLRUCache
    20  // (originally by Nathan Schrenk). The object maintains a doubly-linked list of
    21  // elements. When an element is accessed, it is promoted to the head of the
    22  // list. When space is needed, the element at the tail of the list
    23  // (the least recently used element) is evicted.
    24  package cache
    25  
    26  import (
    27  	"container/list"
    28  	"fmt"
    29  	"sync"
    30  	"time"
    31  )
    32  
    33  // LRUCache is a typical LRU cache implementation.  If the cache
    34  // reaches the capacity, the least recently used item is deleted from
    35  // the cache. Note the capacity is not the number of items, but the
    36  // total sum of the Size() of each item.
    37  type LRUCache struct {
    38  	mu sync.Mutex
    39  
    40  	// list & table contain *entry objects.
    41  	list  *list.List
    42  	table map[string]*list.Element
    43  
    44  	size      int64
    45  	capacity  int64
    46  	evictions int64
    47  }
    48  
    49  // Value is the interface values that go into LRUCache need to satisfy
    50  type Value interface {
    51  	// Size returns how big this value is. If you want to just track
    52  	// the cache by number of objects, you may return the size as 1.
    53  	Size() int
    54  }
    55  
    56  // CachedString 每个字符串记1个, 与StringValue不同
    57  type CachedString string
    58  
    59  // Size return cached string size
    60  func (c CachedString) Size() int {
    61  	return 1
    62  }
    63  
    64  // StringValue alias name of string
    65  type StringValue string
    66  
    67  // Size return cached string value lenght
    68  func (s StringValue) Size() int {
    69  	return len(s)
    70  }
    71  
    72  // Item is what is stored in the cache
    73  type Item struct {
    74  	Key   string
    75  	Value Value
    76  }
    77  
    78  type entry struct {
    79  	key          string
    80  	value        Value
    81  	size         int64
    82  	timeAccessed time.Time
    83  }
    84  
    85  // NewLRUCache creates a new empty cache with the given capacity.
    86  func NewLRUCache(capacity int64) *LRUCache {
    87  	return &LRUCache{
    88  		list:     list.New(),
    89  		table:    make(map[string]*list.Element),
    90  		capacity: capacity,
    91  	}
    92  }
    93  
    94  // Get returns a value from the cache, and marks the entry as most
    95  // recently used.
    96  func (lru *LRUCache) Get(key string) (v Value, ok bool) {
    97  	lru.mu.Lock()
    98  	defer lru.mu.Unlock()
    99  
   100  	element := lru.table[key]
   101  	if element == nil {
   102  		return nil, false
   103  	}
   104  	lru.moveToFront(element)
   105  	return element.Value.(*entry).value, true
   106  }
   107  
   108  // Peek returns a value from the cache without changing the LRU order.
   109  func (lru *LRUCache) Peek(key string) (v Value, ok bool) {
   110  	lru.mu.Lock()
   111  	defer lru.mu.Unlock()
   112  
   113  	element := lru.table[key]
   114  	if element == nil {
   115  		return nil, false
   116  	}
   117  	return element.Value.(*entry).value, true
   118  }
   119  
   120  // Set sets a value in the cache.
   121  func (lru *LRUCache) Set(key string, value Value) {
   122  	lru.mu.Lock()
   123  	defer lru.mu.Unlock()
   124  
   125  	if element := lru.table[key]; element != nil {
   126  		lru.updateInplace(element, value)
   127  	} else {
   128  		lru.addNew(key, value)
   129  	}
   130  }
   131  
   132  // SetIfAbsent will set the value in the cache if not present. If the
   133  // value exists in the cache, we don't set it.
   134  func (lru *LRUCache) SetIfAbsent(key string, value Value) {
   135  	lru.mu.Lock()
   136  	defer lru.mu.Unlock()
   137  
   138  	if element := lru.table[key]; element != nil {
   139  		lru.moveToFront(element)
   140  	} else {
   141  		lru.addNew(key, value)
   142  	}
   143  }
   144  
   145  // Delete removes an entry from the cache, and returns if the entry existed.
   146  func (lru *LRUCache) Delete(key string) bool {
   147  	lru.mu.Lock()
   148  	defer lru.mu.Unlock()
   149  
   150  	element := lru.table[key]
   151  	if element == nil {
   152  		return false
   153  	}
   154  
   155  	lru.list.Remove(element)
   156  	delete(lru.table, key)
   157  	lru.size -= element.Value.(*entry).size
   158  	return true
   159  }
   160  
   161  // Clear will clear the entire cache.
   162  func (lru *LRUCache) Clear() {
   163  	lru.mu.Lock()
   164  	defer lru.mu.Unlock()
   165  
   166  	lru.list.Init()
   167  	lru.table = make(map[string]*list.Element)
   168  	lru.size = 0
   169  }
   170  
   171  // SetCapacity will set the capacity of the cache. If the capacity is
   172  // smaller, and the current cache size exceed that capacity, the cache
   173  // will be shrank.
   174  func (lru *LRUCache) SetCapacity(capacity int64) {
   175  	lru.mu.Lock()
   176  	defer lru.mu.Unlock()
   177  
   178  	lru.capacity = capacity
   179  	lru.checkCapacity()
   180  }
   181  
   182  // Stats returns a few stats on the cache.
   183  func (lru *LRUCache) Stats() (length, size, capacity, evictions int64, oldest time.Time) {
   184  	lru.mu.Lock()
   185  	defer lru.mu.Unlock()
   186  	if lastElem := lru.list.Back(); lastElem != nil {
   187  		oldest = lastElem.Value.(*entry).timeAccessed
   188  	}
   189  	return int64(lru.list.Len()), lru.size, lru.capacity, lru.evictions, oldest
   190  }
   191  
   192  // StatsJSON returns stats as a JSON object in a string.
   193  func (lru *LRUCache) StatsJSON() string {
   194  	if lru == nil {
   195  		return "{}"
   196  	}
   197  	l, s, c, e, o := lru.Stats()
   198  	return fmt.Sprintf("{\"Length\": %v, \"Size\": %v, \"Capacity\": %v, \"Evictions\": %v, \"OldestAccess\": \"%v\"}", l, s, c, e, o)
   199  }
   200  
   201  // Length returns how many elements are in the cache
   202  func (lru *LRUCache) Length() int64 {
   203  	lru.mu.Lock()
   204  	defer lru.mu.Unlock()
   205  	return int64(lru.list.Len())
   206  }
   207  
   208  // Size returns the sum of the objects' Size() method.
   209  func (lru *LRUCache) Size() int64 {
   210  	lru.mu.Lock()
   211  	defer lru.mu.Unlock()
   212  	return lru.size
   213  }
   214  
   215  // Capacity returns the cache maximum capacity.
   216  func (lru *LRUCache) Capacity() int64 {
   217  	lru.mu.Lock()
   218  	defer lru.mu.Unlock()
   219  	return lru.capacity
   220  }
   221  
   222  // Evictions returns the eviction count.
   223  func (lru *LRUCache) Evictions() int64 {
   224  	lru.mu.Lock()
   225  	defer lru.mu.Unlock()
   226  	return lru.evictions
   227  }
   228  
   229  // Oldest returns the insertion time of the oldest element in the cache,
   230  // or a IsZero() time if cache is empty.
   231  func (lru *LRUCache) Oldest() (oldest time.Time) {
   232  	lru.mu.Lock()
   233  	defer lru.mu.Unlock()
   234  	if lastElem := lru.list.Back(); lastElem != nil {
   235  		oldest = lastElem.Value.(*entry).timeAccessed
   236  	}
   237  	return
   238  }
   239  
   240  // Keys returns all the keys for the cache, ordered from most recently
   241  // used to last recently used.
   242  func (lru *LRUCache) Keys() []string {
   243  	lru.mu.Lock()
   244  	defer lru.mu.Unlock()
   245  
   246  	keys := make([]string, 0, lru.list.Len())
   247  	for e := lru.list.Front(); e != nil; e = e.Next() {
   248  		keys = append(keys, e.Value.(*entry).key)
   249  	}
   250  	return keys
   251  }
   252  
   253  // Items returns all the values for the cache, ordered from most recently
   254  // used to last recently used.
   255  func (lru *LRUCache) Items() []Item {
   256  	lru.mu.Lock()
   257  	defer lru.mu.Unlock()
   258  
   259  	items := make([]Item, 0, lru.list.Len())
   260  	for e := lru.list.Front(); e != nil; e = e.Next() {
   261  		v := e.Value.(*entry)
   262  		items = append(items, Item{Key: v.key, Value: v.value})
   263  	}
   264  	return items
   265  }
   266  
   267  func (lru *LRUCache) updateInplace(element *list.Element, value Value) {
   268  	valueSize := int64(value.Size())
   269  	sizeDiff := valueSize - element.Value.(*entry).size
   270  	element.Value.(*entry).value = value
   271  	element.Value.(*entry).size = valueSize
   272  	lru.size += sizeDiff
   273  	lru.moveToFront(element)
   274  	lru.checkCapacity()
   275  }
   276  
   277  func (lru *LRUCache) moveToFront(element *list.Element) {
   278  	lru.list.MoveToFront(element)
   279  	element.Value.(*entry).timeAccessed = time.Now()
   280  }
   281  
   282  func (lru *LRUCache) addNew(key string, value Value) {
   283  	newEntry := &entry{key, value, int64(value.Size()), time.Now()}
   284  	element := lru.list.PushFront(newEntry)
   285  	lru.table[key] = element
   286  	lru.size += newEntry.size
   287  	lru.checkCapacity()
   288  }
   289  
   290  func (lru *LRUCache) checkCapacity() {
   291  	// Partially duplicated from Delete
   292  	for lru.size > lru.capacity {
   293  		delElem := lru.list.Back()
   294  		delValue := delElem.Value.(*entry)
   295  		lru.list.Remove(delElem)
   296  		delete(lru.table, delValue.key)
   297  		lru.size -= delValue.size
   298  		lru.evictions++
   299  	}
   300  }