github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/cache/lru_cache.go (about)

     1  // Copyright 2012, Google Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cache
     6  
     7  import (
     8  	"container/list"
     9  	"fmt"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  type LRUCache struct {
    15  	mu sync.Mutex
    16  
    17  	// list & table of *entry objects
    18  	list  *list.List
    19  	table map[string]*list.Element
    20  
    21  	// Our current size, in bytes. Obviously a gross simplification and low-grade
    22  	// approximation.
    23  	size uint64
    24  
    25  	// How many bytes we are limiting the cache to.
    26  	capacity uint64
    27  }
    28  
    29  func NewLRUCache(capacity uint64) *LRUCache {
    30  	return &LRUCache{
    31  		list:     list.New(),
    32  		table:    make(map[string]*list.Element),
    33  		capacity: capacity,
    34  	}
    35  }
    36  
    37  func (lru *LRUCache) Get(key string) (v interface{}, ok bool) {
    38  	lru.mu.Lock()
    39  	defer lru.mu.Unlock()
    40  
    41  	element := lru.table[key]
    42  	if element == nil {
    43  		return nil, false
    44  	}
    45  	lru.moveToFront(element)
    46  	return element.Value.(*entry).value, true
    47  }
    48  
    49  func (lru *LRUCache) Set(key string, value interface{}, size int) {
    50  	lru.mu.Lock()
    51  	defer lru.mu.Unlock()
    52  
    53  	size = adjustValueSize(value, size)
    54  	if element := lru.table[key]; element != nil {
    55  		lru.updateInplace(element, value, size)
    56  	} else {
    57  		lru.addNew(key, value, size)
    58  	}
    59  }
    60  
    61  func (lru *LRUCache) SetIfAbsent(key string, value interface{}, size int) {
    62  	lru.mu.Lock()
    63  	defer lru.mu.Unlock()
    64  
    65  	size = adjustValueSize(value, size)
    66  	if element := lru.table[key]; element != nil {
    67  		lru.moveToFront(element)
    68  	} else {
    69  		lru.addNew(key, value, size)
    70  	}
    71  }
    72  
    73  func (lru *LRUCache) Take(key string) (v interface{}, ok bool) {
    74  	lru.mu.Lock()
    75  	defer lru.mu.Unlock()
    76  
    77  	element := lru.table[key]
    78  	if element == nil {
    79  		return nil, false
    80  	}
    81  
    82  	lru.list.Remove(element)
    83  	delete(lru.table, key)
    84  	lru.size -= uint64(element.Value.(*entry).size)
    85  	return element.Value.(*entry).value, true
    86  }
    87  
    88  func (lru *LRUCache) Delete(key string) bool {
    89  	lru.mu.Lock()
    90  	defer lru.mu.Unlock()
    91  
    92  	element := lru.table[key]
    93  	if element == nil {
    94  		return false
    95  	}
    96  
    97  	lru.list.Remove(element)
    98  	delete(lru.table, key)
    99  	lru.size -= uint64(element.Value.(*entry).size)
   100  	return true
   101  }
   102  
   103  func (lru *LRUCache) Clear() {
   104  	lru.mu.Lock()
   105  	defer lru.mu.Unlock()
   106  
   107  	lru.list.Init()
   108  	lru.table = make(map[string]*list.Element)
   109  	lru.size = 0
   110  }
   111  
   112  func (lru *LRUCache) SetCapacity(capacity uint64) {
   113  	lru.mu.Lock()
   114  	defer lru.mu.Unlock()
   115  
   116  	lru.capacity = capacity
   117  	lru.checkCapacity()
   118  }
   119  
   120  func (lru *LRUCache) Stats() (length, size, capacity uint64, oldest time.Time) {
   121  	lru.mu.Lock()
   122  	defer lru.mu.Unlock()
   123  	if lastElem := lru.list.Back(); lastElem != nil {
   124  		oldest = lastElem.Value.(*entry).timeAccessed
   125  	}
   126  	return uint64(lru.list.Len()), lru.size, lru.capacity, oldest
   127  }
   128  
   129  func (lru *LRUCache) StatsJSON() string {
   130  	if lru == nil {
   131  		return "{}"
   132  	}
   133  	l, s, c, o := lru.Stats()
   134  	return fmt.Sprintf("{\"Length\": %v, \"Size\": %v, \"Capacity\": %v, \"OldestAccess\": \"%v\"}", l, s, c, o)
   135  }
   136  
   137  func (lru *LRUCache) Keys() []string {
   138  	lru.mu.Lock()
   139  	defer lru.mu.Unlock()
   140  
   141  	keys := make([]string, 0, lru.list.Len())
   142  	for e := lru.list.Front(); e != nil; e = e.Next() {
   143  		keys = append(keys, e.Value.(*entry).key)
   144  	}
   145  	return keys
   146  }
   147  
   148  func (lru *LRUCache) Items() []Item {
   149  	lru.mu.Lock()
   150  	defer lru.mu.Unlock()
   151  
   152  	items := make([]Item, 0, lru.list.Len())
   153  	for e := lru.list.Front(); e != nil; e = e.Next() {
   154  		v := e.Value.(*entry)
   155  		items = append(items, Item{Key: v.key, Value: v.value, Size: v.size})
   156  	}
   157  	return items
   158  }
   159  
   160  func (lru *LRUCache) updateInplace(element *list.Element, value interface{}, size int) {
   161  	sizeDiff := size - element.Value.(*entry).size
   162  	element.Value.(*entry).value = value
   163  	element.Value.(*entry).size = size
   164  	lru.size += uint64(sizeDiff)
   165  	lru.moveToFront(element)
   166  	lru.checkCapacity()
   167  }
   168  
   169  func (lru *LRUCache) moveToFront(element *list.Element) {
   170  	lru.list.MoveToFront(element)
   171  	element.Value.(*entry).timeAccessed = time.Now()
   172  }
   173  
   174  func (lru *LRUCache) addNew(key string, value interface{}, size int) {
   175  	newEntry := &entry{
   176  		key:          key,
   177  		value:        value,
   178  		size:         size,
   179  		timeAccessed: time.Now(),
   180  	}
   181  	element := lru.list.PushFront(newEntry)
   182  	lru.table[key] = element
   183  	lru.size += uint64(newEntry.size)
   184  	lru.checkCapacity()
   185  }
   186  
   187  func (lru *LRUCache) checkCapacity() {
   188  	// Partially duplicated from Delete
   189  	for lru.size > lru.capacity {
   190  		delElem := lru.list.Back()
   191  		delValue := delElem.Value.(*entry)
   192  		lru.list.Remove(delElem)
   193  		delete(lru.table, delValue.key)
   194  		lru.size -= uint64(delValue.size)
   195  	}
   196  }