From 2c0951e80af5bc9238dd7036e7a90a93be6ebabe Mon Sep 17 00:00:00 2001 From: old-tom <892955278@qq.com> Date: Fri, 22 Dec 2023 14:56:26 +0800 Subject: [PATCH] first commit --- README.md | 0 cache.go | 18 +++++++++ fifo/fifo.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++ fifo/fifo_test.go | 10 +++++ go.mod | 5 +++ go.sum | 2 + len.go | 41 ++++++++++++++++++++ 7 files changed, 173 insertions(+) create mode 100644 README.md create mode 100644 cache.go create mode 100644 fifo/fifo.go create mode 100644 fifo/fifo_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 len.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..78e2283 --- /dev/null +++ b/cache.go @@ -0,0 +1,18 @@ +package cache + +type Cache interface { + // Set 设置/添加一个缓存,如果 key 存在,用新值覆盖旧值 + Set(key string, value any) + // Get 通过 key 获取一个缓存值 + Get(Key string) any + // Del 通过 key 删除一个缓存值 + Del(Key string) + // DelOldest 删除最“无用”的一个缓存值 + DelOldest() + // Len 获取缓存已存在的记录数 + Len() int +} + +type Value interface { + Len() int +} diff --git a/fifo/fifo.go b/fifo/fifo.go new file mode 100644 index 0000000..da0bccc --- /dev/null +++ b/fifo/fifo.go @@ -0,0 +1,97 @@ +package fifo + +import ( + "cache" + "container/list" +) + +/** + * 先进先出缓存 + */ +type fifo struct { + // 缓存最大容量 + maxBytes int + // 当一个 entry 从缓存中移除时调用该回调函数,默认为 nil + onEvicted func(key string, value any) + //已使用的字节数,只包括值,key 不算 + usedBytes int + //缓存容器,存放entry + ll *list.List + //指针容器 + cache map[string]*list.Element +} + +func New(maxBytes int, onEvicted func(key string, value any)) cache.Cache { + return &fifo{ + maxBytes: maxBytes, + onEvicted: onEvicted, + ll: list.New(), + cache: make(map[string]*list.Element), + } +} + +// Set 往 Cache 尾部增加一个元素(如果已经存在,则放入尾部,并修改值) +func (f *fifo) Set(key string, value any) { + //元素存在 + if e, ok := f.cache[key]; ok { + f.ll.MoveToBack(e) + en := e.Value.(*entry) + f.usedBytes = f.usedBytes - cache.CalcLen(en.value) + cache.CalcLen(value) + en.value = value + } else { + en := &entry{key, value} + e := f.ll.PushBack(en) + f.cache[key] = e + f.usedBytes += en.Len() + //元素容量校验 + if f.CheckOverFlow() { + f.DelOldest() + } + } +} + +// CheckOverFlow 校验容量是否溢出 +func (f *fifo) CheckOverFlow() bool { + return f.maxBytes > 0 && f.usedBytes > f.maxBytes +} +func (f *fifo) Get(key string) any { + if e, ok := f.cache[key]; ok { + return e.Value.(*entry).value + } + return nil +} +func (f *fifo) Del(key string) { + if e, ok := f.cache[key]; ok { + f.removeElement(e) + } +} +func (f *fifo) DelOldest() { + //删除列表头部元素 + f.removeElement(f.ll.Front()) +} +func (f *fifo) Len() int { + return f.ll.Len() +} + +// removeElement 删除元素 +func (f *fifo) removeElement(e *list.Element) { + if e == nil { + return + } + f.ll.Remove(e) + en := e.Value.(*entry) + f.usedBytes -= en.Len() + delete(f.cache, en.key) + if f.onEvicted != nil { + f.onEvicted(en.key, en.value) + } +} + +type entry struct { + key string + value any +} + +func (e *entry) Len() int { + return cache.CalcLen(e.value) +} diff --git a/fifo/fifo_test.go b/fifo/fifo_test.go new file mode 100644 index 0000000..05b3bd7 --- /dev/null +++ b/fifo/fifo_test.go @@ -0,0 +1,10 @@ +package fifo_test + +import ( + "github.com/matryer/is" + "testing" +) + +func TestFifo(t *testing.T) { + is.New() +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e573f5a --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module cache + +go 1.21.5 + +require github.com/matryer/is v1.4.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f95502a --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= +github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= diff --git a/len.go b/len.go new file mode 100644 index 0000000..8b55e2a --- /dev/null +++ b/len.go @@ -0,0 +1,41 @@ +package cache + +import ( + "fmt" + "runtime" +) + +func CalcLen(value any) int { + var n int + switch v := value.(type) { + case Value: + n = v.Len() + case string: + if runtime.GOARCH == "amd64" { + n = 16 + len(v) + } else { + n = 8 + len(v) + } + case bool, uint8, int8: + n = 1 + case int16, uint16: + n = 2 + case int32, uint32, float32: + n = 4 + case int64, uint64, float64: + n = 8 + case int, uint: + if runtime.GOARCH == "amd64" { + n = 8 + } else { + n = 4 + } + case complex64: + n = 8 + case complex128: + n = 16 + default: + panic(fmt.Sprintf("%T is not implement cache.Value", value)) + } + return n +}