diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-cache/v3/simple')
| -rw-r--r-- | vendor/codeberg.org/gruf/go-cache/v3/simple/cache.go | 454 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-cache/v3/simple/pool.go | 23 | 
2 files changed, 477 insertions, 0 deletions
| diff --git a/vendor/codeberg.org/gruf/go-cache/v3/simple/cache.go b/vendor/codeberg.org/gruf/go-cache/v3/simple/cache.go new file mode 100644 index 000000000..0224871bc --- /dev/null +++ b/vendor/codeberg.org/gruf/go-cache/v3/simple/cache.go @@ -0,0 +1,454 @@ +package simple + +import ( +	"sync" + +	"codeberg.org/gruf/go-maps" +) + +// Entry represents an item in the cache. +type Entry struct { +	Key   any +	Value any +} + +// Cache is the underlying Cache implementation, providing both the base Cache interface and unsafe access to underlying map to allow flexibility in building your own. +type Cache[Key comparable, Value any] struct { +	// Evict is the hook that is called when an item is evicted from the cache. +	Evict func(Key, Value) + +	// Invalid is the hook that is called when an item's data in the cache is invalidated, includes Add/Set. +	Invalid func(Key, Value) + +	// Cache is the underlying hashmap used for this cache. +	Cache maps.LRUMap[Key, *Entry] + +	// Embedded mutex. +	sync.Mutex +} + +// New returns a new initialized Cache with given initial length, maximum capacity and item TTL. +func New[K comparable, V any](len, cap int) *Cache[K, V] { +	c := new(Cache[K, V]) +	c.Init(len, cap) +	return c +} + +// Init will initialize this cache with given initial length, maximum capacity and item TTL. +func (c *Cache[K, V]) Init(len, cap int) { +	c.SetEvictionCallback(nil) +	c.SetInvalidateCallback(nil) +	c.Cache.Init(len, cap) +} + +// SetEvictionCallback: implements cache.Cache's SetEvictionCallback(). +func (c *Cache[K, V]) SetEvictionCallback(hook func(K, V)) { +	c.locked(func() { c.Evict = hook }) +} + +// SetInvalidateCallback: implements cache.Cache's SetInvalidateCallback(). +func (c *Cache[K, V]) SetInvalidateCallback(hook func(K, V)) { +	c.locked(func() { c.Invalid = hook }) +} + +// Get: implements cache.Cache's Get(). +func (c *Cache[K, V]) Get(key K) (V, bool) { +	var ( +		// did exist in cache? +		ok bool + +		// cached value. +		v V +	) + +	c.locked(func() { +		var item *Entry + +		// Check for item in cache +		item, ok = c.Cache.Get(key) +		if !ok { +			return +		} + +		// Set item value. +		v = item.Value.(V) +	}) + +	return v, ok +} + +// Add: implements cache.Cache's Add(). +func (c *Cache[K, V]) Add(key K, value V) bool { +	var ( +		// did exist in cache? +		ok bool + +		// was entry evicted? +		ev bool + +		// evicted key values. +		evcK K +		evcV V + +		// hook func ptrs. +		evict func(K, V) +	) + +	c.locked(func() { +		// Check if in cache. +		ok = c.Cache.Has(key) +		if ok { +			return +		} + +		// Alloc new entry. +		new := getEntry() +		new.Key = key +		new.Value = value + +		// Add new entry to cache and catched any evicted item. +		c.Cache.SetWithHook(key, new, func(_ K, item *Entry) { +			evcK = item.Key.(K) +			evcV = item.Value.(V) +			ev = true +			putEntry(item) +		}) + +		// Set hook func ptr. +		evict = c.Evict +	}) + +	if ev && evict != nil { +		// Pass to eviction hook. +		evict(evcK, evcV) +	} + +	return !ok +} + +// Set: implements cache.Cache's Set(). +func (c *Cache[K, V]) Set(key K, value V) { +	var ( +		// did exist in cache? +		ok bool + +		// was entry evicted? +		ev bool + +		// old value. +		oldV V + +		// evicted key values. +		evcK K +		evcV V + +		// hook func ptrs. +		invalid func(K, V) +		evict   func(K, V) +	) + +	c.locked(func() { +		var item *Entry + +		// Check for item in cache +		item, ok = c.Cache.Get(key) + +		if ok { +			// Set old value. +			oldV = item.Value.(V) + +			// Update the existing item. +			item.Value = value +		} else { +			// Alloc new entry. +			new := getEntry() +			new.Key = key +			new.Value = value + +			// Add new entry to cache and catched any evicted item. +			c.Cache.SetWithHook(key, new, func(_ K, item *Entry) { +				evcK = item.Key.(K) +				evcV = item.Value.(V) +				ev = true +				putEntry(item) +			}) +		} + +		// Set hook func ptrs. +		invalid = c.Invalid +		evict = c.Evict +	}) + +	if ok && invalid != nil { +		// Pass to invalidate hook. +		invalid(key, oldV) +	} + +	if ev && evict != nil { +		// Pass to eviction hook. +		evict(evcK, evcV) +	} +} + +// CAS: implements cache.Cache's CAS(). +func (c *Cache[K, V]) CAS(key K, old V, new V, cmp func(V, V) bool) bool { +	var ( +		// did exist in cache? +		ok bool + +		// swapped value. +		oldV V + +		// hook func ptrs. +		invalid func(K, V) +	) + +	c.locked(func() { +		var item *Entry + +		// Check for item in cache +		item, ok = c.Cache.Get(key) +		if !ok { +			return +		} + +		// Set old value. +		oldV = item.Value.(V) + +		// Perform the comparison +		if !cmp(old, oldV) { +			var zero V +			oldV = zero +			return +		} + +		// Update value. +		item.Value = new + +		// Set hook func ptr. +		invalid = c.Invalid +	}) + +	if ok && invalid != nil { +		// Pass to invalidate hook. +		invalid(key, oldV) +	} + +	return ok +} + +// Swap: implements cache.Cache's Swap(). +func (c *Cache[K, V]) Swap(key K, swp V) V { +	var ( +		// did exist in cache? +		ok bool + +		// swapped value. +		oldV V + +		// hook func ptrs. +		invalid func(K, V) +	) + +	c.locked(func() { +		var item *Entry + +		// Check for item in cache +		item, ok = c.Cache.Get(key) +		if !ok { +			return +		} + +		// Set old value. +		oldV = item.Value.(V) + +		// Update value. +		item.Value = swp + +		// Set hook func ptr. +		invalid = c.Invalid +	}) + +	if ok && invalid != nil { +		// Pass to invalidate hook. +		invalid(key, oldV) +	} + +	return oldV +} + +// Has: implements cache.Cache's Has(). +func (c *Cache[K, V]) Has(key K) (ok bool) { +	c.locked(func() { +		ok = c.Cache.Has(key) +	}) +	return +} + +// Invalidate: implements cache.Cache's Invalidate(). +func (c *Cache[K, V]) Invalidate(key K) (ok bool) { +	var ( +		// old value. +		oldV V + +		// hook func ptrs. +		invalid func(K, V) +	) + +	c.locked(func() { +		var item *Entry + +		// Check for item in cache +		item, ok = c.Cache.Get(key) +		if !ok { +			return +		} + +		// Set old value. +		oldV = item.Value.(V) + +		// Remove from cache map +		_ = c.Cache.Delete(key) + +		// Free entry +		putEntry(item) + +		// Set hook func ptrs. +		invalid = c.Invalid +	}) + +	if ok && invalid != nil { +		// Pass to invalidate hook. +		invalid(key, oldV) +	} + +	return +} + +// InvalidateAll: implements cache.Cache's InvalidateAll(). +func (c *Cache[K, V]) InvalidateAll(keys ...K) (ok bool) { +	var ( +		// deleted items. +		items []*Entry + +		// hook func ptrs. +		invalid func(K, V) +	) + +	// Allocate a slice for invalidated. +	items = make([]*Entry, 0, len(keys)) + +	c.locked(func() { +		for x := range keys { +			var item *Entry + +			// Check for item in cache +			item, ok = c.Cache.Get(keys[x]) +			if !ok { +				continue +			} + +			// Append this old value. +			items = append(items, item) + +			// Remove from cache map +			_ = c.Cache.Delete(keys[x]) +		} + +		// Set hook func ptrs. +		invalid = c.Invalid +	}) + +	if invalid != nil { +		for x := range items { +			// Pass to invalidate hook. +			k := items[x].Key.(K) +			v := items[x].Value.(V) +			invalid(k, v) + +			// Free this entry. +			putEntry(items[x]) +		} +	} + +	return +} + +// Clear: implements cache.Cache's Clear(). +func (c *Cache[K, V]) Clear() { c.Trim(100) } + +// Trim will truncate the cache to ensure it stays within given percentage of total capacity. +func (c *Cache[K, V]) Trim(perc float64) { +	var ( +		// deleted items +		items []*Entry + +		// hook func ptrs. +		invalid func(K, V) +	) + +	c.locked(func() { +		// Calculate number of cache items to truncate. +		max := (perc / 100) * float64(c.Cache.Cap()) +		diff := c.Cache.Len() - int(max) +		if diff <= 0 { +			return +		} + +		// Set hook func ptr. +		invalid = c.Invalid + +		// Truncate by calculated length. +		items = c.truncate(diff, invalid) +	}) + +	if invalid != nil { +		for x := range items { +			// Pass to invalidate hook. +			k := items[x].Key.(K) +			v := items[x].Value.(V) +			invalid(k, v) + +			// Free this entry. +			putEntry(items[x]) +		} +	} +} + +// Len: implements cache.Cache's Len(). +func (c *Cache[K, V]) Len() (l int) { +	c.locked(func() { l = c.Cache.Len() }) +	return +} + +// Cap: implements cache.Cache's Cap(). +func (c *Cache[K, V]) Cap() (l int) { +	c.locked(func() { l = c.Cache.Cap() }) +	return +} + +// locked performs given function within mutex lock (NOTE: UNLOCK IS NOT DEFERRED). +func (c *Cache[K, V]) locked(fn func()) { +	c.Lock() +	fn() +	c.Unlock() +} + +// truncate will truncate the cache by given size, returning deleted items. +func (c *Cache[K, V]) truncate(sz int, hook func(K, V)) []*Entry { +	if hook == nil { +		// No hook to execute, simply release all truncated entries. +		c.Cache.Truncate(sz, func(_ K, item *Entry) { putEntry(item) }) +		return nil +	} + +	// Allocate a slice for deleted. +	deleted := make([]*Entry, 0, sz) + +	// Truncate and catch all deleted k-v pairs. +	c.Cache.Truncate(sz, func(_ K, item *Entry) { +		deleted = append(deleted, item) +	}) + +	return deleted +} diff --git a/vendor/codeberg.org/gruf/go-cache/v3/simple/pool.go b/vendor/codeberg.org/gruf/go-cache/v3/simple/pool.go new file mode 100644 index 000000000..2fc99ab0f --- /dev/null +++ b/vendor/codeberg.org/gruf/go-cache/v3/simple/pool.go @@ -0,0 +1,23 @@ +package simple + +import "sync" + +// entryPool is a global pool for Entry +// objects, regardless of cache type. +var entryPool sync.Pool + +// getEntry fetches an Entry from pool, or allocates new. +func getEntry() *Entry { +	v := entryPool.Get() +	if v == nil { +		return new(Entry) +	} +	return v.(*Entry) +} + +// putEntry replaces an Entry in the pool. +func putEntry(e *Entry) { +	e.Key = nil +	e.Value = nil +	entryPool.Put(e) +} | 
