123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- // Copyright (c) 2014-present José Carlos Nieto, https://menteslibres.net/xiam
- //
- // Permission is hereby granted, free of charge, to any person obtaining
- // a copy of this software and associated documentation files (the
- // "Software"), to deal in the Software without restriction, including
- // without limitation the rights to use, copy, modify, merge, publish,
- // distribute, sublicense, and/or sell copies of the Software, and to
- // permit persons to whom the Software is furnished to do so, subject to
- // the following conditions:
- //
- // The above copyright notice and this permission notice shall be
- // included in all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- package cache
-
- import (
- "container/list"
- "errors"
- "fmt"
- "strconv"
- "sync"
-
- "upper.io/db.v3/internal/cache/hashstructure"
- )
-
- const defaultCapacity = 128
-
- // Cache holds a map of volatile key -> values.
- type Cache struct {
- cache map[string]*list.Element
- li *list.List
- capacity int
- mu sync.RWMutex
- }
-
- type item struct {
- key string
- value interface{}
- }
-
- // NewCacheWithCapacity initializes a new caching space with the given
- // capacity.
- func NewCacheWithCapacity(capacity int) (*Cache, error) {
- if capacity < 1 {
- return nil, errors.New("Capacity must be greater than zero.")
- }
- return &Cache{
- cache: make(map[string]*list.Element),
- li: list.New(),
- capacity: capacity,
- }, nil
- }
-
- // NewCache initializes a new caching space with default settings.
- func NewCache() *Cache {
- c, err := NewCacheWithCapacity(defaultCapacity)
- if err != nil {
- panic(err.Error()) // Should never happen as we're not providing a negative defaultCapacity.
- }
- return c
- }
-
- // Read attempts to retrieve a cached value as a string, if the value does not
- // exists returns an empty string and false.
- func (c *Cache) Read(h Hashable) (string, bool) {
- if v, ok := c.ReadRaw(h); ok {
- if s, ok := v.(string); ok {
- return s, true
- }
- }
- return "", false
- }
-
- // ReadRaw attempts to retrieve a cached value as an interface{}, if the value
- // does not exists returns nil and false.
- func (c *Cache) ReadRaw(h Hashable) (interface{}, bool) {
- c.mu.RLock()
- defer c.mu.RUnlock()
- data, ok := c.cache[h.Hash()]
- if ok {
- return data.Value.(*item).value, true
- }
- return nil, false
- }
-
- // Write stores a value in memory. If the value already exists its overwritten.
- func (c *Cache) Write(h Hashable, value interface{}) {
- key := h.Hash()
-
- c.mu.Lock()
- defer c.mu.Unlock()
-
- if el, ok := c.cache[key]; ok {
- el.Value.(*item).value = value
- c.li.MoveToFront(el)
- return
- }
-
- c.cache[key] = c.li.PushFront(&item{key, value})
-
- for c.li.Len() > c.capacity {
- el := c.li.Remove(c.li.Back())
- delete(c.cache, el.(*item).key)
- if p, ok := el.(*item).value.(HasOnPurge); ok {
- p.OnPurge()
- }
- }
- }
-
- // Clear generates a new memory space, leaving the old memory unreferenced, so
- // it can be claimed by the garbage collector.
- func (c *Cache) Clear() {
- c.mu.Lock()
- defer c.mu.Unlock()
- for _, el := range c.cache {
- if p, ok := el.Value.(*item).value.(HasOnPurge); ok {
- p.OnPurge()
- }
- }
- c.cache = make(map[string]*list.Element)
- c.li.Init()
- }
-
- // Hash returns a hash of the given struct.
- func Hash(v interface{}) string {
- q, err := hashstructure.Hash(v, nil)
- if err != nil {
- panic(fmt.Sprintf("Could not hash struct: ", err.Error()))
- }
- return strconv.FormatUint(q, 10)
- }
-
- type hash struct {
- name string
- }
-
- func (h *hash) Hash() string {
- return h.name
- }
-
- // String returns a Hashable that produces a hash equal to the given string.
- func String(s string) Hashable {
- return &hash{s}
- }
|