mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2025-12-24 06:40:25 -05:00
95 lines
2.0 KiB
Go
95 lines
2.0 KiB
Go
package sync
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// Cache is a barebones cache implementation.
|
|
type Cache struct {
|
|
// capacity and length have to be the first words
|
|
// in order to be 64-aligned on 32-bit architectures.
|
|
capacity, length uint64 // access atomically
|
|
entries sync.Map
|
|
pool sync.Pool
|
|
}
|
|
|
|
// CacheEntry represents an entry on the cache. You can type assert on V.
|
|
type CacheEntry struct {
|
|
V interface{}
|
|
expiration time.Time
|
|
}
|
|
|
|
// NewCache returns a new instance of Cache.
|
|
func NewCache(capacity int) Cache {
|
|
return Cache{
|
|
capacity: uint64(capacity),
|
|
pool: sync.Pool{New: func() interface{} {
|
|
return new(CacheEntry)
|
|
}},
|
|
}
|
|
}
|
|
|
|
// Load loads an entry by given key
|
|
func (c *Cache) Load(key string) *CacheEntry {
|
|
if mapEntry, ok := c.entries.Load(key); ok {
|
|
entry := mapEntry.(*CacheEntry)
|
|
if c.expired(entry) {
|
|
c.entries.Delete(key)
|
|
return nil
|
|
}
|
|
return entry
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Store adds an entry for given key and value
|
|
func (c *Cache) Store(key string, val interface{}, expiration time.Time) {
|
|
if c.length > c.capacity {
|
|
c.evict()
|
|
}
|
|
|
|
poolEntry := c.pool.Get() //nolint: ifshort
|
|
if mapEntry, loaded := c.entries.LoadOrStore(key, poolEntry); loaded {
|
|
entry := mapEntry.(*CacheEntry)
|
|
entry.V = val
|
|
entry.expiration = expiration
|
|
|
|
c.pool.Put(poolEntry)
|
|
} else {
|
|
entry := poolEntry.(*CacheEntry)
|
|
entry.V = val
|
|
entry.expiration = expiration
|
|
|
|
atomic.AddUint64(&c.length, 1)
|
|
}
|
|
}
|
|
|
|
// Delete removes an entry by given key
|
|
func (c *Cache) Delete(key string) bool {
|
|
_, loaded := c.entries.LoadAndDelete(key)
|
|
|
|
if loaded {
|
|
atomic.AddUint64(&c.length, ^uint64(0))
|
|
}
|
|
|
|
return loaded
|
|
}
|
|
|
|
// evict frees memory from the cache by removing entries that exceeded the cache TTL.
|
|
func (c *Cache) evict() {
|
|
c.entries.Range(func(key, mapEntry interface{}) bool {
|
|
entry := mapEntry.(*CacheEntry)
|
|
if c.expired(entry) {
|
|
c.Delete(key.(string))
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
// expired checks if an entry is expired
|
|
func (c *Cache) expired(e *CacheEntry) bool {
|
|
return e.expiration.Before(time.Now())
|
|
}
|