mirror of
https://gitclone.com/github.com/MetaCubeX/Clash.Meta
synced 2025-02-23 20:52:15 +08:00
chore: cleanup code
This commit is contained in:
parent
ac381736a5
commit
147400fbe0
@ -29,7 +29,6 @@ type ARC[K comparable, V any] struct {
|
|||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
len int
|
len int
|
||||||
cache map[K]*entry[K, V]
|
cache map[K]*entry[K, V]
|
||||||
staleReturn bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Adaptive Replacement Cache (ARC).
|
// New returns a new Adaptive Replacement Cache (ARC).
|
||||||
@ -74,21 +73,23 @@ func (a *ARC[K, V]) SetWithExpire(key K, value V, expires time.Time) {
|
|||||||
|
|
||||||
func (a *ARC[K, V]) setWithExpire(key K, value V, expires time.Time) {
|
func (a *ARC[K, V]) setWithExpire(key K, value V, expires time.Time) {
|
||||||
ent, ok := a.cache[key]
|
ent, ok := a.cache[key]
|
||||||
if ok != true {
|
if !ok {
|
||||||
a.len++
|
a.len++
|
||||||
ent := &entry[K, V]{key: key, value: value, ghost: false, expires: expires.Unix()}
|
ent := &entry[K, V]{key: key, value: value, ghost: false, expires: expires.Unix()}
|
||||||
a.req(ent)
|
a.req(ent)
|
||||||
a.cache[key] = ent
|
a.cache[key] = ent
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if ent.ghost {
|
if ent.ghost {
|
||||||
a.len++
|
a.len++
|
||||||
}
|
}
|
||||||
|
|
||||||
ent.value = value
|
ent.value = value
|
||||||
ent.ghost = false
|
ent.ghost = false
|
||||||
ent.expires = expires.Unix()
|
ent.expires = expires.Unix()
|
||||||
a.req(ent)
|
a.req(ent)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves a previously via Set inserted entry.
|
// Get retrieves a previously via Set inserted entry.
|
||||||
// This optimizes future access to this entry (side effect).
|
// This optimizes future access to this entry (side effect).
|
||||||
@ -97,25 +98,25 @@ func (a *ARC[K, V]) Get(key K) (value V, ok bool) {
|
|||||||
defer a.mutex.Unlock()
|
defer a.mutex.Unlock()
|
||||||
|
|
||||||
ent, ok := a.get(key)
|
ent, ok := a.get(key)
|
||||||
if ok {
|
if !ok {
|
||||||
return ent.value, true
|
|
||||||
}
|
|
||||||
return lo.Empty[V](), false
|
return lo.Empty[V](), false
|
||||||
}
|
}
|
||||||
|
return ent.value, true
|
||||||
|
}
|
||||||
|
|
||||||
func (a *ARC[K, V]) get(key K) (e *entry[K, V], ok bool) {
|
func (a *ARC[K, V]) get(key K) (e *entry[K, V], ok bool) {
|
||||||
ent, ok := a.cache[key]
|
ent, ok := a.cache[key]
|
||||||
if ok {
|
if !ok {
|
||||||
|
return ent, false
|
||||||
|
}
|
||||||
a.req(ent)
|
a.req(ent)
|
||||||
return ent, !ent.ghost
|
return ent, !ent.ghost
|
||||||
}
|
}
|
||||||
return ent, false
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWithExpire returns any representation of a cached response,
|
// GetWithExpire returns any representation of a cached response,
|
||||||
// a time.Time Give expected expires,
|
// a time.Time Give expected expires,
|
||||||
// and a bool set to true if the key was found.
|
// and a bool set to true if the key was found.
|
||||||
// This method will NOT check the maxAge of element and will NOT update the expires.
|
// This method will NOT update the expires.
|
||||||
func (a *ARC[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
|
func (a *ARC[K, V]) GetWithExpire(key K) (V, time.Time, bool) {
|
||||||
a.mutex.Lock()
|
a.mutex.Lock()
|
||||||
defer a.mutex.Unlock()
|
defer a.mutex.Unlock()
|
||||||
@ -138,10 +139,11 @@ func (a *ARC[K, V]) Len() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *ARC[K, V]) req(ent *entry[K, V]) {
|
func (a *ARC[K, V]) req(ent *entry[K, V]) {
|
||||||
if ent.ll == a.t1 || ent.ll == a.t2 {
|
switch {
|
||||||
|
case ent.ll == a.t1 || ent.ll == a.t2:
|
||||||
// Case I
|
// Case I
|
||||||
ent.setMRU(a.t2)
|
ent.setMRU(a.t2)
|
||||||
} else if ent.ll == a.b1 {
|
case ent.ll == a.b1:
|
||||||
// Case II
|
// Case II
|
||||||
// Cache Miss in t1 and t2
|
// Cache Miss in t1 and t2
|
||||||
|
|
||||||
@ -152,16 +154,11 @@ func (a *ARC[K, V]) req(ent *entry[K, V]) {
|
|||||||
} else {
|
} else {
|
||||||
d = a.b2.Len() / a.b1.Len()
|
d = a.b2.Len() / a.b1.Len()
|
||||||
}
|
}
|
||||||
|
a.p = min(a.p+d, a.c)
|
||||||
// a.p = min(a.p+d, a.c)
|
|
||||||
a.p = a.p + d
|
|
||||||
if a.c < a.p {
|
|
||||||
a.p = a.c
|
|
||||||
}
|
|
||||||
|
|
||||||
a.replace(ent)
|
a.replace(ent)
|
||||||
ent.setMRU(a.t2)
|
ent.setMRU(a.t2)
|
||||||
} else if ent.ll == a.b2 {
|
case ent.ll == a.b2:
|
||||||
// Case III
|
// Case III
|
||||||
// Cache Miss in t1 and t2
|
// Cache Miss in t1 and t2
|
||||||
|
|
||||||
@ -172,35 +169,30 @@ func (a *ARC[K, V]) req(ent *entry[K, V]) {
|
|||||||
} else {
|
} else {
|
||||||
d = a.b1.Len() / a.b2.Len()
|
d = a.b1.Len() / a.b2.Len()
|
||||||
}
|
}
|
||||||
//a.p = max(a.p-d, 0)
|
a.p = max(a.p-d, 0)
|
||||||
a.p = a.p - d
|
|
||||||
if a.p < 0 {
|
|
||||||
a.p = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
a.replace(ent)
|
a.replace(ent)
|
||||||
ent.setMRU(a.t2)
|
ent.setMRU(a.t2)
|
||||||
} else if ent.ll == nil {
|
case ent.ll == nil && a.t1.Len()+a.b1.Len() == a.c:
|
||||||
// Case IV
|
// Case IV A
|
||||||
|
|
||||||
if a.t1.Len()+a.b1.Len() == a.c {
|
|
||||||
// Case A
|
|
||||||
if a.t1.Len() < a.c {
|
if a.t1.Len() < a.c {
|
||||||
a.delLRU(a.b1)
|
a.delLRU(a.b1)
|
||||||
a.replace(ent)
|
a.replace(ent)
|
||||||
} else {
|
} else {
|
||||||
a.delLRU(a.t1)
|
a.delLRU(a.t1)
|
||||||
}
|
}
|
||||||
} else if a.t1.Len()+a.b1.Len() < a.c {
|
ent.setMRU(a.t1)
|
||||||
// Case B
|
case ent.ll == nil && a.t1.Len()+a.b1.Len() < a.c:
|
||||||
|
// Case IV B
|
||||||
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
|
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {
|
||||||
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
|
if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {
|
||||||
a.delLRU(a.b2)
|
a.delLRU(a.b2)
|
||||||
}
|
}
|
||||||
a.replace(ent)
|
a.replace(ent)
|
||||||
}
|
}
|
||||||
}
|
ent.setMRU(a.t1)
|
||||||
|
case ent.ll == nil:
|
||||||
|
// Case IV, not A nor B
|
||||||
ent.setMRU(a.t1)
|
ent.setMRU(a.t1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,3 +219,17 @@ func (a *ARC[K, V]) replace(ent *entry[K, V]) {
|
|||||||
lru.setMRU(a.b2)
|
lru.setMRU(a.b2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(a int, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
@ -1,59 +1,105 @@
|
|||||||
package arc
|
package arc
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
func TestInsertion(t *testing.T) {
|
||||||
cache := New[string, string](WithSize[string, string](3))
|
cache := New[string, string](WithSize[string, string](3))
|
||||||
if cache.Len() != 0 {
|
if got, want := cache.Len(), 0; got != want {
|
||||||
t.Error("Empty cache should have length 0")
|
t.Errorf("empty cache.Len(): got %d want %d", cache.Len(), want)
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.Set("Hello", "World")
|
const (
|
||||||
if cache.Len() != 1 {
|
k1 = "Hello"
|
||||||
t.Error("Cache should have length 1")
|
k2 = "Hallo"
|
||||||
|
k3 = "Ciao"
|
||||||
|
k4 = "Salut"
|
||||||
|
|
||||||
|
v1 = "World"
|
||||||
|
v2 = "Worlds"
|
||||||
|
v3 = "Welt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Insert the first value
|
||||||
|
cache.Set(k1, v1)
|
||||||
|
if got, want := cache.Len(), 1; got != want {
|
||||||
|
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
|
||||||
|
}
|
||||||
|
if got, ok := cache.Get(k1); !ok || got != v1 {
|
||||||
|
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", k1, got, ok, v1)
|
||||||
}
|
}
|
||||||
|
|
||||||
var val interface{}
|
// Replace existing value for a given key
|
||||||
var ok bool
|
cache.Set(k1, v2)
|
||||||
|
if got, want := cache.Len(), 1; got != want {
|
||||||
if val, ok = cache.Get("Hello"); val != "World" || ok != true {
|
t.Errorf("re-insertion: cache.Len(): got %d want %d", cache.Len(), want)
|
||||||
t.Error("Didn't set \"Hello\" to \"World\"")
|
}
|
||||||
|
if got, ok := cache.Get(k1); !ok || got != v2 {
|
||||||
|
t.Errorf("re-insertion: cache.Get(%v): got (%v,%t) want (%v,true)", k1, got, ok, v2)
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.Set("Hello", "World1")
|
// Add a second different key
|
||||||
if cache.Len() != 1 {
|
cache.Set(k2, v3)
|
||||||
t.Error("Inserting the same entry multiple times shouldn't increase cache size")
|
if got, want := cache.Len(), 2; got != want {
|
||||||
|
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
|
||||||
|
}
|
||||||
|
if got, ok := cache.Get(k1); !ok || got != v2 {
|
||||||
|
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", k1, got, ok, v2)
|
||||||
|
}
|
||||||
|
if got, ok := cache.Get(k2); !ok || got != v3 {
|
||||||
|
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", k2, got, ok, v3)
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok = cache.Get("Hello"); val != "World1" || ok != true {
|
// Fill cache
|
||||||
t.Error("Didn't update \"Hello\" to \"World1\"")
|
cache.Set(k3, v1)
|
||||||
|
if got, want := cache.Len(), 3; got != want {
|
||||||
|
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
|
||||||
}
|
}
|
||||||
|
|
||||||
cache.Set("Hallo", "Welt")
|
// Exceed size, this should not exceed size:
|
||||||
if cache.Len() != 2 {
|
cache.Set(k4, v1)
|
||||||
t.Error("Inserting two different entries should result into lenght=2")
|
if got, want := cache.Len(), 3; got != want {
|
||||||
}
|
t.Errorf("insertion of key out of size: cache.Len(): got %d want %d", cache.Len(), want)
|
||||||
|
|
||||||
if val, ok = cache.Get("Hallo"); val != "Welt" || ok != true {
|
|
||||||
t.Error("Didn't set \"Hallo\" to \"Welt\"")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicReplace(t *testing.T) {
|
func TestEviction(t *testing.T) {
|
||||||
cache := New[string, string](WithSize[string, string](3))
|
size := 3
|
||||||
|
cache := New[string, string](WithSize[string, string](size))
|
||||||
cache.Set("Hello", "Hallo")
|
if got, want := cache.Len(), 0; got != want {
|
||||||
cache.Set("World", "Welt")
|
t.Errorf("empty cache.Len(): got %d want %d", cache.Len(), want)
|
||||||
cache.Get("World")
|
|
||||||
cache.Set("Cache", "Cache")
|
|
||||||
cache.Set("Replace", "Ersetzen")
|
|
||||||
|
|
||||||
value, ok := cache.Get("World")
|
|
||||||
if !ok || value != "Welt" {
|
|
||||||
t.Error("ARC should have replaced \"Hello\"")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if cache.Len() != 3 {
|
tests := []struct {
|
||||||
t.Error("ARC should have a maximum size of 3")
|
k, v string
|
||||||
|
}{
|
||||||
|
{"k1", "v1"},
|
||||||
|
{"k2", "v2"},
|
||||||
|
{"k3", "v3"},
|
||||||
|
{"k4", "v4"},
|
||||||
|
}
|
||||||
|
for i, tt := range tests[:size] {
|
||||||
|
cache.Set(tt.k, tt.v)
|
||||||
|
if got, want := cache.Len(), i+1; got != want {
|
||||||
|
t.Errorf("insertion of key #%d: cache.Len(): got %d want %d", want, cache.Len(), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exceed size and check we don't outgrow it:
|
||||||
|
cache.Set(tests[size].k, tests[size].v)
|
||||||
|
if got := cache.Len(); got != size {
|
||||||
|
t.Errorf("insertion of overflow key #%d: cache.Len(): got %d want %d", 4, cache.Len(), size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that LRU got evicted:
|
||||||
|
if got, ok := cache.Get(tests[0].k); ok || got != "" {
|
||||||
|
t.Errorf("cache.Get(%v): got (%v,%t) want (<nil>,true)", tests[0].k, got, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests[1:] {
|
||||||
|
if got, ok := cache.Get(tt.k); !ok || got != tt.v {
|
||||||
|
t.Errorf("cache.Get(%v): got (%v,%t) want (%v,true)", tt.k, got, ok, tt.v)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ func New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {
|
|||||||
return lc
|
return lc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the any representation of a cached response and a bool
|
// Get returns any representation of a cached response and a bool
|
||||||
// set to true if the key was found.
|
// set to true if the key was found.
|
||||||
func (c *LruCache[K, V]) Get(key K) (V, bool) {
|
func (c *LruCache[K, V]) Get(key K) (V, bool) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
@ -110,7 +110,7 @@ func (c *LruCache[K, V]) GetOrStore(key K, constructor func() V) (V, bool) {
|
|||||||
return value, true
|
return value, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWithExpire returns the any representation of a cached response,
|
// GetWithExpire returns any representation of a cached response,
|
||||||
// a time.Time Give expected expires,
|
// a time.Time Give expected expires,
|
||||||
// and a bool set to true if the key was found.
|
// and a bool set to true if the key was found.
|
||||||
// This method will NOT check the maxAge of element and will NOT update the expires.
|
// This method will NOT check the maxAge of element and will NOT update the expires.
|
||||||
@ -135,7 +135,7 @@ func (c *LruCache[K, V]) Exist(key K) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stores the any representation of a response for a given key.
|
// Set stores any representation of a response for a given key.
|
||||||
func (c *LruCache[K, V]) Set(key K, value V) {
|
func (c *LruCache[K, V]) Set(key K, value V) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
@ -151,7 +151,7 @@ func (c *LruCache[K, V]) set(key K, value V) {
|
|||||||
c.setWithExpire(key, value, time.Unix(expires, 0))
|
c.setWithExpire(key, value, time.Unix(expires, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWithExpire stores the any representation of a response for a given key and given expires.
|
// SetWithExpire stores any representation of a response for a given key and given expires.
|
||||||
// The expires time will round to second.
|
// The expires time will round to second.
|
||||||
func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
|
func (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
|
Loading…
Reference in New Issue
Block a user