diff --git a/utility/cache.go b/utility/cache.go new file mode 100644 index 000000000..80ed4025a --- /dev/null +++ b/utility/cache.go @@ -0,0 +1,176 @@ +// Copyright 2013 Prometheus Team +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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 utility + +import ( + "container/list" + "sync" +) + +type Cache interface { + Put(k, v interface{}) (replaced bool, err error) + PutIfAbsent(k, v interface{}) (put bool, err error) + Get(k interface{}) (v interface{}, ok bool, err error) + Has(k interface{}) (ok bool, err error) + Delete(k interface{}) (deleted bool, err error) + Clear() (cleared bool, err error) +} + +type LRUCache struct { + list *list.List + table map[interface{}]*list.Element + + limit uint + size uint +} + +func NewLRUCache(limit uint) *LRUCache { + return &LRUCache{ + list: list.New(), + table: map[interface{}]*list.Element{}, + + limit: limit, + } +} + +func (c *LRUCache) Has(k interface{}) (has bool, err error) { + _, ok := c.table[k] + return ok, nil +} + +func (c *LRUCache) Get(k interface{}) (v interface{}, ok bool, err error) { + element, ok := c.table[k] + if !ok { + return nil, false, nil + } + + c.moveToFront(element) + + return element.Value, true, nil +} + +func (c *LRUCache) Put(k, v interface{}) (replaced bool, err error) { + element, ok := c.table[k] + if ok { + c.updateInplace(element, v) + return true, nil + } + + c.addNew(k, v) + return false, nil +} + +func (c *LRUCache) PutIfAbsent(k, v interface{}) (put bool, err error) { + if _, ok := c.table[k]; ok { + return false, nil + } + + c.addNew(k, v) + return true, nil +} + +func (c *LRUCache) Delete(k interface{}) (deleted bool, err error) { + element, ok := c.table[k] + if !ok { + return false, nil + } + + c.list.Remove(element) + delete(c.table, k) + + return true, nil +} + +func (c *LRUCache) Clear() (cleared bool, err error) { + c.list.Init() + c.table = map[interface{}]*list.Element{} + c.size = 0 + + return true, nil +} + +func (c *LRUCache) updateInplace(e *list.Element, v interface{}) { + e.Value = v + c.moveToFront(e) + c.checkCapacity() +} + +func (c *LRUCache) moveToFront(e *list.Element) { + c.list.MoveToFront(e) +} + +func (c *LRUCache) addNew(k, v interface{}) { + c.table[k] = c.list.PushFront(v) + c.checkCapacity() +} + +func (c *LRUCache) checkCapacity() { + for c.size > c.limit { + delElem := c.list.Back() + v := delElem.Value + c.list.Remove(delElem) + delete(c.table, v) + } +} + +type SynchronizedCache struct { + mu sync.Mutex + c Cache +} + +func (c *SynchronizedCache) Put(k, v interface{}) (replaced bool, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.c.Put(k, v) +} +func (c *SynchronizedCache) PutIfAbsent(k, v interface{}) (put bool, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.PutIfAbsent(k, v) +} + +func (c *SynchronizedCache) Get(k interface{}) (v interface{}, ok bool, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.c.Get(k) +} +func (c *SynchronizedCache) Has(k interface{}) (ok bool, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.c.Has(k) +} + +func (c *SynchronizedCache) Delete(k interface{}) (deleted bool, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.c.Delete(k) +} + +func (c *SynchronizedCache) Clear() (cleared bool, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + return c.c.Clear() +} + +func NewSynchronizedCache(c Cache) *SynchronizedCache { + return &SynchronizedCache{ + c: c, + } +}