finclip-app-manager/vendor/github.com/bluele/gcache/simple.go

308 lines
6.3 KiB
Go
Raw Normal View History

2023-11-02 18:36:36 +08:00
package gcache
import (
"time"
)
// SimpleCache has no clear priority for evict cache. It depends on key-value map order.
type SimpleCache struct {
baseCache
items map[interface{}]*simpleItem
}
func newSimpleCache(cb *CacheBuilder) *SimpleCache {
c := &SimpleCache{}
buildCache(&c.baseCache, cb)
c.init()
c.loadGroup.cache = c
return c
}
func (c *SimpleCache) init() {
if c.size <= 0 {
c.items = make(map[interface{}]*simpleItem)
} else {
c.items = make(map[interface{}]*simpleItem, c.size)
}
}
// Set a new key-value pair
func (c *SimpleCache) Set(key, value interface{}) error {
c.mu.Lock()
defer c.mu.Unlock()
_, err := c.set(key, value)
return err
}
// Set a new key-value pair with an expiration time
func (c *SimpleCache) SetWithExpire(key, value interface{}, expiration time.Duration) error {
c.mu.Lock()
defer c.mu.Unlock()
item, err := c.set(key, value)
if err != nil {
return err
}
t := c.clock.Now().Add(expiration)
item.(*simpleItem).expiration = &t
return nil
}
func (c *SimpleCache) set(key, value interface{}) (interface{}, error) {
var err error
if c.serializeFunc != nil {
value, err = c.serializeFunc(key, value)
if err != nil {
return nil, err
}
}
// Check for existing item
item, ok := c.items[key]
if ok {
item.value = value
} else {
// Verify size not exceeded
if (len(c.items) >= c.size) && c.size > 0 {
c.evict(1)
}
item = &simpleItem{
clock: c.clock,
value: value,
}
c.items[key] = item
}
if c.expiration != nil {
t := c.clock.Now().Add(*c.expiration)
item.expiration = &t
}
if c.addedFunc != nil {
c.addedFunc(key, value)
}
return item, nil
}
// Get a value from cache pool using key if it exists.
// If it dose not exists key and has LoaderFunc,
// generate a value using `LoaderFunc` method returns value.
func (c *SimpleCache) Get(key interface{}) (interface{}, error) {
v, err := c.get(key, false)
if err == KeyNotFoundError {
return c.getWithLoader(key, true)
}
return v, err
}
// GetIFPresent gets a value from cache pool using key if it exists.
// If it dose not exists key, returns KeyNotFoundError.
// And send a request which refresh value for specified key if cache object has LoaderFunc.
func (c *SimpleCache) GetIFPresent(key interface{}) (interface{}, error) {
v, err := c.get(key, false)
if err == KeyNotFoundError {
return c.getWithLoader(key, false)
}
return v, nil
}
func (c *SimpleCache) get(key interface{}, onLoad bool) (interface{}, error) {
v, err := c.getValue(key, onLoad)
if err != nil {
return nil, err
}
if c.deserializeFunc != nil {
return c.deserializeFunc(key, v)
}
return v, nil
}
func (c *SimpleCache) getValue(key interface{}, onLoad bool) (interface{}, error) {
c.mu.Lock()
item, ok := c.items[key]
if ok {
if !item.IsExpired(nil) {
v := item.value
c.mu.Unlock()
if !onLoad {
c.stats.IncrHitCount()
}
return v, nil
}
c.remove(key)
}
c.mu.Unlock()
if !onLoad {
c.stats.IncrMissCount()
}
return nil, KeyNotFoundError
}
func (c *SimpleCache) getWithLoader(key interface{}, isWait bool) (interface{}, error) {
if c.loaderExpireFunc == nil {
return nil, KeyNotFoundError
}
value, _, err := c.load(key, func(v interface{}, expiration *time.Duration, e error) (interface{}, error) {
if e != nil {
return nil, e
}
c.mu.Lock()
defer c.mu.Unlock()
item, err := c.set(key, v)
if err != nil {
return nil, err
}
if expiration != nil {
t := c.clock.Now().Add(*expiration)
item.(*simpleItem).expiration = &t
}
return v, nil
}, isWait)
if err != nil {
return nil, err
}
return value, nil
}
func (c *SimpleCache) evict(count int) {
now := c.clock.Now()
current := 0
for key, item := range c.items {
if current >= count {
return
}
if item.expiration == nil || now.After(*item.expiration) {
defer c.remove(key)
current++
}
}
}
// Has checks if key exists in cache
func (c *SimpleCache) Has(key interface{}) bool {
c.mu.RLock()
defer c.mu.RUnlock()
now := time.Now()
return c.has(key, &now)
}
func (c *SimpleCache) has(key interface{}, now *time.Time) bool {
item, ok := c.items[key]
if !ok {
return false
}
return !item.IsExpired(now)
}
// Remove removes the provided key from the cache.
func (c *SimpleCache) Remove(key interface{}) bool {
c.mu.Lock()
defer c.mu.Unlock()
return c.remove(key)
}
func (c *SimpleCache) remove(key interface{}) bool {
item, ok := c.items[key]
if ok {
delete(c.items, key)
if c.evictedFunc != nil {
c.evictedFunc(key, item.value)
}
return true
}
return false
}
// Returns a slice of the keys in the cache.
func (c *SimpleCache) keys() []interface{} {
c.mu.RLock()
defer c.mu.RUnlock()
keys := make([]interface{}, len(c.items))
var i = 0
for k := range c.items {
keys[i] = k
i++
}
return keys
}
// GetALL returns all key-value pairs in the cache.
func (c *SimpleCache) GetALL(checkExpired bool) map[interface{}]interface{} {
c.mu.RLock()
defer c.mu.RUnlock()
items := make(map[interface{}]interface{}, len(c.items))
now := time.Now()
for k, item := range c.items {
if !checkExpired || c.has(k, &now) {
items[k] = item.value
}
}
return items
}
// Keys returns a slice of the keys in the cache.
func (c *SimpleCache) Keys(checkExpired bool) []interface{} {
c.mu.RLock()
defer c.mu.RUnlock()
keys := make([]interface{}, 0, len(c.items))
now := time.Now()
for k := range c.items {
if !checkExpired || c.has(k, &now) {
keys = append(keys, k)
}
}
return keys
}
// Len returns the number of items in the cache.
func (c *SimpleCache) Len(checkExpired bool) int {
c.mu.RLock()
defer c.mu.RUnlock()
if !checkExpired {
return len(c.items)
}
var length int
now := time.Now()
for k := range c.items {
if c.has(k, &now) {
length++
}
}
return length
}
// Completely clear the cache
func (c *SimpleCache) Purge() {
c.mu.Lock()
defer c.mu.Unlock()
if c.purgeVisitorFunc != nil {
for key, item := range c.items {
c.purgeVisitorFunc(key, item.value)
}
}
c.init()
}
type simpleItem struct {
clock Clock
value interface{}
expiration *time.Time
}
// IsExpired returns boolean value whether this item is expired or not.
func (si *simpleItem) IsExpired(now *time.Time) bool {
if si.expiration == nil {
return false
}
if now == nil {
t := si.clock.Now()
now = &t
}
return si.expiration.Before(*now)
}