secretcache/lruCache.go (80 lines of code) (raw):
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You
// may not use this file except in compliance with the License. A copy of
// the License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
// ANY KIND, either express or implied. See the License for the specific
// language governing permissions and limitations under the License.
package secretcache
import (
"sync"
)
// lruCache is a cache implementation using a map and doubly linked list.
type lruCache struct {
cacheMap map[string]*lruItem
cacheMaxSize int
cacheSize int
mux sync.Mutex
head *lruItem
tail *lruItem
}
// lruItem is the cache item to hold data and linked list pointers.
type lruItem struct {
next *lruItem
prev *lruItem
key string
data interface{}
}
// newLRUCache initialises an lruCache instance with given max size.
func newLRUCache(maxSize int) *lruCache {
return &lruCache{
cacheMap: make(map[string]*lruItem),
cacheMaxSize: maxSize,
}
}
// get gets the cached item's data for the given key.
// Updates the fetched item to be head of the linked list.
func (l *lruCache) get(key string) (interface{}, bool) {
l.mux.Lock()
defer l.mux.Unlock()
item, found := l.cacheMap[key]
if !found {
return nil, false
}
l.updateHead(item)
return item.data, true
}
// putIfAbsent puts an lruItem initialised from the given data in the cache.
// Updates head of the linked list to be the new lruItem.
// If cache size is over max allowed size, removes the tail item from cache.
// Returns true if new key is inserted to cache, false if it already existed.
func (l *lruCache) putIfAbsent(key string, data interface{}) bool {
l.mux.Lock()
defer l.mux.Unlock()
_, found := l.cacheMap[key]
if found {
return false
}
item := &lruItem{key: key, data: data}
l.cacheMap[key] = item
l.cacheSize++
l.updateHead(item)
if l.cacheSize > l.cacheMaxSize {
delete(l.cacheMap, (*l.tail).key)
l.unlink(l.tail)
l.cacheSize--
}
return true
}
// updateHead updates head of the linked list to be the input lruItem.
func (l *lruCache) updateHead(item *lruItem) {
if l.head == item {
return
}
l.unlink(item)
item.next = l.head
if l.head != nil {
l.head.prev = item
}
l.head = item
if l.tail == nil {
l.tail = item
}
}
// unlink removes the input lruItem from the linked list.
func (l *lruCache) unlink(item *lruItem) {
if l.head == item {
l.head = item.next
}
if l.tail == item {
l.tail = item.prev
}
if item.prev != nil {
item.prev.next = item.next
}
if item.next != nil {
item.next.prev = item.prev
}
}