108 lines
2.3 KiB
Go
108 lines
2.3 KiB
Go
package ccache
|
|
|
|
import (
|
|
"fmt"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type Sized interface {
|
|
Size() int64
|
|
}
|
|
|
|
type TrackedItem[T any] interface {
|
|
Value() T
|
|
Release()
|
|
Expired() bool
|
|
TTL() time.Duration
|
|
Expires() time.Time
|
|
Extend(duration time.Duration)
|
|
}
|
|
|
|
type Item[T any] struct {
|
|
key string
|
|
group string
|
|
promotions int32
|
|
refCount int32
|
|
expires int64
|
|
size int64
|
|
value T
|
|
next *Item[T]
|
|
prev *Item[T]
|
|
}
|
|
|
|
func newItem[T any](key string, value T, expires int64, track bool) *Item[T] {
|
|
size := int64(1)
|
|
|
|
// https://github.com/golang/go/issues/49206
|
|
if sized, ok := (interface{})(value).(Sized); ok {
|
|
size = sized.Size()
|
|
}
|
|
|
|
item := &Item[T]{
|
|
key: key,
|
|
value: value,
|
|
promotions: 0,
|
|
size: size,
|
|
expires: expires,
|
|
}
|
|
if track {
|
|
item.refCount = 1
|
|
}
|
|
return item
|
|
}
|
|
|
|
func (i *Item[T]) shouldPromote(getsPerPromote int32) bool {
|
|
i.promotions += 1
|
|
return i.promotions == getsPerPromote
|
|
}
|
|
|
|
func (i *Item[T]) Key() string {
|
|
return i.key
|
|
}
|
|
|
|
func (i *Item[T]) Value() T {
|
|
return i.value
|
|
}
|
|
|
|
func (i *Item[T]) track() {
|
|
atomic.AddInt32(&i.refCount, 1)
|
|
}
|
|
|
|
func (i *Item[T]) Release() {
|
|
atomic.AddInt32(&i.refCount, -1)
|
|
}
|
|
|
|
func (i *Item[T]) Expired() bool {
|
|
expires := atomic.LoadInt64(&i.expires)
|
|
return expires < time.Now().UnixNano()
|
|
}
|
|
|
|
func (i *Item[T]) TTL() time.Duration {
|
|
expires := atomic.LoadInt64(&i.expires)
|
|
return time.Nanosecond * time.Duration(expires-time.Now().UnixNano())
|
|
}
|
|
|
|
func (i *Item[T]) Expires() time.Time {
|
|
expires := atomic.LoadInt64(&i.expires)
|
|
return time.Unix(0, expires)
|
|
}
|
|
|
|
func (i *Item[T]) Extend(duration time.Duration) {
|
|
atomic.StoreInt64(&i.expires, time.Now().Add(duration).UnixNano())
|
|
}
|
|
|
|
// String returns a string representation of the Item. This includes the default string
|
|
// representation of its Value(), as implemented by fmt.Sprintf with "%v", but the exact
|
|
// format of the string should not be relied on; it is provided only for debugging
|
|
// purposes, and because otherwise including an Item in a call to fmt.Printf or
|
|
// fmt.Sprintf expression could cause fields of the Item to be read in a non-thread-safe
|
|
// way.
|
|
func (i *Item[T]) String() string {
|
|
group := i.group
|
|
if group == "" {
|
|
return fmt.Sprintf("Item(%s:%v)", i.key, i.value)
|
|
}
|
|
return fmt.Sprintf("Item(%s:%s:%v)", group, i.key, i.value)
|
|
}
|