1// @author Couchbase <info@couchbase.com>
2// @copyright 2017 Couchbase, 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// Package cbauthimpl contains internal implementation details of
17// cbauth. It's APIs are subject to change without notice.
18package cbauthimpl
19
20import (
21	"container/list"
22	"sync"
23)
24
25type item struct {
26	key     interface{}
27	value   interface{}
28	lruElem *list.Element
29}
30
31// LRUCache implements simple LRU Cache
32type LRUCache struct {
33	sync.Mutex
34	lru   *list.List
35	items map[interface{}]*item
36
37	maxSize int
38}
39
40// NewLRUCache creates new LRUCache
41func NewLRUCache(size int) *LRUCache {
42	return &LRUCache{
43		lru:     list.New(),
44		items:   make(map[interface{}]*item),
45		maxSize: size,
46	}
47}
48
49// Get gets the value by key, returns (nil, false) if the value is not found
50func (c *LRUCache) Get(key interface{}) (interface{}, bool) {
51	c.Lock()
52	defer c.Unlock()
53
54	itm, ok := c.items[key]
55	if !ok {
56		return nil, false
57	}
58
59	c.touch(itm)
60	return itm.value, true
61}
62
63// Set sets the value for the key in LRU Cache
64func (c *LRUCache) Set(key interface{}, value interface{}) {
65	c.Lock()
66	defer c.Unlock()
67
68	itm, ok := c.items[key]
69	if !ok {
70		c.create(key, value)
71		return
72	}
73
74	itm.value = value
75	c.touch(itm)
76}
77
78func (c *LRUCache) maybeEvict() {
79	if len(c.items) < c.maxSize {
80		return
81	}
82
83	victim := c.lru.Remove(c.lru.Front()).(*item)
84	delete(c.items, victim.key)
85}
86
87func (c *LRUCache) create(key interface{}, value interface{}) {
88	c.maybeEvict()
89
90	itm := &item{
91		key:   key,
92		value: value,
93	}
94
95	itm.lruElem = c.lru.PushBack(itm)
96
97	c.items[key] = itm
98}
99
100func (c *LRUCache) touch(itm *item) {
101	c.lru.MoveToBack(itm.lruElem)
102}
103