gopass/internal/cache/inmem.go
Dominik Schulz 98deea8304
age: Ignore not-existing .ssh directory (#2347)
* age: Ignore not-existing .ssh directory

Fixes #2333

RELEASE_NOTES=[BUGFIX] Ignore not-existing .ssh dir

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

* Replace racy time dependent test with a properly mocked time.Now one

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

* Avoid another conurrency issue

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>

Signed-off-by: Dominik Schulz <dominik.schulz@gauner.org>
2022-09-24 07:24:55 +02:00

119 lines
2.1 KiB
Go

package cache
import (
"sync"
"time"
)
type cacheEntry[V any] struct {
value V
maxExpire time.Time
expire time.Time
created time.Time
now func() time.Time
}
func (ce *cacheEntry[V]) isExpired() bool {
if ce.now().After(ce.maxExpire) {
return true
}
if ce.now().After(ce.expire) {
return true
}
return false
}
// InMemTTL implements a simple TTLed cache in memory. It is concurrency safe.
type InMemTTL[K comparable, V any] struct {
sync.Mutex
now func() time.Time
ttl time.Duration
maxTTL time.Duration
entries map[K]cacheEntry[V]
}
// NewInMemTTL creates a new TTLed cache.
func NewInMemTTL[K comparable, V any](ttl time.Duration, maxTTL time.Duration) *InMemTTL[K, V] {
return &InMemTTL[K, V]{
now: time.Now,
ttl: ttl,
maxTTL: maxTTL,
}
}
// Get retrieves a single entry, extending it's TTL.
func (c *InMemTTL[K, V]) Get(key K) (V, bool) {
c.Lock()
defer c.Unlock()
var zero V
if c.entries == nil {
return zero, false
}
ce, found := c.entries[key]
if !found {
// not found
return zero, false
}
if ce.isExpired() {
// expired
return zero, false
}
ce.expire = c.now().Add(c.ttl)
c.entries[key] = ce
return ce.value, true
}
// purgeExpire will remove expired entries. It is called by Set.
func (c *InMemTTL[K, V]) purgeExpired() {
for k, ce := range c.entries {
if ce.isExpired() {
delete(c.entries, k)
}
}
}
// Set creates or overwrites an entry.
func (c *InMemTTL[K, V]) Set(key K, value V) {
c.Lock()
defer c.Unlock()
if c.entries == nil {
c.entries = make(map[K]cacheEntry[V], 10)
}
now := c.now()
c.entries[key] = cacheEntry[V]{
value: value,
maxExpire: now.Add(c.maxTTL),
expire: now.Add(c.ttl),
created: now,
now: func() time.Time {
return c.now()
},
}
c.purgeExpired()
}
// Remove removes a single entry from the cache.
func (c *InMemTTL[K, V]) Remove(key K) {
c.Lock()
defer c.Unlock()
delete(c.entries, key)
}
// Purge removes all entries from the cache.
func (c *InMemTTL[K, V]) Purge() {
c.Lock()
defer c.Unlock()
c.entries = make(map[K]cacheEntry[V], 10)
}