mirror of
https://github.com/charlienet/go-mixed.git
synced 2025-07-18 00:22:41 +08:00
update
This commit is contained in:
31
cache/big_cache.go → cache/bigcache/big_cache.go
vendored
31
cache/big_cache.go → cache/bigcache/big_cache.go
vendored
@ -1,4 +1,4 @@
|
|||||||
package cache
|
package bigcache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -8,8 +8,6 @@ import (
|
|||||||
"github.com/charlienet/go-mixed/logx"
|
"github.com/charlienet/go-mixed/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ MemCache = &bigCacheClient{}
|
|
||||||
|
|
||||||
type BigCacheConfig struct {
|
type BigCacheConfig struct {
|
||||||
Shards int
|
Shards int
|
||||||
LifeWindow time.Duration
|
LifeWindow time.Duration
|
||||||
@ -49,8 +47,13 @@ func NewBigCache(c BigCacheConfig) (*bigCacheClient, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *bigCacheClient) Get(key string) ([]byte, error) {
|
func (c *bigCacheClient) Get(key string) ([]byte, bool) {
|
||||||
return c.cache.Get(key)
|
b, err := c.cache.Get(key)
|
||||||
|
if err == nil {
|
||||||
|
return b, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *bigCacheClient) Set(key string, entry []byte, expire time.Duration) error {
|
func (c *bigCacheClient) Set(key string, entry []byte, expire time.Duration) error {
|
||||||
@ -68,9 +71,23 @@ func (c *bigCacheClient) Delete(keys ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *bigCacheClient) Exist(key string) {
|
func (c *bigCacheClient) Exist(key string) bool {
|
||||||
|
_, err := c.cache.Get(key)
|
||||||
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return !errors.Is(err, bigcache.ErrEntryNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *bigCacheClient) Clear() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *bigCacheClient) IsNotFound(err error) bool {
|
func (c *bigCacheClient) IsNotFound(err error) bool {
|
||||||
return errors.Is(err, bigcache.ErrEntryNotFound)
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return !errors.Is(err, bigcache.ErrEntryNotFound)
|
||||||
}
|
}
|
27
cache/bigcache/big_cache_test.go
vendored
Normal file
27
cache/bigcache/big_cache_test.go
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package bigcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBigCache(t *testing.T) {
|
||||||
|
r := require.New(t)
|
||||||
|
|
||||||
|
c, err := NewBigCache(BigCacheConfig{})
|
||||||
|
r.Nil(err)
|
||||||
|
|
||||||
|
cacheKey := "a"
|
||||||
|
cacheValue := "bbb"
|
||||||
|
|
||||||
|
c.Set(cacheKey, []byte(cacheValue), time.Second*5)
|
||||||
|
|
||||||
|
r.True(c.Exist(cacheKey))
|
||||||
|
r.False(c.Exist("abb"))
|
||||||
|
|
||||||
|
b, ok := c.Get(cacheKey)
|
||||||
|
r.True(ok)
|
||||||
|
r.Equal(cacheValue, string(b))
|
||||||
|
}
|
142
cache/cache.go
vendored
142
cache/cache.go
vendored
@ -6,62 +6,73 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charlienet/go-mixed/bytesconv"
|
|
||||||
"github.com/charlienet/go-mixed/json"
|
|
||||||
"github.com/charlienet/go-mixed/locker"
|
"github.com/charlienet/go-mixed/locker"
|
||||||
"github.com/charlienet/go-mixed/logx"
|
"github.com/charlienet/go-mixed/logx"
|
||||||
|
"golang.org/x/sync/singleflight"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotFound = errors.New("key not found")
|
var ErrNotFound = errors.New("key not found")
|
||||||
|
|
||||||
|
// 数据加载函数定义
|
||||||
type LoadFunc func(context.Context) (any, error)
|
type LoadFunc func(context.Context) (any, error)
|
||||||
|
|
||||||
|
type ICache interface {
|
||||||
|
}
|
||||||
|
|
||||||
type Cache struct {
|
type Cache struct {
|
||||||
prefix string // 键前缀
|
prefix string // 键前缀
|
||||||
retry int // 资源获取时的重试次数
|
retry int // 资源获取时的重试次数
|
||||||
mem MemCache // 内存缓存
|
mem MemCache // 内存缓存
|
||||||
distributdCache DistributdCache // 分布式缓存
|
rds DistributedCache // 远程缓存
|
||||||
publishSubscribe PublishSubscribe // 发布订阅
|
publishSubscribe PublishSubscribe // 发布订阅
|
||||||
|
group singleflight.Group // singleflight.Group
|
||||||
lock locker.ChanLocker // 资源锁
|
lock locker.ChanLocker // 资源锁
|
||||||
stats *Stats // 缓存命中计数
|
stats *Stats // 缓存命中计数
|
||||||
qps *qps // 访问计数
|
qps *qps // 访问计数
|
||||||
logger logx.Logger // 日志记录
|
logger logx.Logger // 日志记录
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCache(opts ...option) *Cache {
|
func New(opts ...option) (*Cache, error) {
|
||||||
|
|
||||||
c := acquireDefaultCache()
|
c := acquireDefaultCache()
|
||||||
for _, f := range opts {
|
for _, f := range opts {
|
||||||
if err := f(c); err != nil {
|
if err := f(c); err != nil {
|
||||||
return c
|
return c, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go c.subscribe()
|
// 未设置内存缓存时,添加默认缓存
|
||||||
|
if c.mem == nil {
|
||||||
|
c.mem = NewTinyLFU(1<<12, time.Second*30)
|
||||||
|
}
|
||||||
|
|
||||||
return c
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Set(key string, value any, expiration time.Duration) error {
|
func (c *Cache) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
|
||||||
if c.mem != nil {
|
buf, err := Marshal(value)
|
||||||
bytes, err := bytesconv.Encode(value)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.mem.Set(key, bytes, expiration)
|
if c.mem != nil {
|
||||||
|
c.mem.Set(key, buf, expiration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.rds != nil {
|
||||||
|
c.rds.Set(ctx, key, buf, expiration)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Get(key string, out any) error {
|
func (c *Cache) Get(ctx context.Context, key string, out any) error {
|
||||||
if c.mem != nil {
|
if c.mem != nil {
|
||||||
c.getFromMem(key, out)
|
c.getFromMem(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.distributdCache != nil {
|
if c.rds != nil {
|
||||||
if err := c.distributdCache.Get(key, out); err != nil {
|
if err := c.rds.Get(ctx, key, out); err != nil {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +81,7 @@ func (c *Cache) Get(key string, out any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) GetFn(ctx context.Context, key string, out any, fn LoadFunc, expiration time.Duration) (bool, error) {
|
func (c *Cache) GetFn(ctx context.Context, key string, out any, fn LoadFunc, expiration time.Duration) (bool, error) {
|
||||||
c.Get(key, out)
|
c.Get(ctx, key, out)
|
||||||
|
|
||||||
// 多级缓存中未找到时,放置缓存对象
|
// 多级缓存中未找到时,放置缓存对象
|
||||||
ret, err := fn(ctx)
|
ret, err := fn(ctx)
|
||||||
@ -78,7 +89,7 @@ func (c *Cache) GetFn(ctx context.Context, key string, out any, fn LoadFunc, exp
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Set(key, ret, expiration)
|
c.Set(ctx, key, ret, expiration)
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -87,32 +98,69 @@ func (c *Cache) Exist(key string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Delete(key ...string) error {
|
func (c *Cache) Delete(ctx context.Context, key ...string) error {
|
||||||
if c.mem != nil {
|
if c.mem != nil {
|
||||||
c.mem.Delete(key...)
|
c.mem.Delete(key...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.distributdCache != nil {
|
if c.rds != nil {
|
||||||
c.distributdCache.Delete(key...)
|
c.rds.Delete(ctx, key...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range key {
|
||||||
|
c.group.Forget(k)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) subscribe() {
|
// 清除本地缓存
|
||||||
|
func (c *Cache) ClearMem() {
|
||||||
|
if c.mem != nil {
|
||||||
|
c.mem.Clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) getFromMem(key string, out any) error {
|
func (c *Cache) Clear() {
|
||||||
bytes, err := c.mem.Get(key)
|
|
||||||
if err != nil {
|
}
|
||||||
return err
|
|
||||||
|
func (c *Cache) Disable() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Enable() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) getOnce(ctx context.Context, key string) (b []byte, cached bool, err error) {
|
||||||
|
if c.mem != nil {
|
||||||
|
b, ok := c.mem.Get(key)
|
||||||
|
if ok {
|
||||||
|
return b, true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.group.Do(key, func() (any, error) {
|
||||||
|
if c.mem != nil {
|
||||||
|
b, ok := c.mem.Get(key)
|
||||||
|
if ok {
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bytesconv.Decode(bytes, out); err != nil {
|
if c.rds != nil {
|
||||||
return err
|
c.rds.Get(ctx, key, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) getFromMem(key string) ([]byte, bool) {
|
||||||
|
bytes, cached := c.mem.Get(key)
|
||||||
|
return bytes, cached
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从缓存加载数据
|
// 从缓存加载数据
|
||||||
@ -125,8 +173,6 @@ func (c *Cache) getFromCache() {
|
|||||||
// 从数据源加载数据
|
// 从数据源加载数据
|
||||||
func (c *Cache) getFromSource(ctx context.Context, key string, fn LoadFunc) error {
|
func (c *Cache) getFromSource(ctx context.Context, key string, fn LoadFunc) error {
|
||||||
|
|
||||||
// 1. 尝试获取资源锁,如成功获取到锁加载数据
|
|
||||||
// 2. 未获取到锁,等待从缓存中获取
|
|
||||||
ch, ok := c.lock.Get(key)
|
ch, ok := c.lock.Get(key)
|
||||||
if ok {
|
if ok {
|
||||||
defer c.lock.Release(key)
|
defer c.lock.Release(key)
|
||||||
@ -150,39 +196,3 @@ func (c *Cache) getFromSource(ctx context.Context, key string, fn LoadFunc) erro
|
|||||||
return c.getFromSource(ctx, key, fn)
|
return c.getFromSource(ctx, key, fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) marshal(value any) ([]byte, error) {
|
|
||||||
switch value := value.(type) {
|
|
||||||
case nil:
|
|
||||||
return nil, nil
|
|
||||||
case []byte:
|
|
||||||
return value, nil
|
|
||||||
case string:
|
|
||||||
return []byte(value), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := json.Marshal(value)
|
|
||||||
return b, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) unmarshal(b []byte, value any) error {
|
|
||||||
if len(b) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch value := value.(type) {
|
|
||||||
case nil:
|
|
||||||
return nil
|
|
||||||
case *[]byte:
|
|
||||||
clone := make([]byte, len(b))
|
|
||||||
copy(clone, b)
|
|
||||||
*value = clone
|
|
||||||
return nil
|
|
||||||
case *string:
|
|
||||||
*value = string(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
err := json.Unmarshal(b, value)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
128
cache/cache_builder.go
vendored
128
cache/cache_builder.go
vendored
@ -1,99 +1,81 @@
|
|||||||
package cache
|
package cache
|
||||||
|
|
||||||
import "github.com/charlienet/go-mixed/logx"
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/cache/bigcache"
|
||||||
|
"github.com/charlienet/go-mixed/cache/freecache"
|
||||||
|
"github.com/charlienet/go-mixed/logx"
|
||||||
|
)
|
||||||
|
|
||||||
const defaultPrefix = "cache"
|
const defaultPrefix = "cache"
|
||||||
|
|
||||||
|
|
||||||
type option func(*Cache) error
|
type option func(*Cache) error
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func acquireDefaultCache() *Cache {
|
func WithRedis(opts RedisConfig) option {
|
||||||
return &Cache{
|
return func(c *Cache) error {
|
||||||
prefix: defaultPrefix,
|
if len(opts.Prefix) == 0 {
|
||||||
qps: NewQps(),
|
opts.Prefix = defaultPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
rds := NewRedis(opts)
|
||||||
|
|
||||||
|
c.rds = rds
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||||
|
defer cancel()
|
||||||
|
return rds.Ping(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cacheBuilder struct {
|
func WithBigCache(opts bigcache.BigCacheConfig) option {
|
||||||
prefix string
|
return func(c *Cache) error {
|
||||||
redisOptions RedisConfig
|
mem, err := bigcache.NewBigCache(opts)
|
||||||
bigCacheConfig BigCacheConfig
|
|
||||||
freeSize int
|
c.mem = mem
|
||||||
publishSubscribe PublishSubscribe
|
return err
|
||||||
log logx.Logger
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCacheBuilder() *cacheBuilder {
|
func WithFreeCache(size int) option {
|
||||||
return &cacheBuilder{}
|
return func(c *Cache) error {
|
||||||
}
|
mem := freecache.NewFreeCache(size)
|
||||||
|
c.mem = mem
|
||||||
|
|
||||||
func (b *cacheBuilder) WithLogger(log logx.Logger) *cacheBuilder {
|
return nil
|
||||||
b.log = log
|
}
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *cacheBuilder) WithPrefix(prefix string) *cacheBuilder {
|
|
||||||
b.prefix = prefix
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *cacheBuilder) WithRedis(opts RedisConfig) *cacheBuilder {
|
|
||||||
b.redisOptions = opts
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *cacheBuilder) WithBigCache(opts BigCacheConfig) *cacheBuilder {
|
|
||||||
b.bigCacheConfig = opts
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *cacheBuilder) WithFreeCache(size int) *cacheBuilder {
|
|
||||||
b.freeSize = size
|
|
||||||
return b
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用自定义分布式缓存
|
// 使用自定义分布式缓存
|
||||||
func WithDistributedCache(c DistributdCache) {
|
func WithDistributedCache(rds DistributedCache) option {
|
||||||
|
return func(c *Cache) error {
|
||||||
|
c.rds = rds
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *cacheBuilder) WithPublishSubscribe(p PublishSubscribe) *cacheBuilder {
|
func WithPublishSubscribe(p PublishSubscribe) option {
|
||||||
b.publishSubscribe = p
|
return func(c *Cache) error {
|
||||||
return b
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b cacheBuilder) Build() (*Cache, error) {
|
func WithLogger(log logx.Logger) option {
|
||||||
var err error
|
return func(c *Cache) error {
|
||||||
cache := acquireDefaultCache()
|
c.logger = log
|
||||||
if len(b.prefix) > 0 {
|
|
||||||
cache.prefix = b.prefix
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func acquireDefaultCache() *Cache {
|
||||||
|
return &Cache{
|
||||||
|
qps: NewQps(),
|
||||||
}
|
}
|
||||||
|
|
||||||
b.redisOptions.Prefix = cache.prefix
|
|
||||||
|
|
||||||
redis := NewRedis(b.redisOptions)
|
|
||||||
if err := redis.Ping(); err != nil {
|
|
||||||
return cache, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var mem MemCache
|
|
||||||
if b.freeSize > 0 {
|
|
||||||
mem = NewFreeCache(b.freeSize)
|
|
||||||
} else {
|
|
||||||
if b.log != nil {
|
|
||||||
b.bigCacheConfig.log = b.log
|
|
||||||
}
|
|
||||||
|
|
||||||
mem, err = NewBigCache(b.bigCacheConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
cache.distributdCache = redis
|
|
||||||
cache.mem = mem
|
|
||||||
cache.publishSubscribe = b.publishSubscribe
|
|
||||||
cache.logger = b.log
|
|
||||||
|
|
||||||
return cache, err
|
|
||||||
}
|
}
|
||||||
|
47
cache/cache_builder_test.go
vendored
47
cache/cache_builder_test.go
vendored
@ -3,27 +3,38 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charlienet/go-mixed/logx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuilder(t *testing.T) {
|
func TestBuilder(t *testing.T) {
|
||||||
cache, err := NewCacheBuilder().
|
now := time.Now()
|
||||||
WithLogger(logx.NewLogrus(logx.WithFormatter(logx.NewNestedFormatter(logx.NestedFormatterOption{
|
t.Log(now)
|
||||||
Color: true,
|
t1, _ := time.ParseDuration("9h27m")
|
||||||
})))).
|
t1 += time.Hour * 24
|
||||||
WithRedis(RedisConfig{
|
t2, _ := time.ParseDuration("16h28m")
|
||||||
Addrs: []string{"192.168.2.222:6379"},
|
t.Log(t1)
|
||||||
Password: "123456",
|
t.Log(t2)
|
||||||
}).
|
|
||||||
WithBigCache(BigCacheConfig{}).
|
|
||||||
// WithFreeCache(10 * 1024 * 1024).
|
|
||||||
Build()
|
|
||||||
|
|
||||||
if err != nil {
|
f := time.Date(2022, time.December, 12, 8, 0, 0, 0, time.Local)
|
||||||
t.Fatal(err)
|
t.Log(f.Sub(time.Now()))
|
||||||
}
|
|
||||||
|
|
||||||
u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
// cache, err := New(
|
||||||
t.Log(cache.Set(defaultKey, u, time.Minute*10))
|
// WithLogger(logx.NewLogrus(logx.WithNestedFormatter(logx.NestedFormatterOption{
|
||||||
|
// Color: true,
|
||||||
|
// }))).
|
||||||
|
// UseRedis(RedisConfig{
|
||||||
|
// Addrs: []string{"192.168.2.222:6379"},
|
||||||
|
// Password: "123456",
|
||||||
|
// }).
|
||||||
|
// UseBigCache(bigcache.BigCacheConfig{}).
|
||||||
|
// Build()
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||||
|
// defer cancel()
|
||||||
|
|
||||||
|
// u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
||||||
|
// t.Log(cache.Set(ctx, defaultKey, u, time.Minute*10))
|
||||||
}
|
}
|
||||||
|
12
cache/cache_preload.go
vendored
Normal file
12
cache/cache_preload.go
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// PreLoadItem 预加载数据项
|
||||||
|
type PreLoadItem struct {
|
||||||
|
Key string
|
||||||
|
Value any
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreloadFunc 数据预加载函数定义
|
||||||
|
type PreloadFunc func(context.Context) ([]PreLoadItem, error)
|
85
cache/cache_test.go
vendored
85
cache/cache_test.go
vendored
@ -6,9 +6,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charlienet/go-mixed/bytesconv"
|
|
||||||
"github.com/charlienet/go-mixed/logx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -16,25 +13,26 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewCache(t *testing.T) {
|
func TestNewCache(t *testing.T) {
|
||||||
c, err := NewCacheBuilder().
|
// c, err := NewCacheBuilder().
|
||||||
WithRedis(RedisConfig{
|
// UseRedis(RedisConfig{
|
||||||
Addrs: []string{"192.168.2.222:6379"},
|
// Addrs: []string{"192.168.2.222:6379"},
|
||||||
Password: "123456",
|
// Password: "123456",
|
||||||
}).
|
// }).
|
||||||
WithPrefix("cache_test").
|
// WithPrefix("cache_test").
|
||||||
WithLogger(logx.NewLogrus()).
|
// WithLogger(logx.NewLogrus()).
|
||||||
Build()
|
// Build()
|
||||||
|
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
c.Set("abc", "value", time.Minute*10)
|
// ctx := context.Background()
|
||||||
|
// c.Set(ctx, "abc", "value", time.Minute*10)
|
||||||
|
|
||||||
var s string
|
// var s string
|
||||||
c.Get("abc", &s)
|
// c.Get(ctx, "abc", &s)
|
||||||
|
|
||||||
t.Log(s)
|
// t.Log(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleUser struct {
|
type SimpleUser struct {
|
||||||
@ -43,45 +41,43 @@ type SimpleUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMemCache(t *testing.T) {
|
func TestMemCache(t *testing.T) {
|
||||||
b, _ := NewBigCache(BigCacheConfig{})
|
// b, _ := bigcache.NewBigCache(bigcache.BigCacheConfig{})
|
||||||
var mems = []MemCache{
|
// var mems = []MemCache{
|
||||||
NewFreeCache(10 * 1024 * 1024),
|
// NewFreeCache(10 * 1024 * 1024),
|
||||||
b,
|
// b,
|
||||||
}
|
// }
|
||||||
|
|
||||||
u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
// u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
||||||
encoded, _ := bytesconv.Encode(u)
|
// encoded, _ := bytesconv.Encode(u)
|
||||||
for _, m := range mems {
|
// for _, m := range mems {
|
||||||
m.Set(defaultKey, encoded, time.Second)
|
// m.Set(defaultKey, encoded, time.Second)
|
||||||
ret, err := m.Get(defaultKey)
|
// ret, _ := m.Get(defaultKey)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var u2 SimpleUser
|
// var u2 SimpleUser
|
||||||
bytesconv.Decode(ret, &u2)
|
// bytesconv.Decode(ret, &u2)
|
||||||
t.Log(u2)
|
// t.Log(u2)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDistributedCache(t *testing.T) {
|
func TestDistributedCache(t *testing.T) {
|
||||||
c := NewRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456", Prefix: "abcdef"})
|
c := NewRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456", Prefix: "abcdef"})
|
||||||
|
|
||||||
if err := c.Ping(); err != nil {
|
ctx := context.Background()
|
||||||
|
if err := c.Ping(ctx); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Log(c.Exist(defaultKey))
|
t.Log(c.Exist(ctx, defaultKey))
|
||||||
|
|
||||||
u := SimpleUser{FirstName: "redis client"}
|
u := SimpleUser{FirstName: "redis client"}
|
||||||
|
|
||||||
var u2 SimpleUser
|
var u2 SimpleUser
|
||||||
c.Get(defaultKey, &u2)
|
c.Get(ctx, defaultKey, &u2)
|
||||||
|
|
||||||
c.Set(defaultKey, u, time.Minute*10)
|
c.Set(ctx, defaultKey, u, time.Minute*10)
|
||||||
t.Log(c.Exist(defaultKey))
|
t.Log(c.Exist(ctx, defaultKey))
|
||||||
|
|
||||||
if err := c.Get(defaultKey, &u2); err != nil {
|
if err := c.Get(ctx, defaultKey, &u2); err != nil {
|
||||||
t.Fatal("err:", err)
|
t.Fatal("err:", err)
|
||||||
}
|
}
|
||||||
t.Logf("%+v", u2)
|
t.Logf("%+v", u2)
|
||||||
@ -136,10 +132,9 @@ func load() (any, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildCache() *Cache {
|
func buildCache() *Cache {
|
||||||
c, err := NewCacheBuilder().
|
c, err := New(
|
||||||
WithFreeCache(10 * 1024 * 1024).
|
WithFreeCache(10*1024*1024),
|
||||||
WithRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456"}).
|
WithRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456"}))
|
||||||
Build()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
10
cache/distributd_cache.go
vendored
10
cache/distributd_cache.go
vendored
@ -1,10 +0,0 @@
|
|||||||
package cache
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type DistributdCache interface {
|
|
||||||
Get(key string, out any) error
|
|
||||||
Set(key string, value any, expiration time.Duration) error
|
|
||||||
Delete(key ...string) error
|
|
||||||
Ping() error
|
|
||||||
}
|
|
14
cache/distributed_cache.go
vendored
Normal file
14
cache/distributed_cache.go
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 分布式缓存接口
|
||||||
|
type DistributedCache interface {
|
||||||
|
Get(ctx context.Context, key string, out any) error
|
||||||
|
Set(ctx context.Context, key string, value any, expiration time.Duration) error
|
||||||
|
Delete(ctx context.Context, key ...string) error
|
||||||
|
Ping(ctx context.Context) error
|
||||||
|
}
|
10
cache/empty_cache_adaper.go
vendored
Normal file
10
cache/empty_cache_adaper.go
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// var emptyCache DistributedCache = &emptyCacheAdapter{}
|
||||||
|
|
||||||
|
type emptyCacheAdapter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyCacheAdapter) Delete(ctx context.Context, keys ...string) {}
|
@ -1,4 +1,4 @@
|
|||||||
package cache
|
package freecache
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -10,8 +10,6 @@ import (
|
|||||||
|
|
||||||
const defaultSize = 10 * 1024 * 1024 // 10M
|
const defaultSize = 10 * 1024 * 1024 // 10M
|
||||||
|
|
||||||
var _ MemCache = &freeCache{}
|
|
||||||
|
|
||||||
type freeCache struct {
|
type freeCache struct {
|
||||||
cache *freecache.Cache
|
cache *freecache.Cache
|
||||||
}
|
}
|
||||||
@ -29,8 +27,12 @@ func NewFreeCache(size int) *freeCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *freeCache) Get(key string) ([]byte, error) {
|
func (c *freeCache) Get(key string) ([]byte, bool) {
|
||||||
return c.cache.Get([]byte(key))
|
b, err := c.cache.Get([]byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return b, false
|
||||||
|
}
|
||||||
|
return b, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *freeCache) Set(key string, value []byte, d time.Duration) error {
|
func (c *freeCache) Set(key string, value []byte, d time.Duration) error {
|
||||||
@ -54,6 +56,10 @@ func (c *freeCache) Exist(key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *freeCache) Clear() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (c *freeCache) IsNotFound(err error) bool {
|
func (c *freeCache) IsNotFound(err error) bool {
|
||||||
return errors.Is(err, freecache.ErrNotFound)
|
return errors.Is(err, freecache.ErrNotFound)
|
||||||
}
|
}
|
1
cache/freecache/free_cache_test.go
vendored
Normal file
1
cache/freecache/free_cache_test.go
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
package freecache
|
7
cache/local_cache.go
vendored
7
cache/local_cache.go
vendored
@ -1,7 +0,0 @@
|
|||||||
package cache
|
|
||||||
|
|
||||||
type LocalCache interface {
|
|
||||||
Set(key string, data []byte)
|
|
||||||
Get(key string) ([]byte, bool)
|
|
||||||
Del(key string)
|
|
||||||
}
|
|
2
cache/lru.go
vendored
Normal file
2
cache/lru.go
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
package cache
|
||||||
|
|
5
cache/mem.go
vendored
5
cache/mem.go
vendored
@ -3,7 +3,8 @@ package cache
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type MemCache interface {
|
type MemCache interface {
|
||||||
Get(key string) ([]byte, error)
|
Get(key string) ([]byte, bool)
|
||||||
Set(key string, entry []byte, expire time.Duration) error
|
Set(key string, b []byte, expire time.Duration) error
|
||||||
Delete(key ...string) error
|
Delete(key ...string) error
|
||||||
|
Clear()
|
||||||
}
|
}
|
||||||
|
43
cache/msg_pack.go
vendored
Normal file
43
cache/msg_pack.go
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Marshal(v any) ([]byte, error) {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil, nil
|
||||||
|
case []byte:
|
||||||
|
return v, nil
|
||||||
|
case string:
|
||||||
|
return []byte(v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := msgpack.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unmarshal(b []byte, v any) error {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := v.(type) {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case *[]byte:
|
||||||
|
clone := make([]byte, len(b))
|
||||||
|
copy(clone, b)
|
||||||
|
*v = clone
|
||||||
|
case *string:
|
||||||
|
*v = string(b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgpack.Unmarshal(b, v)
|
||||||
|
}
|
6
cache/qps.go
vendored
6
cache/qps.go
vendored
@ -31,7 +31,9 @@ func (q *qps) statisticsTotal() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second)
|
ticker := time.NewTicker(time.Minute)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
q.all.viewTotal = atomic.SwapInt64(&q.all.total, 0)
|
q.all.viewTotal = atomic.SwapInt64(&q.all.total, 0)
|
||||||
q.memoryTotal.viewTotal = atomic.SwapInt64(&q.memoryTotal.total, 0)
|
q.memoryTotal.viewTotal = atomic.SwapInt64(&q.memoryTotal.total, 0)
|
||||||
@ -39,5 +41,7 @@ func (q *qps) statisticsTotal() {
|
|||||||
q.redisTotal.viewTotal = atomic.SwapInt64(&q.redisTotal.total, 0)
|
q.redisTotal.viewTotal = atomic.SwapInt64(&q.redisTotal.total, 0)
|
||||||
q.redisHit.viewTotal = atomic.SwapInt64(&q.redisHit.total, 0)
|
q.redisHit.viewTotal = atomic.SwapInt64(&q.redisHit.total, 0)
|
||||||
q.sourceTotal.viewTotal = atomic.SwapInt64(&q.sourceTotal.total, 0)
|
q.sourceTotal.viewTotal = atomic.SwapInt64(&q.sourceTotal.total, 0)
|
||||||
|
|
||||||
|
// percnt := 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
cache/readme.md
vendored
8
cache/readme.md
vendored
@ -14,6 +14,14 @@
|
|||||||
|
|
||||||
## 使用方式
|
## 使用方式
|
||||||
|
|
||||||
|
创建
|
||||||
|
|
||||||
|
```go
|
||||||
|
cache.New().UseRedis().UseBigCache().Build()
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
```go
|
```go
|
||||||
Cache.Get(key, dist, func() (bool,error){}, options func(){})
|
Cache.Get(key, dist, func() (bool,error){}, options func(){})
|
||||||
|
Cache.GetFn(context, key, dist, func() (bool, error))
|
||||||
```
|
```
|
||||||
|
14
cache/redis.go
vendored
14
cache/redis.go
vendored
@ -54,7 +54,7 @@ func NewRedis(c RedisConfig) *redisClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *redisClient) Get(key string, out any) error {
|
func (c *redisClient) Get(cxt context.Context, key string, out any) error {
|
||||||
val, err := c.client.Get(context.Background(), c.getKey(key)).Result()
|
val, err := c.client.Get(context.Background(), c.getKey(key)).Result()
|
||||||
if errors.Is(err, redis.Nil) {
|
if errors.Is(err, redis.Nil) {
|
||||||
return ErrNotFound
|
return ErrNotFound
|
||||||
@ -72,23 +72,23 @@ func (c *redisClient) Get(key string, out any) error {
|
|||||||
return json.Unmarshal(bytesconv.StringToBytes(val), out)
|
return json.Unmarshal(bytesconv.StringToBytes(val), out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *redisClient) Set(key string, value any, expiration time.Duration) error {
|
func (c *redisClient) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
|
||||||
j, _ := json.Marshal(value)
|
j, _ := json.Marshal(value)
|
||||||
return c.client.Set(context.Background(), c.getKey(key), j, expiration).Err()
|
return c.client.Set(context.Background(), c.getKey(key), j, expiration).Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *redisClient) Exist(key string) (bool, error) {
|
func (c *redisClient) Exist(ctx context.Context, key string) (bool, error) {
|
||||||
val, err := c.client.Exists(context.Background(), c.getKey(key)).Result()
|
val, err := c.client.Exists(context.Background(), c.getKey(key)).Result()
|
||||||
return val > 0, err
|
return val > 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *redisClient) Delete(key ...string) error {
|
func (c *redisClient) Delete(ctx context.Context, key ...string) error {
|
||||||
keys := make([]string, 0, len(key))
|
keys := make([]string, 0, len(key))
|
||||||
for _, k := range key {
|
for _, k := range key {
|
||||||
keys = append(keys, c.getKey(k))
|
keys = append(keys, c.getKey(k))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ , err := c.client.Del(context.Background(), keys...).Result()
|
_, err := c.client.Del(context.Background(), keys...).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -96,8 +96,8 @@ func (c *redisClient) Delete(key ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *redisClient) Ping() error {
|
func (c *redisClient) Ping(ctx context.Context) error {
|
||||||
_, err := c.client.Ping(context.Background()).Result()
|
_, err := c.client.Ping(ctx).Result()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
cache/stats.go
vendored
4
cache/stats.go
vendored
@ -7,11 +7,11 @@ type Stats struct {
|
|||||||
Misses uint64
|
Misses uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) AddHits() {
|
func (s *Stats) IncrementHits() {
|
||||||
atomic.AddUint64(&s.Hits, 1)
|
atomic.AddUint64(&s.Hits, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Stats) AddMisses() {
|
func (s *Stats) IncrementMisses() {
|
||||||
atomic.AddUint64(&s.Misses, 1)
|
atomic.AddUint64(&s.Misses, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
cache/tiny_lfu.go
vendored
18
cache/tiny_lfu.go
vendored
@ -7,6 +7,8 @@ import (
|
|||||||
"github.com/vmihailenco/go-tinylfu"
|
"github.com/vmihailenco/go-tinylfu"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ MemCache = &TinyLFU{}
|
||||||
|
|
||||||
type TinyLFU struct {
|
type TinyLFU struct {
|
||||||
mu locker.Locker
|
mu locker.Locker
|
||||||
lfu *tinylfu.T
|
lfu *tinylfu.T
|
||||||
@ -21,7 +23,7 @@ func NewTinyLFU(size int, ttl time.Duration) *TinyLFU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TinyLFU) Set(key string, b []byte, expire time.Duration) {
|
func (c *TinyLFU) Set(key string, b []byte, expire time.Duration) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
@ -30,6 +32,8 @@ func (c *TinyLFU) Set(key string, b []byte, expire time.Duration) {
|
|||||||
Value: b,
|
Value: b,
|
||||||
ExpireAt: time.Now().Add(c.ttl),
|
ExpireAt: time.Now().Add(c.ttl),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TinyLFU) Get(key string) ([]byte, bool) {
|
func (c *TinyLFU) Get(key string) ([]byte, bool) {
|
||||||
@ -44,9 +48,17 @@ func (c *TinyLFU) Get(key string) ([]byte, bool) {
|
|||||||
return val.([]byte), true
|
return val.([]byte), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *TinyLFU) Del(key string) {
|
func (c *TinyLFU) Delete(keys ...string) error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
c.lfu.Del(key)
|
for _, k := range keys {
|
||||||
|
c.lfu.Del(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TinyLFU) Clear() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,3 +9,16 @@ func ParseDuration(s string) (time.Duration, error) {
|
|||||||
|
|
||||||
return time.ParseDuration(s)
|
return time.ParseDuration(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseDurationDefault(s string, d time.Duration) time.Duration {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
ret, err := time.ParseDuration(s)
|
||||||
|
if err != nil {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
14
calendar/scheduled_executor.go
Normal file
14
calendar/scheduled_executor.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package calendar
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ScheduledExecutor struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScheduledExecutor() *ScheduledExecutor {
|
||||||
|
return &ScheduledExecutor{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ScheduledExecutor) Schedule(i any, duration time.Duration) {
|
||||||
|
|
||||||
|
}
|
14
calendar/scheduled_executor_test.go
Normal file
14
calendar/scheduled_executor_test.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package calendar_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/calendar"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecutor(t *testing.T) {
|
||||||
|
executor := calendar.NewScheduledExecutor()
|
||||||
|
|
||||||
|
executor.Schedule(nil, time.Minute)
|
||||||
|
}
|
@ -1,9 +1,5 @@
|
|||||||
package list
|
package list
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/charlienet/go-mixed/locker"
|
|
||||||
)
|
|
||||||
|
|
||||||
const minCapacity = 16
|
const minCapacity = 16
|
||||||
|
|
||||||
type ArrayList[T any] struct {
|
type ArrayList[T any] struct {
|
||||||
@ -31,7 +27,7 @@ func NewArrayList[T any](elems ...T) *ArrayList[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
l := &ArrayList[T]{
|
l := &ArrayList[T]{
|
||||||
list: list[T]{size: size, locker: locker.EmptyLocker},
|
list: list[T]{size: size},
|
||||||
buf: buf,
|
buf: buf,
|
||||||
tail: tail,
|
tail: tail,
|
||||||
minCap: minCap,
|
minCap: minCap,
|
||||||
@ -45,8 +41,8 @@ func NewArrayList[T any](elems ...T) *ArrayList[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) PushFront(v T) {
|
func (l *ArrayList[T]) PushFront(v T) {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.grow()
|
l.grow()
|
||||||
|
|
||||||
@ -56,8 +52,8 @@ func (l *ArrayList[T]) PushFront(v T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) PushBack(v T) {
|
func (l *ArrayList[T]) PushBack(v T) {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.grow()
|
l.grow()
|
||||||
|
|
||||||
@ -68,8 +64,8 @@ func (l *ArrayList[T]) PushBack(v T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) PopFront() T {
|
func (l *ArrayList[T]) PopFront() T {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
if l.size <= 0 {
|
if l.size <= 0 {
|
||||||
panic("list: PopFront() called on empty list")
|
panic("list: PopFront() called on empty list")
|
||||||
@ -86,8 +82,8 @@ func (l *ArrayList[T]) PopFront() T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) PopBack() T {
|
func (l *ArrayList[T]) PopBack() T {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.tail = l.prev(l.tail)
|
l.tail = l.prev(l.tail)
|
||||||
|
|
||||||
@ -105,8 +101,8 @@ func (l *ArrayList[T]) RemoveAt(at int) T {
|
|||||||
panic(ErrorOutOffRange)
|
panic(ErrorOutOffRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
rm := (l.head + at) & (len(l.buf) - 1)
|
rm := (l.head + at) & (len(l.buf) - 1)
|
||||||
if at*2 < l.size {
|
if at*2 < l.size {
|
||||||
@ -127,22 +123,22 @@ func (l *ArrayList[T]) RemoveAt(at int) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) Front() T {
|
func (l *ArrayList[T]) Front() T {
|
||||||
l.locker.RLock()
|
l.mu.RLock()
|
||||||
defer l.locker.RUnlock()
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
return l.buf[l.head]
|
return l.buf[l.head]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) Back() T {
|
func (l *ArrayList[T]) Back() T {
|
||||||
l.locker.RLock()
|
l.mu.RLock()
|
||||||
defer l.locker.RUnlock()
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
return l.buf[l.tail]
|
return l.buf[l.tail]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *ArrayList[T]) ForEach(fn func(T)) {
|
func (l *ArrayList[T]) ForEach(fn func(T)) {
|
||||||
l.locker.RLock()
|
l.mu.RLock()
|
||||||
defer l.locker.RUnlock()
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
n := l.head
|
n := l.head
|
||||||
for i := 0; i < l.size; i++ {
|
for i := 0; i < l.size; i++ {
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
package list
|
package list
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/charlienet/go-mixed/locker"
|
|
||||||
)
|
|
||||||
|
|
||||||
type LinkedList[T any] struct {
|
type LinkedList[T any] struct {
|
||||||
list[T]
|
list[T]
|
||||||
front, tail *LinkedNode[T]
|
front, tail *LinkedNode[T]
|
||||||
@ -16,9 +12,7 @@ type LinkedNode[T any] struct {
|
|||||||
|
|
||||||
// NewLinkedList 初始化链表
|
// NewLinkedList 初始化链表
|
||||||
func NewLinkedList[T any](elems ...T) *LinkedList[T] {
|
func NewLinkedList[T any](elems ...T) *LinkedList[T] {
|
||||||
l := &LinkedList[T]{
|
l := &LinkedList[T]{}
|
||||||
list: list[T]{locker: locker.EmptyLocker},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range elems {
|
for _, e := range elems {
|
||||||
l.pushBackNode(&LinkedNode[T]{Value: e})
|
l.pushBackNode(&LinkedNode[T]{Value: e})
|
||||||
@ -28,8 +22,8 @@ func NewLinkedList[T any](elems ...T) *LinkedList[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *LinkedList[T]) PushBack(v T) *LinkedList[T] {
|
func (l *LinkedList[T]) PushBack(v T) *LinkedList[T] {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.pushBackNode(&LinkedNode[T]{Value: v})
|
l.pushBackNode(&LinkedNode[T]{Value: v})
|
||||||
|
|
||||||
@ -37,8 +31,8 @@ func (l *LinkedList[T]) PushBack(v T) *LinkedList[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *LinkedList[T]) PushFront(v T) *LinkedList[T] {
|
func (l *LinkedList[T]) PushFront(v T) *LinkedList[T] {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
l.pushFrontNode(&LinkedNode[T]{Value: v})
|
l.pushFrontNode(&LinkedNode[T]{Value: v})
|
||||||
|
|
||||||
@ -65,8 +59,8 @@ func (l *LinkedList[T]) Back() T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *LinkedList[T]) ForEach(fn func(T) bool) {
|
func (l *LinkedList[T]) ForEach(fn func(T) bool) {
|
||||||
l.locker.RLock()
|
l.mu.RLock()
|
||||||
defer l.locker.RUnlock()
|
defer l.mu.RUnlock()
|
||||||
|
|
||||||
for current := l.front; current != nil; current = current.Next {
|
for current := l.front; current != nil; current = current.Next {
|
||||||
if fn(current.Value) {
|
if fn(current.Value) {
|
||||||
@ -77,12 +71,10 @@ func (l *LinkedList[T]) ForEach(fn func(T) bool) {
|
|||||||
|
|
||||||
func (l *LinkedList[T]) GetAt(i int) T {
|
func (l *LinkedList[T]) GetAt(i int) T {
|
||||||
if i <= l.Size() {
|
if i <= l.Size() {
|
||||||
var n int
|
for n, current := 0, l.front; current != nil; current, n = current.Next, n+1 {
|
||||||
for current := l.front; current != nil; current = current.Next {
|
|
||||||
if n == i {
|
if n == i {
|
||||||
return current.Value
|
return current.Value
|
||||||
}
|
}
|
||||||
n++
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,8 +82,8 @@ func (l *LinkedList[T]) GetAt(i int) T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *LinkedList[T]) Remove(n *LinkedNode[T]) {
|
func (l *LinkedList[T]) Remove(n *LinkedNode[T]) {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
if n.Next != nil {
|
if n.Next != nil {
|
||||||
n.Next.Prev = n.Prev
|
n.Next.Prev = n.Prev
|
||||||
@ -112,8 +104,8 @@ func (l *LinkedList[T]) Remove(n *LinkedNode[T]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *LinkedList[T]) RemoveAt(index int) {
|
func (l *LinkedList[T]) RemoveAt(index int) {
|
||||||
l.locker.Lock()
|
l.mu.Lock()
|
||||||
defer l.locker.Unlock()
|
defer l.mu.Unlock()
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
for current := l.front; current != nil; current = current.Next {
|
for current := l.front; current != nil; current = current.Next {
|
||||||
|
@ -13,11 +13,11 @@ type List[T any] interface {
|
|||||||
|
|
||||||
type list[T any] struct {
|
type list[T any] struct {
|
||||||
size int
|
size int
|
||||||
locker locker.RWLocker
|
mu locker.WithRWLocker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *list[T]) Synchronize() {
|
func (l *list[T]) Synchronize() {
|
||||||
l.locker = locker.NewRWLocker()
|
l.mu.Synchronize()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *list[T]) ForEach(fn func(T) bool) { panic("Not Implemented") }
|
func (l *list[T]) ForEach(fn func(T) bool) { panic("Not Implemented") }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package collections
|
package collections
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
var _ Queue[string] = &ArrayQueue[string]{}
|
var _ Queue[string] = &ArrayQueue[string]{}
|
||||||
|
|
||||||
|
@ -1 +1,10 @@
|
|||||||
package rbtree
|
package rbtree
|
||||||
|
|
||||||
|
type color bool
|
||||||
|
|
||||||
|
const (
|
||||||
|
black, red color = true, false
|
||||||
|
)
|
||||||
|
|
||||||
|
type TreeNode[K any, V any] struct {
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package crypto
|
package sm2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
@ -14,7 +14,7 @@ var (
|
|||||||
C1C2C3 = 1
|
C1C2C3 = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ IAsymmetric = &sm2Instance{}
|
type option func(*sm2Instance) error
|
||||||
|
|
||||||
type sm2Instance struct {
|
type sm2Instance struct {
|
||||||
mode int
|
mode int
|
||||||
@ -22,9 +22,42 @@ type sm2Instance struct {
|
|||||||
puk *s.PublicKey
|
puk *s.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
type option func(*sm2Instance) error
|
func WithSm2PrivateKey(p []byte, pwd []byte) option {
|
||||||
|
return func(so *sm2Instance) error {
|
||||||
|
priv, err := x.ReadPrivateKeyFromPem(p, pwd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func NewSm2(opts ...option) (*sm2Instance, error) {
|
so.prk = priv
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithSm2PublicKey(p []byte) option {
|
||||||
|
return func(so *sm2Instance) error {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := x.ReadPublicKeyFromPem(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
so.puk = pub
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithMode(mode int) option {
|
||||||
|
return func(so *sm2Instance) error {
|
||||||
|
so.mode = mode
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(opts ...option) (*sm2Instance, error) {
|
||||||
o := &sm2Instance{
|
o := &sm2Instance{
|
||||||
mode: defaultMode,
|
mode: defaultMode,
|
||||||
}
|
}
|
||||||
@ -51,37 +84,6 @@ func NewSm2(opts ...option) (*sm2Instance, error) {
|
|||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseSm2PrivateKey(p []byte, pwd []byte) option {
|
|
||||||
return func(so *sm2Instance) error {
|
|
||||||
priv, err := x.ReadPrivateKeyFromPem(p, pwd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
so.prk = priv
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseSm2PublicKey(p []byte) option {
|
|
||||||
return func(so *sm2Instance) error {
|
|
||||||
pub, err := x.ReadPublicKeyFromPem(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
so.puk = pub
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithMode(mode int) option {
|
|
||||||
return func(so *sm2Instance) error {
|
|
||||||
so.mode = mode
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *sm2Instance) Encrypt(msg []byte) ([]byte, error) {
|
func (o *sm2Instance) Encrypt(msg []byte) ([]byte, error) {
|
||||||
return s.Encrypt(o.puk, msg, rand.Reader, o.mode)
|
return s.Encrypt(o.puk, msg, rand.Reader, o.mode)
|
||||||
}
|
}
|
@ -1,20 +1,34 @@
|
|||||||
package crypto_test
|
package sm2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/charlienet/go-mixed/crypto"
|
"github.com/tjfoc/gmsm/sm2"
|
||||||
|
x "github.com/tjfoc/gmsm/x509"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestPem(t *testing.T) {
|
||||||
|
|
||||||
|
key, _ := sm2.GenerateKey(rand.Reader)
|
||||||
|
|
||||||
|
prv, _ := x.WritePrivateKeyToPem(key, []byte{})
|
||||||
|
pub, _ := x.WritePublicKeyToPem(key.Public().(*sm2.PublicKey))
|
||||||
|
|
||||||
|
t.Log(x.WritePublicKeyToHex(&key.PublicKey))
|
||||||
|
t.Log(string(prv))
|
||||||
|
t.Log(string(pub))
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewSm2(t *testing.T) {
|
func TestNewSm2(t *testing.T) {
|
||||||
o, err := crypto.NewSm2()
|
o, err := New()
|
||||||
t.Logf("%+v, %v", o, err)
|
t.Logf("%+v, %v", o, err)
|
||||||
|
|
||||||
t.Log(crypto.NewSm2(crypto.ParseSm2PrivateKey([]byte{}, []byte{})))
|
t.Log(New(WithSm2PrivateKey([]byte{}, []byte{})))
|
||||||
|
|
||||||
msg := []byte("123456")
|
msg := []byte("123456")
|
||||||
sign, err := o.Sign(msg)
|
sign, err := o.Sign(msg)
|
||||||
@ -49,9 +63,9 @@ hslcifiQY8173nHtaB3R6T0PwRQTwKbpdec0dwVCpvVcdzHtivndlG0mqQ==
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestPrivatePem(t *testing.T) {
|
func TestPrivatePem(t *testing.T) {
|
||||||
signer, err := crypto.NewSm2(
|
signer, err := New(
|
||||||
crypto.ParseSm2PrivateKey([]byte(privPem), []byte{}),
|
WithSm2PrivateKey([]byte(privPem), []byte{}),
|
||||||
crypto.ParseSm2PublicKey([]byte(pubPem)))
|
WithSm2PublicKey([]byte(pubPem)))
|
||||||
|
|
||||||
t.Log(signer, err)
|
t.Log(signer, err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -67,9 +81,9 @@ func TestPrivatePem(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBadPublicPem(t *testing.T) {
|
func TestBadPublicPem(t *testing.T) {
|
||||||
signer, err := crypto.NewSm2(
|
signer, err := New(
|
||||||
crypto.ParseSm2PrivateKey([]byte(privPem), []byte{}),
|
WithSm2PrivateKey([]byte(privPem), []byte{}),
|
||||||
crypto.ParseSm2PublicKey([]byte(badPubPem)))
|
WithSm2PublicKey([]byte(badPubPem)))
|
||||||
|
|
||||||
t.Log(signer, err)
|
t.Log(signer, err)
|
||||||
|
|
19
encode/encode.go
Normal file
19
encode/encode.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package encode
|
||||||
|
|
||||||
|
import "encoding/hex"
|
||||||
|
|
||||||
|
type BytesResult []byte
|
||||||
|
|
||||||
|
func FromString(s string) BytesResult {
|
||||||
|
return BytesResult([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromHexString(s string) BytesResult {
|
||||||
|
b, _ := hex.DecodeString(s)
|
||||||
|
return BytesResult(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromBytes(b []byte) BytesResult {
|
||||||
|
return BytesResult(b)
|
||||||
|
}
|
||||||
|
|
@ -12,6 +12,17 @@ const (
|
|||||||
defaultErrorCode = "999999"
|
defaultErrorCode = "999999"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Error interface {
|
||||||
|
Wraped() []error
|
||||||
|
Code() string
|
||||||
|
|
||||||
|
error
|
||||||
|
|
||||||
|
private()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Error = &CodeError{}
|
||||||
|
|
||||||
type CodeError struct {
|
type CodeError struct {
|
||||||
cause error // 原始错误信息
|
cause error // 原始错误信息
|
||||||
code string // 错误码
|
code string // 错误码
|
||||||
@ -70,6 +81,10 @@ func (e *CodeError) WithCause(err error) *CodeError {
|
|||||||
return new(err, e.code, e.message)
|
return new(err, e.code, e.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *CodeError) Wraped() []error {
|
||||||
|
return []error{}
|
||||||
|
}
|
||||||
|
|
||||||
func (e *CodeError) Format(s fmt.State, verb rune) {
|
func (e *CodeError) Format(s fmt.State, verb rune) {
|
||||||
switch verb {
|
switch verb {
|
||||||
case 'v':
|
case 'v':
|
||||||
@ -86,6 +101,8 @@ func (e *CodeError) Format(s fmt.State, verb rune) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*CodeError) private() {}
|
||||||
|
|
||||||
func new(err error, code string, args ...any) *CodeError {
|
func new(err error, code string, args ...any) *CodeError {
|
||||||
return &CodeError{
|
return &CodeError{
|
||||||
code: code,
|
code: code,
|
||||||
@ -102,7 +119,7 @@ func newf(err error, code string, format string, args ...any) *CodeError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Error(code string, args ...any) *CodeError {
|
func ErrorWithCode(code string, args ...any) *CodeError {
|
||||||
return new(nil, code, args...)
|
return new(nil, code, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,8 +16,8 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
globalError = errors.Error(defaultErrorCode, "全局错误对象")
|
globalError = errors.ErrorWithCode(defaultErrorCode, "全局错误对象")
|
||||||
errorbyCode = errors.Error(testCode, "全局错误对象")
|
errorbyCode = errors.ErrorWithCode(testCode, "全局错误对象")
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPersetError(t *testing.T) {
|
func TestPersetError(t *testing.T) {
|
||||||
@ -40,7 +40,7 @@ func TestWithMessage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewError(t *testing.T) {
|
func TestNewError(t *testing.T) {
|
||||||
var e error = errors.Error("123456", "测试")
|
var e error = errors.ErrorWithCode("123456", "测试")
|
||||||
|
|
||||||
err := e.(*errors.CodeError)
|
err := e.(*errors.CodeError)
|
||||||
t.Log(e, err.Code())
|
t.Log(e, err.Code())
|
||||||
@ -58,17 +58,17 @@ func TestWithStack(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLogMessage(t *testing.T) {
|
func TestLogMessage(t *testing.T) {
|
||||||
t.Logf("%+v", errors.Error("88888", "错误消息"))
|
t.Logf("%+v", errors.ErrorWithCode("88888", "错误消息"))
|
||||||
t.Log(errors.Error("77777"))
|
t.Log(errors.ErrorWithCode("77777"))
|
||||||
t.Log(errors.Error("77777", "测试"))
|
t.Log(errors.ErrorWithCode("77777", "测试"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIs(t *testing.T) {
|
func TestIs(t *testing.T) {
|
||||||
code1 := "000090"
|
code1 := "000090"
|
||||||
code2 := "000091"
|
code2 := "000091"
|
||||||
e1 := errors.Error(code1)
|
e1 := errors.ErrorWithCode(code1)
|
||||||
e2 := errors.Error(code1)
|
e2 := errors.ErrorWithCode(code1)
|
||||||
e3 := errors.Error(code2)
|
e3 := errors.ErrorWithCode(code2)
|
||||||
|
|
||||||
t.Log(errors.Is(e1, e2))
|
t.Log(errors.Is(e1, e2))
|
||||||
t.Log(errors.Is(e1, e3))
|
t.Log(errors.Is(e1, e3))
|
||||||
|
21
file_store/file_store.go
Normal file
21
file_store/file_store.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package filestore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() {
|
||||||
|
f, err := os.Open("")
|
||||||
|
|
||||||
|
_ = err
|
||||||
|
_ = f
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Store(name string, reader io.Reader) error {
|
||||||
|
|
||||||
|
return errors.New("abc")
|
||||||
|
|
||||||
|
}
|
6
file_store/oss/oss.go
Normal file
6
file_store/oss/oss.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package oss
|
||||||
|
|
||||||
|
// add two int
|
||||||
|
func Add(a, b int) {
|
||||||
|
|
||||||
|
}
|
38
file_store/readme.md
Normal file
38
file_store/readme.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 文件存储组件
|
||||||
|
|
||||||
|
1. 支持磁盘,OSS,FTP等
|
||||||
|
2. 支持自定义存储
|
||||||
|
3. 支持同时存储到不同的存储目标
|
||||||
|
|
||||||
|
创建文件存储
|
||||||
|
|
||||||
|
```GO
|
||||||
|
|
||||||
|
New().WithStore(LocalDisk("D:\abc"))
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
删除文件
|
||||||
|
|
||||||
|
```GO
|
||||||
|
|
||||||
|
filestore.Delete("filename")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
文件移动
|
||||||
|
|
||||||
|
```GO
|
||||||
|
|
||||||
|
filestore.Move("old", "new")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
文件重命名
|
||||||
|
|
||||||
|
```GO
|
||||||
|
|
||||||
|
filestore.Rename("old", "new")
|
||||||
|
|
||||||
|
```
|
51
go.mod
51
go.mod
@ -1,52 +1,61 @@
|
|||||||
module github.com/charlienet/go-mixed
|
module github.com/charlienet/go-mixed
|
||||||
|
|
||||||
go 1.18
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bits-and-blooms/bitset v1.3.3
|
github.com/bits-and-blooms/bitset v1.8.0
|
||||||
github.com/cespare/xxhash/v2 v2.1.2
|
github.com/cespare/xxhash/v2 v2.2.0
|
||||||
github.com/go-playground/universal-translator v0.18.0
|
github.com/go-playground/universal-translator v0.18.1
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/shopspring/decimal v1.3.1
|
github.com/shopspring/decimal v1.3.1
|
||||||
github.com/spaolacci/murmur3 v1.1.0
|
github.com/spaolacci/murmur3 v1.1.0
|
||||||
github.com/tjfoc/gmsm v1.4.1
|
github.com/tjfoc/gmsm v1.4.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/alphadose/haxmap v1.3.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
||||||
github.com/jonboulle/clockwork v0.3.0 // indirect
|
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||||
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
|
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect
|
||||||
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
|
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.1 // indirect
|
||||||
github.com/tebeka/strftime v0.1.5 // indirect
|
github.com/tebeka/strftime v0.1.5 // indirect
|
||||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
github.com/yuin/gopher-lua v1.1.0 // indirect
|
||||||
|
golang.org/x/net v0.14.0 // indirect
|
||||||
|
golang.org/x/text v0.12.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alicebob/miniredis/v2 v2.23.0
|
github.com/allegro/bigcache/v3 v3.1.0
|
||||||
github.com/allegro/bigcache/v3 v3.0.2
|
github.com/coocood/freecache v1.2.3
|
||||||
github.com/alphadose/haxmap v1.0.2
|
github.com/vmihailenco/go-tinylfu v0.2.2
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alicebob/miniredis/v2 v2.30.5
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
||||||
github.com/coocood/freecache v1.2.2
|
github.com/dlclark/regexp2 v1.10.0
|
||||||
github.com/dlclark/regexp2 v1.7.0
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5
|
github.com/go-redis/redis/v8 v8.11.5
|
||||||
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
|
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.3
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/vmihailenco/go-tinylfu v0.2.2
|
github.com/vmihailenco/msgpack/v5 v5.3.5
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
golang.org/x/crypto v0.12.0
|
||||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
|
golang.org/x/sync v0.3.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
golang.org/x/sys v0.11.0 // indirect
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
|
|
||||||
)
|
)
|
||||||
|
114
go.sum
114
go.sum
@ -3,27 +3,40 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
||||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw=
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
|
||||||
github.com/alicebob/miniredis/v2 v2.23.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88=
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||||
github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI=
|
github.com/alicebob/miniredis/v2 v2.23.1 h1:jR6wZggBxwWygeXcdNyguCOCIjPsZyNUNlAkTx2fu0U=
|
||||||
github.com/allegro/bigcache/v3 v3.0.2/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
|
github.com/alicebob/miniredis/v2 v2.23.1/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q=
|
||||||
github.com/alphadose/haxmap v1.0.2 h1:ZZwFf15DcsAz4O+SyqrpH/xeO5Plh7mNRXDM9QIcWQQ=
|
github.com/alicebob/miniredis/v2 v2.30.0 h1:uA3uhDbCxfO9+DI/DuGeAMr9qI+noVWwGPNTFuKID5M=
|
||||||
github.com/alphadose/haxmap v1.0.2/go.mod h1:Pq2IXbl9/ytYHfrIAd7rIVtZQ2ezdIhZfvdqOizDeWY=
|
github.com/alicebob/miniredis/v2 v2.30.0/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q=
|
||||||
|
github.com/alicebob/miniredis/v2 v2.30.5 h1:3r6kTHdKnuP4fkS8k2IrvSfxpxUTcW1SOL0wN7b7Dt0=
|
||||||
|
github.com/alicebob/miniredis/v2 v2.30.5/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg=
|
||||||
|
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
|
||||||
|
github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
|
||||||
|
github.com/alphadose/haxmap v1.2.0 h1:noGrAmCE+gNheZ4KpW+sYj9W5uMcO1UAjbAq9XBOAfM=
|
||||||
|
github.com/alphadose/haxmap v1.2.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
|
||||||
|
github.com/alphadose/haxmap v1.3.0 h1:C/2LboOnPCZP27GmmSXOcwx360st0P8N0fTJ3voefKc=
|
||||||
|
github.com/alphadose/haxmap v1.3.0/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM=
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
|
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
|
||||||
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
||||||
github.com/bits-and-blooms/bitset v1.3.3 h1:R1XWiopGiXf66xygsiLpzLo67xEYvMkHw3w+rCOSAwg=
|
github.com/bits-and-blooms/bitset v1.4.0 h1:+YZ8ePm+He2pU3dZlIZiOeAKfrBkXi1lSrXJ/Xzgbu8=
|
||||||
github.com/bits-and-blooms/bitset v1.3.3/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
github.com/bits-and-blooms/bitset v1.4.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||||
|
github.com/bits-and-blooms/bitset v1.5.0 h1:NpE8frKRLGHIcEzkR+gZhiioW1+WbYV6fKwD6ZIpQT8=
|
||||||
|
github.com/bits-and-blooms/bitset v1.5.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||||
|
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
|
||||||
|
github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/coocood/freecache v1.2.2 h1:UPkJCxhRujykq1jXuwxAPgDHnm6lKGrLZPnuHzgWRtE=
|
github.com/coocood/freecache v1.2.3 h1:lcBwpZrwBZRZyLk/8EMyQVXRiFl663cCuMOrjCALeto=
|
||||||
github.com/coocood/freecache v1.2.2/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
|
github.com/coocood/freecache v1.2.3/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -31,6 +44,10 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
|||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
||||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0=
|
||||||
|
github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||||
|
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
@ -39,8 +56,12 @@ github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0
|
|||||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
@ -63,6 +84,8 @@ github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uc
|
|||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
||||||
github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
|
github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
|
||||||
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||||
|
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||||
|
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo=
|
github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo=
|
||||||
@ -88,31 +111,57 @@ github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5g
|
|||||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
|
||||||
|
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/tebeka/strftime v0.1.5 h1:1NQKN1NiQgkqd/2moD6ySP/5CoZQsKa1d3ZhJ44Jpmg=
|
github.com/tebeka/strftime v0.1.5 h1:1NQKN1NiQgkqd/2moD6ySP/5CoZQsKa1d3ZhJ44Jpmg=
|
||||||
github.com/tebeka/strftime v0.1.5/go.mod h1:29/OidkoWHdEKZqzyDLUyC+LmgDgdHo4WAFCDT7D/Ig=
|
github.com/tebeka/strftime v0.1.5/go.mod h1:29/OidkoWHdEKZqzyDLUyC+LmgDgdHo4WAFCDT7D/Ig=
|
||||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||||
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
|
||||||
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
|
github.com/vmihailenco/go-tinylfu v0.2.2 h1:H1eiG6HM36iniK6+21n9LLpzx1G9R3DJa2UjUjbynsI=
|
||||||
github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
|
github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw=
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
|
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64 h1:5mLPGnFdSsevFRFc9q3yYbBkB6tsm4aCwwQV/j1JQAQ=
|
||||||
|
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||||
|
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
|
||||||
|
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
|
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
|
||||||
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI=
|
||||||
|
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
|
||||||
|
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
@ -122,24 +171,41 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||||
|
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@ -164,6 +230,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
|||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
118
idGenerator/id_generator.go
Normal file
118
idGenerator/id_generator.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package idgenerator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 时间段开始时间 2022-01-01
|
||||||
|
const startTimeStamp = 1640966400
|
||||||
|
|
||||||
|
const (
|
||||||
|
MachineIdBits = uint(8) //机器id所占的位数
|
||||||
|
SequenceBits = uint(12) //序列所占的位数
|
||||||
|
MachineIdMax = int64(-1 ^ (-1 << MachineIdBits)) //支持的最大机器id数量
|
||||||
|
SequenceMask = uint64(-1 ^ (-1 << SequenceBits)) //
|
||||||
|
MachineIdShift = uint64(SequenceBits) //机器id左移位数
|
||||||
|
TimestampShift = uint64(SequenceBits + MachineIdBits) //时间戳左移位数
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimePrecision int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Second TimePrecision = iota // 秒
|
||||||
|
Minute // 分
|
||||||
|
Day // 日
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Machine int
|
||||||
|
TimeScope TimePrecision
|
||||||
|
}
|
||||||
|
|
||||||
|
type Generator struct {
|
||||||
|
machine uint64
|
||||||
|
timeScope TimePrecision // 时间段精度
|
||||||
|
timestamp uint64 // 上次生成时间
|
||||||
|
sequence uint64 // 上次使用序列
|
||||||
|
}
|
||||||
|
|
||||||
|
type Id struct {
|
||||||
|
machine uint64 // 机器标识
|
||||||
|
Scope int // 时间精度
|
||||||
|
Timestamp uint64 // 生成时间
|
||||||
|
Sequence uint64 // 标识序列
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(cfg Config) *Generator {
|
||||||
|
return &Generator{
|
||||||
|
machine: uint64(cfg.Machine),
|
||||||
|
timeScope: cfg.TimeScope,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) Next() Id {
|
||||||
|
|
||||||
|
now := currentTimestamp()
|
||||||
|
|
||||||
|
if g.timestamp == now && g.sequence == 0 {
|
||||||
|
fmt.Println(time.Now().Format("2006-01-02 15:04:05.000"), "下一个时间点")
|
||||||
|
for now <= g.timestamp {
|
||||||
|
// runtime.Gosched()
|
||||||
|
now = currentTimestamp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.timestamp = now // 标识生成时间
|
||||||
|
g.sequence = (g.sequence + 1) & SequenceMask // 生成下一序列,超过最大值时返回零
|
||||||
|
|
||||||
|
return Id{
|
||||||
|
machine: g.machine,
|
||||||
|
Timestamp: g.timestamp,
|
||||||
|
Sequence: g.sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Id) Id() uint64 {
|
||||||
|
return i.Timestamp<<TimestampShift | i.machine<<MachineIdShift | i.Sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Id) genCheck() uint64 {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Id) String() string {
|
||||||
|
return fmt.Sprintf("Time:%d scope: %d Machine: %d Ser:%06d id:%d", i.Timestamp, i.Scope, i.machine, i.Sequence, i.Id())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) NextId() uint64 {
|
||||||
|
return g.Next().Id()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) Batch(num int) []uint64 {
|
||||||
|
ret := make([]uint64, num)
|
||||||
|
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
ret[i] = g.NextId()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func Decode(id uint64) Id {
|
||||||
|
return Id{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Verify(id uint64) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentTimestamp() uint64 {
|
||||||
|
return uint64(time.Now().Unix() - startTimeStamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname runtimeNano runtime.nanotime
|
||||||
|
func runtimeNano() int64
|
32
idGenerator/id_generator_test.go
Normal file
32
idGenerator/id_generator_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package idgenerator_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
idgenerator "github.com/charlienet/go-mixed/idGenerator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNextId(t *testing.T) {
|
||||||
|
generator := idgenerator.New(idgenerator.Config{})
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
t.Log(generator.Next().Id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBatch(t *testing.T) {
|
||||||
|
generator := idgenerator.New(idgenerator.Config{})
|
||||||
|
|
||||||
|
b := generator.Batch(20)
|
||||||
|
for _, i := range b {
|
||||||
|
t.Log(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNext(t *testing.T) {
|
||||||
|
generator := idgenerator.New(idgenerator.Config{})
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
t.Log(generator.Next())
|
||||||
|
}
|
||||||
|
}
|
38
ketama/ketama.go
Normal file
38
ketama/ketama.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package ketama
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charlienet/go-mixed/locker"
|
||||||
|
"github.com/charlienet/go-mixed/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ketama struct {
|
||||||
|
mu locker.WithRWLocker
|
||||||
|
replicas int
|
||||||
|
m maps.Map[uint64, string]
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *Ketama {
|
||||||
|
return &Ketama{
|
||||||
|
m: maps.NewHashMap[uint64, string](),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Ketama) Synchronize() {
|
||||||
|
k.mu.Synchronize()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Ketama) Add(nodes ...string) {
|
||||||
|
k.mu.Lock()
|
||||||
|
defer k.mu.Unlock()
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
_ = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Ketama) IsEmpty() bool {
|
||||||
|
k.mu.RLock()
|
||||||
|
defer k.mu.RUnlock()
|
||||||
|
|
||||||
|
return k.m.Count() == 0
|
||||||
|
}
|
32
ketama/ketama_test.go
Normal file
32
ketama/ketama_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package ketama_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/ketama"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
k := ketama.New()
|
||||||
|
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
k.Synchronize()
|
||||||
|
|
||||||
|
// k.Lock()
|
||||||
|
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
|
||||||
|
t.Log(k.IsEmpty())
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
t.Log(k.IsEmpty())
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
k.Synchronize()
|
||||||
|
|
||||||
|
t.Log(k.IsEmpty())
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
t.Log(k.IsEmpty())
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
t.Log(k.IsEmpty())
|
||||||
|
|
||||||
|
t.Logf("%+v", k)
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
package locker
|
package locker
|
||||||
|
|
||||||
|
var _ RWLocker = &emptyLocker{}
|
||||||
|
var _ Locker = &emptyLocker{}
|
||||||
|
|
||||||
var EmptyLocker = &emptyLocker{}
|
var EmptyLocker = &emptyLocker{}
|
||||||
|
|
||||||
type emptyLocker struct{}
|
type emptyLocker struct{}
|
||||||
|
96
locker/synchronizeable.go
Normal file
96
locker/synchronizeable.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package locker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WithLocker struct {
|
||||||
|
once sync.Once
|
||||||
|
mu Locker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithLocker) Synchronize() {
|
||||||
|
if w.mu == nil || w.mu == EmptyLocker {
|
||||||
|
w.mu = NewLocker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithLocker) Lock() {
|
||||||
|
w.ensureLocker().Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithLocker) Unlock() {
|
||||||
|
w.ensureLocker().Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithLocker) TryLock() bool {
|
||||||
|
return w.ensureLocker().TryLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithLocker) ensureLocker() Locker {
|
||||||
|
w.once.Do(func() {
|
||||||
|
if w.mu == nil {
|
||||||
|
w.mu = EmptyLocker
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return w.mu
|
||||||
|
}
|
||||||
|
|
||||||
|
type WithSpinLocker struct {
|
||||||
|
WithLocker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithSpinLocker) Synchronize() {
|
||||||
|
if w.mu == nil || w.mu == EmptyLocker {
|
||||||
|
w.mu = NewSpinLocker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WithRWLocker struct {
|
||||||
|
once sync.Once
|
||||||
|
mu RWLocker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) Synchronize() {
|
||||||
|
if w.mu == nil || w.mu == EmptyLocker {
|
||||||
|
log.Println("初始化有效锁")
|
||||||
|
w.mu = NewRWLocker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) Lock() {
|
||||||
|
w.ensureLocker().Lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) TryLock() bool {
|
||||||
|
return w.ensureLocker().TryLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) Unlock() {
|
||||||
|
w.ensureLocker().Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) RLock() {
|
||||||
|
w.ensureLocker().RLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) TryRLock() bool {
|
||||||
|
return w.ensureLocker().TryRLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) RUnlock() {
|
||||||
|
w.ensureLocker().RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WithRWLocker) ensureLocker() RWLocker {
|
||||||
|
w.once.Do(func() {
|
||||||
|
if w.mu == nil {
|
||||||
|
log.Println("初始化一个空锁")
|
||||||
|
w.mu = EmptyLocker
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return w.mu
|
||||||
|
}
|
@ -4,7 +4,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/charlienet/go-mixed/fs"
|
"github.com/charlienet/go-mixed/fs"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -61,6 +60,12 @@ func WithOptions(o LogrusOptions) logrusOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithNestedFormatter(o NestedFormatterOption) logrusOption {
|
||||||
|
return func(logrusLogger *logrus.Logger) {
|
||||||
|
logrusLogger.Formatter = NewNestedFormatter(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithFormatter(formatter logrus.Formatter) logrusOption {
|
func WithFormatter(formatter logrus.Formatter) logrusOption {
|
||||||
return func(logrusLogger *logrus.Logger) {
|
return func(logrusLogger *logrus.Logger) {
|
||||||
logrusLogger.SetFormatter(formatter)
|
logrusLogger.SetFormatter(formatter)
|
||||||
@ -68,7 +73,6 @@ func WithFormatter(formatter logrus.Formatter) logrusOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func WithOutput(options LogrusOutputOptions) logrusOption {
|
func WithOutput(options LogrusOutputOptions) logrusOption {
|
||||||
_ = time.Now()
|
|
||||||
return func(l *logrus.Logger) {
|
return func(l *logrus.Logger) {
|
||||||
var writer io.Writer
|
var writer io.Writer
|
||||||
switch {
|
switch {
|
||||||
|
@ -3,8 +3,9 @@ package maps
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/exp/slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/expr"
|
||||||
xmaps "golang.org/x/exp/maps"
|
xmaps "golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,8 +125,12 @@ func (m *sorted_map[K, V]) Asc() SortedMap[K, V] {
|
|||||||
func (m *sorted_map[K, V]) Desc() SortedMap[K, V] {
|
func (m *sorted_map[K, V]) Desc() SortedMap[K, V] {
|
||||||
keys := m.keys
|
keys := m.keys
|
||||||
|
|
||||||
slices.SortFunc(keys, func(a, b K) bool {
|
slices.SortFunc(keys, func(a, b K) int {
|
||||||
return a > b
|
if a == b {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr.Ternary(a > b, -1, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
return &sorted_map[K, V]{
|
return &sorted_map[K, V]{
|
||||||
|
35
mathx/int.go
35
mathx/int.go
@ -1,35 +0,0 @@
|
|||||||
package mathx
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/charlienet/go-mixed/expr"
|
|
||||||
"golang.org/x/exp/constraints"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Max returns the larger one of v1 and v2.
|
|
||||||
func Max[T constraints.Ordered](v1, v2 T) T {
|
|
||||||
return expr.Ternary(v1 > v2, v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Min returns the smaller one of v1 and v2.
|
|
||||||
func Min[T constraints.Ordered](v1, v2 T) T {
|
|
||||||
return expr.Ternary(v1 < v2, v1, v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Abs1[T constraints.Signed](n T) T {
|
|
||||||
shift := 63
|
|
||||||
switch unsafe.Sizeof(n) {
|
|
||||||
case 1:
|
|
||||||
shift = 7
|
|
||||||
case 4:
|
|
||||||
shift = 31
|
|
||||||
}
|
|
||||||
|
|
||||||
y := n >> shift
|
|
||||||
return T((n ^ y) - y)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Abs(n int64) int64 {
|
|
||||||
y := n >> 63
|
|
||||||
return (n ^ y) - y
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package mathx_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/charlienet/go-mixed/mathx"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMin(t *testing.T) {
|
|
||||||
assert.Equal(t, 1, mathx.Min(1, 3))
|
|
||||||
assert.Equal(t, 2, mathx.Min(66, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMax(t *testing.T) {
|
|
||||||
assert.Equal(t, 3, mathx.Max(1, 3))
|
|
||||||
assert.Equal(t, 66, mathx.Max(66, 2))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAbs(t *testing.T) {
|
|
||||||
assert.Equal(t, 23, mathx.Abs1(23))
|
|
||||||
assert.Equal(t, 23, mathx.Abs1(-23))
|
|
||||||
assert.Equal(t, 0, mathx.Abs1(0))
|
|
||||||
|
|
||||||
var u int8 = -127
|
|
||||||
var exp int8 = 127
|
|
||||||
assert.Equal(t, exp, mathx.Abs1(u))
|
|
||||||
}
|
|
46
mathx/math.go
Normal file
46
mathx/math.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package mathx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charlienet/go-mixed/expr"
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Real interface {
|
||||||
|
constraints.Integer | constraints.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the larger one of v1 and v2.
|
||||||
|
func Max[T Real](v1, v2 T) T {
|
||||||
|
return expr.Ternary(v1 > v2, v1, v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Min returns the smaller one of v1 and v2.
|
||||||
|
func Min[T Real](v1, v2 T) T {
|
||||||
|
return expr.Ternary(v1 < v2, v1, v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Abs[T Real](val T) T {
|
||||||
|
return expr.Ternary(val < 0, -val, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neg returns the negative of value. It does not negate value. For
|
||||||
|
// negating, simply use -value instead.
|
||||||
|
func Neg[T Real](value T) T {
|
||||||
|
return expr.Ternary(value < 0, value, -value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Clamp[T Real](value, min, max T) T {
|
||||||
|
if min > max {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
if value < min {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
if value > max {
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
54
mathx/math_test.go
Normal file
54
mathx/math_test.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package mathx_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/mathx"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMin(t *testing.T) {
|
||||||
|
assert.Equal(t, 1, mathx.Min(1, 3))
|
||||||
|
assert.Equal(t, 2, mathx.Min(66, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMax(t *testing.T) {
|
||||||
|
assert.Equal(t, 3, mathx.Max(1, 3))
|
||||||
|
assert.Equal(t, 66, mathx.Max(66, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAbs(t *testing.T) {
|
||||||
|
assert.Equal(t, 23, mathx.Abs(23))
|
||||||
|
assert.Equal(t, 23, mathx.Abs(-23))
|
||||||
|
assert.Equal(t, 0, mathx.Abs(0))
|
||||||
|
|
||||||
|
var u int8 = -127
|
||||||
|
var exp int8 = 127
|
||||||
|
assert.Equal(t, exp, mathx.Abs(u))
|
||||||
|
|
||||||
|
assert.Equal(t, 1.23, mathx.Abs(-1.23))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClamp(t *testing.T) {
|
||||||
|
tests := []struct{ value, min, max, want int }{
|
||||||
|
// Min.
|
||||||
|
{-1, 0, 2, 0},
|
||||||
|
{0, 0, 2, 0},
|
||||||
|
// Mid.
|
||||||
|
{1, 0, 2, 1},
|
||||||
|
{2, 0, 2, 2},
|
||||||
|
// Max.
|
||||||
|
{2, 0, 2, 2},
|
||||||
|
{3, 0, 2, 2},
|
||||||
|
// Empty range.
|
||||||
|
{-1, 0, 0, 0},
|
||||||
|
{0, 0, 0, 0},
|
||||||
|
{1, 0, 0, 0},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
got := mathx.Clamp(test.value, test.min, test.max)
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Clamp(%v, %v, %v) = %v, want %v", test.value, test.min, test.max, got, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,7 @@ type option struct {
|
|||||||
type setFunc func(option)
|
type setFunc func(option)
|
||||||
|
|
||||||
func WithSync() setFunc {
|
func WithSync() setFunc {
|
||||||
|
|
||||||
return func(o option) {
|
return func(o option) {
|
||||||
o.locker = &sync.RWMutex{}
|
o.locker = &sync.RWMutex{}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package sets
|
package sets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/expr"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type sorted_set[T constraints.Ordered] struct {
|
type sorted_set[T constraints.Ordered] struct {
|
||||||
@ -50,8 +52,12 @@ func (s *sorted_set[T]) Asc() Set[T] {
|
|||||||
|
|
||||||
func (s *sorted_set[T]) Desc() Set[T] {
|
func (s *sorted_set[T]) Desc() Set[T] {
|
||||||
keys := s.sorted
|
keys := s.sorted
|
||||||
slices.SortFunc(keys, func(a, b T) bool {
|
slices.SortFunc(keys, func(a, b T) int {
|
||||||
return a > b
|
if a == b {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr.Ternary(a > b, -1, 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
return &sorted_set[T]{
|
return &sorted_set[T]{
|
||||||
|
8
stringx/append.go
Normal file
8
stringx/append.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package stringx
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func Append[E any](dst []byte, e E) []byte {
|
||||||
|
toAppend := fmt.Sprintf("%v", e)
|
||||||
|
return append(dst, []byte(toAppend)...)
|
||||||
|
}
|
8
stringx/append_test.go
Normal file
8
stringx/append_test.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package stringx_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAppend(t *testing.T) {
|
||||||
|
}
|
40
stringx/mask.go
Normal file
40
stringx/mask.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package stringx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Place int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Begin Place = iota
|
||||||
|
Middle
|
||||||
|
End
|
||||||
|
)
|
||||||
|
|
||||||
|
func Mask(s string, place Place, length int, mask ...rune) string {
|
||||||
|
m := '*'
|
||||||
|
if len(mask) > 0 {
|
||||||
|
m = mask[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(s)
|
||||||
|
if length >= n {
|
||||||
|
return strings.Repeat(string(m), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
if place == Middle {
|
||||||
|
i = (n - length) / 2
|
||||||
|
} else if place == End {
|
||||||
|
i = n - length
|
||||||
|
}
|
||||||
|
|
||||||
|
end := i + length
|
||||||
|
r := []rune(s)
|
||||||
|
for ; i < end; i++ {
|
||||||
|
r[i] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(r)
|
||||||
|
}
|
28
stringx/mask_test.go
Normal file
28
stringx/mask_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package stringx_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/stringx"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMask(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
origin string
|
||||||
|
place stringx.Place
|
||||||
|
length int
|
||||||
|
excepted string
|
||||||
|
}{
|
||||||
|
{"aa", stringx.Begin, 6, "**"},
|
||||||
|
{"18980832408", stringx.Begin, 4, "****0832408"},
|
||||||
|
{"18980832408", stringx.End, 4, "1898083****"},
|
||||||
|
{"18980832408", stringx.Middle, 4, "189****2408"},
|
||||||
|
}
|
||||||
|
|
||||||
|
a := assert.New(t)
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Log(stringx.Mask(c.origin, c.place, c.length))
|
||||||
|
a.Equal(c.excepted, stringx.Mask(c.origin, c.place, c.length))
|
||||||
|
}
|
||||||
|
}
|
25
tree/avl_tree.go
Normal file
25
tree/avl_tree.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package tree
|
||||||
|
|
||||||
|
type avlTree struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAVLTree() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 左单旋,新插入的节点在右子树的右侧
|
||||||
|
func (t *avlTree) rotateL() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右单旋,新插入的节点在左子树的左侧
|
||||||
|
func (t *avlTree) rotateR() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右左双旋,新插入的节点在右子树的左侧
|
||||||
|
// 1. 先对subR进行一个右单旋
|
||||||
|
// 2. 再对parent进行一个左单旋然后修改平衡因子
|
||||||
|
func (t *avlTree) rotateRL() {
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user