This commit is contained in:
2024-08-17 12:04:33 +08:00
commit c13f9b8b82
12 changed files with 791 additions and 0 deletions

18
.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
# ide
/.idea
/.vscode
# test
/test
/demo
/debug
/config.yaml
/config.json
/config.toml
*.log
*_test.go
# build
/dist
/build
/release

14
LICENSE Normal file
View File

@@ -0,0 +1,14 @@
The Clear BSD License
Copyright (c) 2024 InTheForest
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of [Owner Organization] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
# cache
该库是go语言实现的低配版缓存库,类似redis超级阉割版本 \
该库只将淘汰时间精确到了秒 \
并且设计上并不支持高精度操作和储蓄大量数据(除非你有足够的内存和超高的性能)

108
cache.go Normal file
View File

@@ -0,0 +1,108 @@
package cache
import (
"fmt"
"strconv"
"sync"
"time"
)
type Cache[K comparable, V any] struct {
mu sync.RWMutex
data map[K]*Data[V] // 数据
cSet func(K, Data[V]) // 设置数据回调
cGet func(K, Data[V]) // 获取数据回调
cGetData func(K, Data[V]) // 获取数据内容回调
cGetTTL func(K, Data[V]) // 获取数据剩余时间回调
cDel func(K, Data[V]) // 删除数据回调
cUpData func(K, Data[V]) // 更新数据内容回调
cUpTTL func(K, Data[V]) // 更新数据剩余时间回调
fatal func(error) // 致命错误执行的内容
gc func() // 自动清理过期数据
persist func() // 持久化数据
is64Bit bool // 是否为64位系统
}
type Data[V any] struct {
End time.Time // 过期时间 v.End.IsZero()=true表示永不过期
Val V // 数据
}
// NewCache 创建一个新的缓存实例
// K: 键的类型(可比较) V: 值的类型(允许any)
func NewCache[K comparable, V any]() *Cache[K, V] {
defFunc := func(k K, d Data[V]) {}
c := &Cache[K, V]{
data: make(map[K]*Data[V]),
cSet: defFunc,
cGet: defFunc,
cGetData: defFunc,
cGetTTL: defFunc,
cDel: defFunc,
cUpData: defFunc,
cUpTTL: defFunc,
fatal: func(err error) { fmt.Println(err) },
}
c.gc = c.collection()
c.is64Bit = strconv.IntSize == 64
return c
}
// 自动清理过期数据
func (c *Cache[K, V]) collection() func() {
ticker := time.NewTicker(time.Second)
stopChan := make(chan struct{})
go func() {
for {
select {
case <-stopChan:
return
case <-ticker.C:
now := time.Now()
var dKeys []K
c.mu.RLock()
for k, v := range c.data {
if v.End.IsZero() && v.End.Before(now) {
dKeys = append(dKeys, k)
}
}
c.mu.RUnlock()
// 删除过期数据
if len(dKeys) > 0 {
c.mu.Lock()
for _, k := range dKeys {
if c.cDel != nil {
c.cDel(k, *c.data[k])
}
delete(c.data, k)
}
c.mu.Unlock()
}
}
}
}()
return func() {
ticker.Stop()
stopChan <- struct{}{}
}
}
func (c *Cache[K, V]) SetFatalFunc(f func(error)) {
c.mu.Lock()
defer c.mu.Unlock()
c.fatal = f
}
// Destroy 销毁缓存实例
// 关闭缓存实例
func (c *Cache[K, V]) Destroy() {
c.gc()
if c.persist != nil {
c.persist()
}
return
}

51
callback.go Normal file
View File

@@ -0,0 +1,51 @@
package cache
type SetCallback[K comparable, V any] struct {
cache *Cache[K, V]
}
func (c *Cache[K, V]) SetCallBack() SetCallback[K, V] {
return SetCallback[K, V]{cache: c}
}
func (c SetCallback[K, V]) SetSet(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cSet = f
}
func (c SetCallback[K, V]) SetGet(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cGet = f
}
func (c SetCallback[K, V]) SetGetData(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cGetData = f
}
func (c SetCallback[K, V]) SetGetTTL(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cGetTTL = f
}
func (c SetCallback[K, V]) SetDel(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cDel = f
}
func (c SetCallback[K, V]) SetUpData(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cUpData = f
}
func (c SetCallback[K, V]) SetUpTTL(f func(K, Data[V])) {
c.cache.mu.Lock()
defer c.cache.mu.Unlock()
c.cache.cUpTTL = f
}

22
del.go Normal file
View File

@@ -0,0 +1,22 @@
package cache
func (c *Cache[K, V]) del(key K) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, nil
}
if c.cDel != nil {
c.cDel(key, *c.data[key])
}
delete(c.data, key)
return true, nil
}
// Del 删除数据
// key: 键 返回值: 是否删除成功 错误
func (c *Cache[K, V]) Del(key K) (bool, error) {
return c.del(key)
}

307
get.go Normal file
View File

@@ -0,0 +1,307 @@
package cache
import (
"errors"
"reflect"
"strconv"
"time"
)
/*
输出时间为-1或者time类型为IsZero()表示永不过期
*/
// Get 获取数据
// key: 键 返回值: 数据 剩余时间 是否存在
func (c *Cache[K, V]) Get(key K) (V, int64, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
if data, ok := c.data[key]; ok {
if data.End.IsZero() {
return data.Val, -1, true
} else if data.End.Before(time.Now()) {
return data.Val, int64(data.End.Sub(time.Now()).Seconds()), true
}
}
return c.zero(), 0, false
}
// GetData 获取数据
// key: 键 返回值: 数据 剩余时间 是否存在
func (c *Cache[K, V]) GetData(key K) (V, bool) {
val, _, ok := c.Get(key)
return val, ok
}
// GetTime 获取数据到期时间
// key: 键 返回值: 到期时间 是否存在
func (c *Cache[K, V]) GetTime(key K) (time.Time, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
if data, ok := c.data[key]; ok {
if c.cGetData != nil {
c.cGetData(key, *data)
}
return data.End, true
}
return time.Time{}, false
}
// GetTTL 获取数据剩余时间
// key: 键 返回值: 剩余时间 是否存在
// 时间为-1表示永不过期
func (c *Cache[K, V]) GetTTL(key K) (int64, bool) {
_, ttl, ok := c.Get(key)
return ttl, ok
}
/*
下方仅为获取数据并且输出指定类型的方法
推荐在定义类型为any的情况下使用
*/
// TypeErrMsg 不支持的的类型转换
var TypeErrMsg = errors.New("type error")
// NegativeNumberErrMsg 负数错误
var NegativeNumberErrMsg = errors.New("negative number")
// NumberBigErrMsg 数字过大
var NumberBigErrMsg = errors.New("number too big")
// GetString 获取数据并转为string
// key: 键 返回值: 数据 是否存在 error
// 支持类型: string,int*,uint*,float*,bool
func (c *Cache[K, V]) GetString(key K) (string, bool, error) {
val, ok := c.GetData(key)
if !ok {
return "", false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.String:
return v.String(), true, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10), true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(v.Uint(), 10), true, nil
case reflect.Float32, reflect.Float64:
return strconv.FormatFloat(v.Float(), 'f', -1, 64), true, nil
case reflect.Bool:
return strconv.FormatBool(v.Bool()), true, nil
default:
return "", true, TypeErrMsg
}
}
// GetInt 获取数据并转为int
// key: 键 返回值: 数据 是否存在 error
// [int64,uint64,float64在32位系统上会出现type error]
// 支持类型: int*,uint*,float*(丢失精度)
func (c *Cache[K, V]) GetInt(key K) (int, bool, error) {
val, ok := c.GetData(key)
if !ok {
return 0, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
return int(v.Int()), true, nil
case reflect.Int64:
if c.is64Bit {
return int(v.Int()), true, nil
} else {
return 0, true, TypeErrMsg
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
return int(v.Uint()), true, nil
case reflect.Uint64:
if c.is64Bit {
return int(v.Uint()), true, nil
} else {
return 0, true, TypeErrMsg
}
case reflect.Float32:
return int(v.Float()), true, nil
case reflect.Float64:
if c.is64Bit {
return int(v.Float()), true, nil
} else {
return 0, true, TypeErrMsg
}
default:
return 0, true, TypeErrMsg
}
}
// GetInt64 获取数据并转为int64
// key: 键 返回值: 数据 是否存在 error
// 支持类型: int*,uint*,float*(丢失精度)
func (c *Cache[K, V]) GetInt64(key K) (int64, bool, error) {
val, ok := c.GetData(key)
if !ok {
return 0, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int(), true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(v.Uint()), true, nil
case reflect.Float32, reflect.Float64:
return int64(v.Float()), true, nil
default:
return 0, true, TypeErrMsg
}
}
// GetUint 获取数据并转为uint
// key: 键 返回值: 数据 是否存在 error
// [负数会导致negative number] [int64,uint64,float64在32位系统上会出现type error]
// 支持类型: int*,uint*,float*
func (c *Cache[K, V]) GetUint(key K) (uint, bool, error) {
val, ok := c.GetData(key)
if !ok {
return 0, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
if v.Int() < 0 {
return 0, true, NegativeNumberErrMsg
}
return uint(v.Int()), true, nil
case reflect.Int64:
if v.Int() < 0 {
return 0, true, NegativeNumberErrMsg
}
if c.is64Bit {
return uint(v.Int()), true, nil
} else {
return 0, true, TypeErrMsg
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
return uint(v.Uint()), true, nil
case reflect.Uint64:
if c.is64Bit {
return uint(v.Uint()), true, nil
} else {
return 0, true, TypeErrMsg
}
case reflect.Float32:
if v.Float() < 0 {
return 0, true, NegativeNumberErrMsg
}
return uint(v.Float()), true, nil
case reflect.Float64:
if v.Float() < 0 {
return 0, true, NegativeNumberErrMsg
}
if c.is64Bit {
return uint(v.Float()), true, nil
} else {
return 0, true, TypeErrMsg
}
default:
return 0, true, TypeErrMsg
}
}
// GetUint64 获取数据并转为uint64
// key: 键 返回值: 数据 是否存在 error
// [负数会导致negative number]
// 支持类型: int*,uint*,float*
func (c *Cache[K, V]) GetUint64(key K) (uint64, bool, error) {
val, ok := c.GetData(key)
if !ok {
return 0, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v.Int() < 0 {
return 0, true, NegativeNumberErrMsg
}
return uint64(v.Int()), true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint(), true, nil
case reflect.Float32, reflect.Float64:
if v.Float() < 0 {
return 0, true, NegativeNumberErrMsg
}
return uint64(v.Float()), true, nil
default:
return 0, true, TypeErrMsg
}
}
// GetFloat32 获取数据并转为float32
// key: 键 返回值: 数据 是否存在 error
// [储蓄超过32位精度数字会出现number too big]
// 支持类型: int*,uint*,float*
func (c *Cache[K, V]) GetFloat32(key K) (float32, bool, error) {
val, ok := c.GetData(key)
if !ok {
return 0, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if v.Int() > 1<<24 {
return 0, true, NumberBigErrMsg
}
return float32(v.Int()), true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if v.Uint() > 1<<24 {
return 0, true, NumberBigErrMsg
}
return float32(v.Uint()), true, nil
case reflect.Float32:
return float32(v.Float()), true, nil
case reflect.Float64:
if v.Float() > 1<<24 {
return 0, true, NumberBigErrMsg
}
return float32(v.Float()), true, nil
default:
return 0, true, TypeErrMsg
}
}
// GetFloat64 获取数据并转为float64
// key: 键 返回值: 数据 是否存在 error
// 支持类型: int*,uint*,float*
func (c *Cache[K, V]) GetFloat64(key K) (float64, bool, error) {
val, ok := c.GetData(key)
if !ok {
return 0, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int()), true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(v.Uint()), true, nil
case reflect.Float32, reflect.Float64:
return v.Float(), true, nil
default:
return 0, true, TypeErrMsg
}
}
// GetBool 获取数据并转为bool
// key: 键 返回值: 数据 是否存在 error
// 支持类型: bool,[int*,uint*,float*]非0为true
func (c *Cache[K, V]) GetBool(key K) (bool, bool, error) {
val, ok := c.GetData(key)
if !ok {
return false, false, nil
}
switch v := reflect.ValueOf(val); v.Kind() {
case reflect.Bool:
return v.Bool(), true, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() != 0, true, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint() != 0, true, nil
case reflect.Float32, reflect.Float64:
return v.Float() != 0, true, nil
default:
return false, true, TypeErrMsg
}
}

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module git.slzz.org/gomod/cache
go 1.18

6
other.go Normal file
View File

@@ -0,0 +1,6 @@
package cache
func (c *Cache[K, V]) zero() V {
var zero V
return zero
}

134
persist.go Normal file
View File

@@ -0,0 +1,134 @@
package cache
import (
"bytes"
"encoding/gob"
"errors"
"os"
"reflect"
"sync"
)
// SetPersist 设置持久化储蓄
// path: 文件路径
// 警告: 请在创建缓存同时设置,否则会出现原有数据丢失
// *如果需要储蓄自定义结构体,请在init包使用gob.Register()注册
func (c *Cache[K, V]) SetPersist(path string) error {
if c.persist != nil {
return errors.New("persist already set up")
}
c.persist = c.setPersist(path)
return nil
}
func (c *Cache[K, V]) setPersist(path string) func() {
gob.Register(map[K]*Data[V]{})
stopChan := make(chan struct{})
var wg sync.WaitGroup
data, err := c.loadCache(path)
if err != nil {
c.fatal(err)
}
c.mu.Lock()
if data != nil {
c.data = data
}
c.mu.Unlock()
go func() {
wg.Add(1)
var newData map[K]*Data[V] // 提取缓存降低锁的时间
var oldData map[K]*Data[V]
// 关闭时保存一次数据
defer func() {
c.mu.RLock()
err := c.saveCache(path, c.data)
if err != nil {
c.fatal(err)
}
c.mu.RUnlock()
}()
for {
select {
case <-stopChan:
wg.Done()
return
default:
c.mu.RLock()
newData = c.data
c.mu.RUnlock()
if compareMaps(newData, oldData) {
continue
}
err := c.saveCache(path, newData)
if err != nil {
c.fatal(err)
continue
} else {
oldData = newData
}
}
}
}()
return func() {
close(stopChan)
wg.Wait()
}
}
// loadCache 加载持久化数据(会覆盖原有数据)
func (c *Cache[K, V]) loadCache(path string) (map[K]*Data[V], error) {
// 判断文件是否存在
if _, err := os.Stat(path); errors.Is(err, os.ErrNotExist) {
return nil, nil
}
// 读取文件
file, err := os.ReadFile(path)
if err != nil {
return nil, err
}
// 解析文件
c.mu.Lock()
defer c.mu.Unlock()
var data map[K]*Data[V]
err = gob.NewDecoder(bytes.NewReader(file)).Decode(&data)
if err != nil {
return nil, err
}
return data, nil
}
// saveCache 保存持久化数据(会覆盖原有文件)
func (c *Cache[K, V]) saveCache(path string, data map[K]*Data[V]) error {
// 编码数据
var buf bytes.Buffer
err := gob.NewEncoder(&buf).Encode(data)
if err != nil {
return err
}
// 写入/覆盖文件
err = os.WriteFile(path, buf.Bytes(), 0666)
if err != nil {
return err
}
return nil
}
// compareMaps 比较两个 map 是否相等
func compareMaps[K comparable, V any](map1, map2 map[K]V) bool {
if len(map1) != len(map2) {
return false
}
for k, v1 := range map1 {
v2, ok := map2[k]
if !ok || !reflect.DeepEqual(v1, v2) {
return false
}
}
return true
}

48
set.go Normal file
View File

@@ -0,0 +1,48 @@
package cache
import (
"errors"
"time"
)
func (c *Cache[K, V]) set(key K, val V, t time.Time) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; ok {
return false, errors.New("key already exists")
}
c.data[key] = &Data[V]{End: t, Val: val}
if c.cSet != nil {
c.cSet(key, *c.data[key])
}
return true, nil
}
// setForced 强制设置数据 数据存在则覆盖
func (c *Cache[K, V]) setForced(key K, val V, t time.Time) error {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = &Data[V]{End: t, Val: val}
if c.cSet != nil {
c.cSet(key, *c.data[key])
}
return nil
}
// Set 设置数据 数据存在则报错
// key: 键 Val: 数据 ttl: 过期时间(秒) 0表示永不过期 返回值: 是否设置成功 错误
func (c *Cache[K, V]) Set(key K, val V, ttl int64) (bool, error) {
if ttl == 0 {
return c.set(key, val, time.Time{})
}
return c.set(key, val, time.Now().Add(time.Second*time.Duration(ttl)))
}
// SetData 设置数据 数据存在则报错 永不过期
// key: 键 Val: 数据 返回值: 是否设置成功 错误 返回值: 是否设置成功 错误
func (c *Cache[K, V]) SetData(key K, val V) (bool, error) {
return c.set(key, val, time.Time{})
}

75
up.go Normal file
View File

@@ -0,0 +1,75 @@
package cache
import (
"errors"
"time"
)
func (c *Cache[K, V]) update(key K, val V, t time.Time) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
}
c.data[key] = &Data[V]{End: t, Val: val}
if c.cUpData != nil {
c.cUpData(key, *c.data[key])
}
if c.cUpTTL != nil {
c.cUpTTL(key, *c.data[key])
}
return true, nil
}
func (c *Cache[K, V]) updateData(key K, val V) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
}
c.data[key].Val = val
if c.cUpData != nil {
c.cUpData(key, *c.data[key])
}
return true, nil
}
func (c *Cache[K, V]) updateTTL(key K, ttl time.Time) (bool, error) {
c.mu.Lock()
defer c.mu.Unlock()
if _, ok := c.data[key]; !ok {
return false, errors.New("key not exists")
}
c.data[key].End = ttl
if c.cUpTTL != nil {
c.cUpTTL(key, *c.data[key])
}
return true, nil
}
// UpdateData 更新数据
// key: 键 Val: 数据 返回值: 是否更新成功 错误
func (c *Cache[K, V]) UpdateData(key K, val V) (bool, error) {
return c.updateData(key, val)
}
// UpdateTTL 更新数据过期时间
// key: 键 ttl: 过期时间(秒) 0表示永不过期 返回值: 是否更新成功 错误
func (c *Cache[K, V]) UpdateTTL(key K, ttl int64) (bool, error) {
if ttl == 0 {
return c.updateTTL(key, time.Time{})
}
return c.updateTTL(key, time.Now().Add(time.Second*time.Duration(ttl)))
}
// UpdateTime 更新数据到期时间(time.Time)
// key: 键 t: 到期时间 返回值: 是否更新成功 错误
func (c *Cache[K, V]) UpdateTime(key K, t time.Time) (bool, error) {
return c.updateTTL(key, t)
}