├── slices ├── README.md ├── shift.go ├── unshift.go ├── for_each.go ├── includes.go ├── index_of.go ├── reverse.go ├── last_index_of.go ├── fill.go ├── any.go ├── every.go ├── index_of_all.go ├── reduce.go ├── find_index.go ├── sort.go ├── find_last_index.go ├── map.go ├── reduce_right.go ├── find.go ├── frequencies.go ├── shuffle.go ├── distinct.go ├── find_last.go ├── to_slice.go ├── filter.go ├── sort_stable.go ├── to_map.go ├── shuffle_test.go ├── equal.go ├── reverse_test.go ├── distinct_test.go ├── for_each_test.go ├── unshift_test.go ├── frequencies_test.go ├── index_of_test.go ├── includes_test.go ├── last_index_of_test.go ├── shift_test.go ├── sort_test.go ├── filter_test.go ├── fill_test.go ├── index_of_all_test.go ├── map_test.go ├── to_slice_test.go ├── every_test.go ├── any_test.go ├── find_index_test.go ├── find_last_index_test.go ├── sort_stable_test.go ├── find_test.go ├── find_last_test.go ├── to_map_test.go ├── reduce_test.go ├── reduce_right_test.go └── equal_test.go ├── container ├── ringbuffer │ ├── docs │ │ ├── impl.png │ │ ├── use.png │ │ ├── logic.png │ │ ├── use.drawio │ │ ├── impl.drawio │ │ └── logic.drawio │ ├── README.md │ ├── round │ │ ├── mod_test.go │ │ ├── uint_overflow_test.go │ │ ├── round_test.go │ │ └── round.go │ ├── unbounded │ │ ├── unbounded_test.go │ │ └── unbounded.go │ └── fix │ │ ├── fix_test.go │ │ └── fix.go ├── stack │ ├── stack_test.go │ └── stack.go ├── list │ └── list_test.go ├── deque │ ├── deque_test.go │ └── deque.go ├── set │ ├── set_test.go │ └── set.go ├── heap │ ├── heapn_test.go │ ├── heap_test.go │ ├── heapn.go │ └── heap.go ├── pqueue │ ├── pqueue_test.go │ └── pqueue.go ├── meap │ ├── meap_test.go │ └── meap.go └── reap │ ├── reap_test.go │ └── reap.go ├── counter ├── README.md ├── qps │ ├── qps_test.go │ └── qps.go └── cm │ ├── cm4_test.go │ ├── cm.go │ ├── cm_test.go │ └── cm4.go ├── crypto ├── README.md ├── padding.go ├── aes │ ├── aes_test.go │ └── aes.go └── query │ ├── query_test.go │ └── query.go ├── go.mod ├── go.sum ├── mem ├── mem.go └── mem_test.go ├── validate ├── slice.go ├── number.go └── string.go ├── pool ├── README.md ├── byte_pool.go ├── fix │ ├── byte_pool.go │ ├── pool_test.go │ ├── buffer_pool.go │ └── pool.go ├── buffer_pool.go ├── pool.go ├── level │ ├── pool.go │ ├── byte_pool.go │ ├── buffer_pool.go │ └── pool_test.go └── pool_test.go ├── .gitignore ├── conv ├── string.go ├── byte.go └── json.go ├── cache ├── cache.go ├── random │ ├── random_test.go │ └── random.go ├── fifo │ ├── fifo.go │ └── fifo_test.go ├── arc │ └── arc_test.go ├── lru │ ├── lru.go │ └── lru_test.go └── slru │ └── slru_test.go ├── hash ├── hash.go └── hash_test.go ├── cmd └── cmd.go ├── README.md ├── limiter ├── sliding_log_limiter_test.go ├── fixed_window_limiter.go ├── token_bucket_limiter.go ├── fixed_window_limiter_test.go ├── leaky_bucket_limiter.go ├── token_bucket_limiter_test.go ├── leaky_bucket_limiter_test.go ├── sliding_window_limiter.go ├── sliding_window_limiter_test.go └── sliding_log_limiter.go ├── timer ├── delayqueue │ ├── delayqueue_test.go │ └── delayqueue.go └── timingwheel │ ├── timingwheel_test.go │ ├── bucket.go │ ├── delayqueue.go │ └── timingwheel.go ├── env └── env.go ├── math └── math.go ├── filter └── bloom │ ├── bloom_test.go │ └── bloom.go └── consistenthash ├── consistenthash.go └── consistenthash_test.go /slices/README.md: -------------------------------------------------------------------------------- 1 | # slices 2 | A set of generic functions for go slice 3 | -------------------------------------------------------------------------------- /container/ringbuffer/docs/impl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/gommon/main/container/ringbuffer/docs/impl.png -------------------------------------------------------------------------------- /container/ringbuffer/docs/use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/gommon/main/container/ringbuffer/docs/use.png -------------------------------------------------------------------------------- /counter/README.md: -------------------------------------------------------------------------------- 1 | # counter 2 | 计数器 3 | 4 | # cm 5 | CountMin计数器,近似统计,消耗空间小 6 | 7 | # qps 8 | 基于滑动窗口的QPS统计 -------------------------------------------------------------------------------- /crypto/README.md: -------------------------------------------------------------------------------- 1 | # crypto 2 | 加密算法 3 | 4 | # aes 5 | AES+CBC加密,添加一些便捷方法 6 | 7 | # query 8 | 可模糊查询的加密,基于分片加密思想 -------------------------------------------------------------------------------- /container/ringbuffer/docs/logic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctfang/gommon/main/container/ringbuffer/docs/logic.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jiaxwu/gommon 2 | 3 | go 1.18 4 | 5 | require golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e 6 | -------------------------------------------------------------------------------- /slices/shift.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Shift remove first item 4 | func Shift[T any](slice []T) ([]T, T) { 5 | t := slice[0] 6 | return slice[1:], t 7 | } 8 | -------------------------------------------------------------------------------- /slices/unshift.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Unshift add items from head 4 | func Unshift[T any](slice []T, items ...T) []T { 5 | return append(items, slice...) 6 | } 7 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= 2 | golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= 3 | -------------------------------------------------------------------------------- /slices/for_each.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // ForEach item execute action 4 | func ForEach[T any](slice []T, action func(item T, index int, slice []T)) { 5 | for index, item := range slice { 6 | action(item, index, slice) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /mem/mem.go: -------------------------------------------------------------------------------- 1 | package mem 2 | 3 | // 快速设置数组 4 | func Memset[T any](a []T, v T) { 5 | if len(a) == 0 { 6 | return 7 | } 8 | a[0] = v 9 | for bp := 1; bp < len(a); bp *= 2 { 10 | copy(a[bp:], a[:bp]) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /validate/slice.go: -------------------------------------------------------------------------------- 1 | package validate 2 | 3 | // 长度在范围内 4 | func SliceLen[T any](s []T, min, max int, errFunc func(min, max int) error) error { 5 | if len(s) < min || len(s) > max { 6 | return errFunc(min, max) 7 | } 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /slices/includes.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Includes item in slice 4 | func Includes[T comparable](slice []T, item T) bool { 5 | for _, _item := range slice { 6 | if _item == item { 7 | return true 8 | } 9 | } 10 | return false 11 | } 12 | -------------------------------------------------------------------------------- /slices/index_of.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // IndexOf slice[i] == item 4 | func IndexOf[T comparable](slice []T, item T) int { 5 | for i := 0; i < len(slice); i++ { 6 | if slice[i] == item { 7 | return i 8 | } 9 | } 10 | return -1 11 | } 12 | -------------------------------------------------------------------------------- /slices/reverse.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Reverse slice 4 | func Reverse[T any](slice []T) []T { 5 | reversed := make([]T, 0, len(slice)) 6 | for i := len(slice) - 1; i >= 0; i-- { 7 | reversed = append(reversed, slice[i]) 8 | } 9 | return reversed 10 | } 11 | -------------------------------------------------------------------------------- /slices/last_index_of.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // LastIndexOf slice[i] == item 4 | func LastIndexOf[T comparable](slice []T, item T) int { 5 | for i := len(slice) - 1; i >= 0; i-- { 6 | if slice[i] == item { 7 | return i 8 | } 9 | } 10 | return -1 11 | } 12 | -------------------------------------------------------------------------------- /container/ringbuffer/README.md: -------------------------------------------------------------------------------- 1 | # ringbuffer 2 | 环形缓冲区 3 | 4 | # fix 5 | 固定长度 6 | 7 | 单生产者和单消费者情况下是线程安全性的 8 | 9 | # round 10 | 固定长度,且长度向上取2的平方 11 | 12 | 单生产者和单消费者情况下是线程安全性的 13 | 14 | 基于Linux的kfifo思想快速取余,性能高 15 | 16 | # unbounded 17 | 动态扩展长度 18 | 19 | 非线程安全 -------------------------------------------------------------------------------- /slices/fill.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Fill items to slice 4 | func Fill[T any](slice []T, itemFunc func(item T, index int, slice []T) T) []T { 5 | for index := 0; index < len(slice); index++ { 6 | slice[index] = itemFunc(slice[index], index, slice) 7 | } 8 | return slice 9 | } 10 | -------------------------------------------------------------------------------- /validate/number.go: -------------------------------------------------------------------------------- 1 | package validate 2 | 3 | import "golang.org/x/exp/constraints" 4 | 5 | // 数值在范围内 6 | func Range[T constraints.Ordered](n, min, max T, errFunc func(min, max T) error) error { 7 | if n < min || n > max { 8 | return errFunc(min, max) 9 | } 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /slices/any.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Any item is meet the condition 4 | func Any[T any](slice []T, condition func(item T, index int, slice []T) bool) bool { 5 | for index, item := range slice { 6 | if condition(item, index, slice) { 7 | return true 8 | } 9 | } 10 | return false 11 | } 12 | -------------------------------------------------------------------------------- /slices/every.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Every item is meet the condition 4 | func Every[T any](slice []T, condition func(item T, index int, slice []T) bool) bool { 5 | for index, item := range slice { 6 | if !condition(item, index, slice) { 7 | return false 8 | } 9 | } 10 | return true 11 | } 12 | -------------------------------------------------------------------------------- /slices/index_of_all.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // IndexOfAll slice[i] == item 4 | func IndexOfAll[T comparable](slice []T, item T) []int { 5 | var indexes []int 6 | for i := 0; i < len(slice); i++ { 7 | if slice[i] == item { 8 | indexes = append(indexes, i) 9 | } 10 | } 11 | return indexes 12 | } 13 | -------------------------------------------------------------------------------- /slices/reduce.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Reduce slice to a value 4 | func Reduce[T any, R any](slice []T, reduce func(total R, item T, index int, slice []T) R, init R) R { 5 | for index := 0; index < len(slice); index++ { 6 | init = reduce(init, slice[index], index, slice) 7 | } 8 | return init 9 | } 10 | -------------------------------------------------------------------------------- /pool/README.md: -------------------------------------------------------------------------------- 1 | # pool 2 | 3 | 各种对象池 4 | 5 | # 泛型sync.Pool 6 | 对`sync.Pool`进行泛型包装,添加`ClearFunc()`用于清理回收的对象 7 | 8 | # FixPool 9 | 基于`channel+select`实现的固定长度缓冲区对象池 10 | 11 | # LevelPool 12 | 多个等级的pool,一般用于字节池但是对象大小跨度比较大的场景 13 | 14 | 比如指定5个level,分别为1KB、2KB、4KB、8KB、16KB和更大 15 | 16 | `Get(cap)`的时候指定长度,这样就会优先获取小的但是满足条件的对象,更加节约资源 -------------------------------------------------------------------------------- /slices/find_index.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // FindIndex first index that meet the condition 4 | func FindIndex[T any](slice []T, condition func(item T, index int, slice []T) bool) int { 5 | for index, item := range slice { 6 | if condition(item, index, slice) { 7 | return index 8 | } 9 | } 10 | return -1 11 | } 12 | -------------------------------------------------------------------------------- /slices/sort.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "sort" 4 | 5 | // Sort sorts the slice given the provided condition function. 6 | func Sort[T any](slice []T, condition func(item1, item2 T) bool) []T { 7 | sort.Slice(slice, func(i, j int) bool { 8 | return condition(slice[i], slice[j]) 9 | }) 10 | return slice 11 | } 12 | -------------------------------------------------------------------------------- /slices/find_last_index.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // FindLastIndex that meet the condition 4 | func FindLastIndex[T any](slice []T, condition func(item T, index int, slice []T) bool) int { 5 | for i := len(slice) - 1; i >= 0; i-- { 6 | if condition(slice[i], i, slice) { 7 | return i 8 | } 9 | } 10 | return -1 11 | } 12 | -------------------------------------------------------------------------------- /slices/map.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Map []T1 to []T2 by mapper 4 | func Map[T1 any, T2 any](slice []T1, mapper func(item T1, index int, slice []T1) T2) []T2 { 5 | mapped := make([]T2, 0, len(slice)) 6 | for index, item := range slice { 7 | mapped = append(mapped, mapper(item, index, slice)) 8 | } 9 | return mapped 10 | } 11 | -------------------------------------------------------------------------------- /slices/reduce_right.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // ReduceRight slice to a value 4 | func ReduceRight[T any, R any](slice []T, reduce func(total R, item T, index int, slice []T) R, init R) R { 5 | for index := len(slice) - 1; index >= 0; index-- { 6 | init = reduce(init, slice[index], index, slice) 7 | } 8 | return init 9 | } 10 | -------------------------------------------------------------------------------- /slices/find.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Find first item that meet the condition 4 | func Find[T any](slice []T, condition func(item T, index int, slice []T) bool) (T, bool) { 5 | for index, item := range slice { 6 | if condition(item, index, slice) { 7 | return item, true 8 | } 9 | } 10 | var t T 11 | return t, false 12 | } 13 | -------------------------------------------------------------------------------- /slices/frequencies.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Frequencies returns a map with the unique values of the collection as keys and their frequencies as the values. 4 | func Frequencies[T comparable](slice []T) map[T]int { 5 | frequencies := map[T]int{} 6 | for _, item := range slice { 7 | frequencies[item]++ 8 | } 9 | return frequencies 10 | } 11 | -------------------------------------------------------------------------------- /slices/shuffle.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | // Shuffle slice 9 | func Shuffle[T any](slice []T) { 10 | rand.Seed(time.Now().UnixNano()) 11 | for i := 0; i < len(slice); i++ { 12 | rand.Shuffle(len(slice), func(i, j int) { 13 | slice[i], slice[j] = slice[j], slice[i] 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /slices/distinct.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Distinct remove the same items 4 | func Distinct[T comparable](slice []T) []T { 5 | m := make(map[T]struct{}, len(slice)) 6 | var res []T 7 | for _, item := range slice { 8 | if _, ok := m[item]; !ok { 9 | res = append(res, item) 10 | m[item] = struct{}{} 11 | } 12 | } 13 | return res 14 | } 15 | -------------------------------------------------------------------------------- /slices/find_last.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // FindLast item that meet the condition 4 | func FindLast[T any](slice []T, condition func(item T, index int, slice []T) bool) (T, bool) { 5 | for i := len(slice) - 1; i >= 0; i-- { 6 | if condition(slice[i], i, slice) { 7 | return slice[i], true 8 | } 9 | } 10 | var t T 11 | return t, false 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | **/testdata/fuzz/*/* -------------------------------------------------------------------------------- /slices/to_slice.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // ToSlice iterates map's key and value to generate a new slice 4 | func ToSlice[K comparable, V any, T any](dict map[K]V, transform func(key K, value V) T) []T { 5 | slice := make([]T, 0, len(dict)) 6 | for key, value := range dict { 7 | slice = append(slice, transform(key, value)) 8 | } 9 | return slice 10 | } 11 | -------------------------------------------------------------------------------- /container/stack/stack_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestStack_Push(t *testing.T) { 8 | s := New[int]() 9 | s.Push(1) 10 | s.Push(2) 11 | s.Push(3) 12 | 13 | i := 3 14 | for !s.Empty() { 15 | last := s.Pop() 16 | if i != last { 17 | t.Errorf("Push() = %v, want %v", last, i) 18 | } 19 | i-- 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /slices/filter.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Filter items that are not meet the condition 4 | func Filter[T any](slice []T, condition func(item T, index int, slice []T) bool) []T { 5 | var filtered []T 6 | for index, item := range slice { 7 | if condition(item, index, slice) { 8 | filtered = append(filtered, item) 9 | } 10 | } 11 | return filtered 12 | } 13 | -------------------------------------------------------------------------------- /container/list/list_test.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import "testing" 4 | 5 | func TestList(t *testing.T) { 6 | l := New[int]() 7 | l.PushBack(1) 8 | l.PushBack(2) 9 | l.PushBack(3) 10 | l.PushBack(4) 11 | i := 1 12 | for !l.Empty() { 13 | first := l.Remove(l.Front()) 14 | if i != first { 15 | t.Errorf("AddLast() = %v, want %v", first, i) 16 | } 17 | i++ 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /container/deque/deque_test.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDequeAll(t *testing.T) { 8 | d := New[int]() 9 | d.PushBack(1) 10 | d.PushBack(2) 11 | d.PushBack(3) 12 | d.PushBack(4) 13 | i := 1 14 | for !d.Empty() { 15 | first := d.PopFront() 16 | if i != first { 17 | t.Errorf("AddLast() = %v, want %v", first, i) 18 | } 19 | i++ 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /slices/sort_stable.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "sort" 4 | 5 | // SortStable sorts the slice given the provided condition function, 6 | // keeping equal elements in their original order. 7 | func SortStable[T any](slice []T, condition func(item1, item2 T) bool) []T { 8 | sort.SliceStable(slice, func(i, j int) bool { 9 | return condition(slice[i], slice[j]) 10 | }) 11 | return slice 12 | } 13 | -------------------------------------------------------------------------------- /conv/string.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import ( 4 | "strconv" 5 | 6 | "golang.org/x/exp/constraints" 7 | ) 8 | 9 | // 数值转字符串 10 | func Itoa[T constraints.Integer](i T) string { 11 | return strconv.Itoa(int(i)) 12 | } 13 | 14 | // 字符串转数值 15 | func Atoi[T constraints.Integer](a string) (T, error) { 16 | i, err := strconv.Atoi(a) 17 | if err != nil { 18 | return 0, err 19 | } 20 | return T(i), nil 21 | } 22 | -------------------------------------------------------------------------------- /cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | // 缓存接口 4 | type Cache[K comparable, V any] interface { 5 | // 添加或更新元素 6 | Put(key K, value V) 7 | // 获取元素 8 | Get(key K) (V, bool) 9 | } 10 | 11 | // 淘汰时触发 12 | type OnEvict[K comparable, V any] func(entry *Entry[K, V]) 13 | 14 | // 缓存项 15 | type Entry[K comparable, V any] struct { 16 | Key K 17 | Value V 18 | } 19 | 20 | // 基础缓存结构 21 | type BaseCache struct { 22 | } 23 | -------------------------------------------------------------------------------- /container/ringbuffer/round/mod_test.go: -------------------------------------------------------------------------------- 1 | package round 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var ( 8 | Len = 8 9 | Mask = Len - 1 10 | In = 8 - 5 11 | ) 12 | 13 | // % len 14 | func BenchmarkModLen(b *testing.B) { 15 | for i := 0; i < b.N; i++ { 16 | _ = In % Len 17 | } 18 | } 19 | 20 | // & Mask 21 | func BenchmarkAndMask(b *testing.B) { 22 | for i := 0; i < b.N; i++ { 23 | _ = In & Mask 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /slices/to_map.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // ToMap iterates the slice to map following the keyFunc and valueFunc 4 | func ToMap[T any, K comparable, V any](slice []T, keyFunc func(item T, index int, slice []T) K, 5 | valueFunc func(item T, index int, slice []T) V) map[K]V { 6 | dict := make(map[K]V, len(slice)) 7 | for index, value := range slice { 8 | dict[keyFunc(value, index, slice)] = valueFunc(value, index, slice) 9 | } 10 | return dict 11 | } 12 | -------------------------------------------------------------------------------- /slices/shuffle_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestShuffle(t *testing.T) { 8 | type args struct { 9 | slice []int 10 | } 11 | tests := []struct { 12 | name string 13 | args args 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice: []int{1, 2, 3, 4, 5, 6}, 19 | }, 20 | }, 21 | } 22 | for _, tt := range tests { 23 | t.Run(tt.name, func(t *testing.T) { 24 | Shuffle(tt.args.slice) 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /container/set/set_test.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSet(t *testing.T) { 8 | s := New[int]() 9 | s.Add(1) 10 | s.Add(2) 11 | s.Add(3) 12 | 13 | if !s.Contains(2) { 14 | t.Errorf("expected contains: %d, but not contains", 2) 15 | } 16 | 17 | if s.Contains(4) { 18 | t.Errorf("expected not contains: %d, but contains", 4) 19 | } 20 | 21 | s.Remove(2) 22 | 23 | if s.Contains(2) { 24 | t.Errorf("expected not contains: %d, but contains", 2) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /container/ringbuffer/round/uint_overflow_test.go: -------------------------------------------------------------------------------- 1 | package round 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | ) 7 | 8 | // uint overflow 9 | func TestUintOverflow(t *testing.T) { 10 | var in uint = math.MaxUint64 11 | var out uint = math.MaxUint64 - 1 12 | if in-out != 1 { 13 | t.Errorf("want %d, but %d", 1, in-out) 14 | } 15 | in++ 16 | if in-out != 2 { 17 | t.Errorf("want %d, but %d", 2, in-out) 18 | } 19 | out++ 20 | if in-out != 1 { 21 | t.Errorf("want %d, but %d", 1, in-out) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /hash/hash.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "hash/maphash" 5 | ) 6 | 7 | // 哈希函数 8 | // 非线程安全,业务请加锁 9 | // 也就是对maphash的包装 10 | type Hash struct { 11 | h *maphash.Hash 12 | } 13 | 14 | func New() *Hash { 15 | h := &Hash{ 16 | h: &maphash.Hash{}, 17 | } 18 | h.h.SetSeed(maphash.MakeSeed()) 19 | return h 20 | } 21 | 22 | // 计算哈希值 23 | func (h *Hash) Sum64(b []byte) uint64 { 24 | h.h.Reset() 25 | h.h.Write(b) 26 | return h.h.Sum64() 27 | } 28 | 29 | // 计算哈希值 30 | func (h *Hash) Sum64String(s string) uint64 { 31 | return h.Sum64([]byte(s)) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/cmd.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "os/exec" 7 | ) 8 | 9 | // 在Shell执行命令 10 | func ShellExec(cmd string) (*bytes.Buffer, error) { 11 | command := exec.Command("sh") 12 | in := bytes.NewBuffer(nil) 13 | out := bytes.NewBuffer(nil) 14 | errOut := bytes.NewBuffer(nil) 15 | command.Stdin = in 16 | command.Stdout = out 17 | command.Stderr = errOut 18 | in.WriteString(cmd) 19 | in.WriteString("\n") 20 | in.WriteString("exit\n") 21 | if err := command.Run(); err != nil { 22 | return nil, errors.New(errOut.String()) 23 | } 24 | return out, nil 25 | } 26 | -------------------------------------------------------------------------------- /container/heap/heapn_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestHeapNAll(t *testing.T) { 8 | h := NewN(4, func(e1 int, e2 int) bool { 9 | return e1 > e2 10 | }) 11 | h.Push(5) 12 | h.Push(6) 13 | h.Push(3) 14 | h.Push(7) 15 | h.Push(2) 16 | h.Push(4) 17 | h.Push(8) 18 | h.Push(9) 19 | h.Push(1) 20 | 21 | v := h.Peek() 22 | if v != 9 { 23 | t.Errorf("Peek() = %v, want %v", v, 9) 24 | } 25 | 26 | i := 9 27 | for !h.Empty() { 28 | v := h.Pop() 29 | if i != v { 30 | t.Errorf("Pop() = %v, want %v", v, i) 31 | } 32 | i-- 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /conv/byte.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | // uint64转bytes 8 | func BigEndianUint64ToBytes(n uint64) []byte { 9 | bytes := make([]byte, 8) 10 | binary.BigEndian.PutUint64(bytes, n) 11 | return bytes 12 | } 13 | 14 | // uint32转bytes 15 | func BigEndianUint32ToBytes(n uint32) []byte { 16 | bytes := make([]byte, 4) 17 | binary.BigEndian.PutUint32(bytes, n) 18 | return bytes 19 | } 20 | 21 | // uint16转bytes 22 | func BigEndianUint16ToBytes(n uint16) []byte { 23 | bytes := make([]byte, 2) 24 | binary.BigEndian.PutUint16(bytes, n) 25 | return bytes 26 | } 27 | -------------------------------------------------------------------------------- /pool/byte_pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | type BytePool struct { 4 | p *Pool[[]byte] 5 | } 6 | 7 | func NewBytePool(size, cap int) *BytePool { 8 | if size > cap { 9 | panic("size must be less then cap") 10 | } 11 | newFunc := func() []byte { 12 | return make([]byte, size, cap) 13 | } 14 | clearFunc := func(b []byte) []byte { 15 | return b[:0] 16 | } 17 | return &BytePool{ 18 | p: New(newFunc, clearFunc), 19 | } 20 | } 21 | 22 | // 获取字节数组 23 | func (p *BytePool) Get() []byte { 24 | return p.p.Get() 25 | } 26 | 27 | // 归还字节数组 28 | func (p *BytePool) Put(b []byte) { 29 | p.p.Put(b) 30 | } 31 | -------------------------------------------------------------------------------- /container/pqueue/pqueue_test.go: -------------------------------------------------------------------------------- 1 | package pqueue 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestPriorityQueueAll(t *testing.T) { 8 | q := New(nil, func(e1 int, e2 int) bool { 9 | return e1 > e2 10 | }) 11 | q.Push(5) 12 | q.Push(6) 13 | q.Push(3) 14 | q.Push(7) 15 | q.Push(2) 16 | q.Push(4) 17 | q.Push(8) 18 | q.Push(9) 19 | q.Push(1) 20 | 21 | v := q.Peek() 22 | if v != 9 { 23 | t.Errorf("Peek() = %v, want %v", v, 9) 24 | } 25 | 26 | i := 9 27 | for !q.Empty() { 28 | v := q.Pop() 29 | if i != v { 30 | t.Errorf("Pop() = %v, want %v", v, i) 31 | } 32 | i-- 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /conv/json.go: -------------------------------------------------------------------------------- 1 | package conv 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | // 必须序列化 8 | func MustMarshal(v any) []byte { 9 | bytes, err := json.Marshal(v) 10 | if err != nil { 11 | panic(err) 12 | } 13 | return bytes 14 | } 15 | 16 | // 必须反序列化 17 | func MustUnmarshal[T any](b []byte) T { 18 | var t T 19 | if err := json.Unmarshal(b, &t); err != nil { 20 | panic(err) 21 | } 22 | return t 23 | } 24 | 25 | // 必须序列化 26 | func MustMarshalToString(v any) string { 27 | return string(MustMarshal(v)) 28 | } 29 | 30 | // 必须反序列化 31 | func MustUnmarshalString[T any](b string) T { 32 | return MustUnmarshal[T]([]byte(b)) 33 | } 34 | -------------------------------------------------------------------------------- /slices/equal.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | // Equal slice1 equal slice2 4 | func Equal[T comparable](slice1, slice2 []T) bool { 5 | if len(slice1) != len(slice2) { 6 | return false 7 | } 8 | for index, item := range slice1 { 9 | if item != slice2[index] { 10 | return false 11 | } 12 | } 13 | return true 14 | } 15 | 16 | // EqualFunc slice1 equal slice2 with func 17 | func EqualFunc[T any](slice1, slice2 []T, f func(item1, item2 T) bool) bool { 18 | if len(slice1) != len(slice2) { 19 | return false 20 | } 21 | for index, item := range slice1 { 22 | if !f(item, slice2[index]) { 23 | return false 24 | } 25 | } 26 | return true 27 | } 28 | -------------------------------------------------------------------------------- /container/set/set.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | type Set[T comparable] struct { 4 | m map[T]struct{} 5 | } 6 | 7 | func New[T comparable]() *Set[T] { 8 | return &Set[T]{m: map[T]struct{}{}} 9 | } 10 | 11 | // 加入集合 12 | func (s *Set[T]) Add(elem T) { 13 | s.m[elem] = struct{}{} 14 | } 15 | 16 | // 移出集合 17 | func (s *Set[T]) Remove(elem T) { 18 | delete(s.m, elem) 19 | } 20 | 21 | // 是否包含元素 22 | func (s *Set[T]) Contains(elem T) bool { 23 | _, contains := s.m[elem] 24 | return contains 25 | } 26 | 27 | // 集合长度 28 | func (s *Set[T]) Len() int { 29 | return len(s.m) 30 | } 31 | 32 | // 集合是否为空 33 | func (s *Set[T]) Empty() bool { 34 | return s.Len() == 0 35 | } 36 | -------------------------------------------------------------------------------- /pool/fix/byte_pool.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | type BytePool struct { 4 | p *Pool[[]byte] 5 | } 6 | 7 | // cacheSize: 字节池缓存长度 8 | // size: 字节数组长度 9 | // cap: 字节数组容量 10 | func NewBytePool(cacheSize, size, cap int) *BytePool { 11 | if size > cap { 12 | panic("size must be less then cap") 13 | } 14 | newFunc := func() []byte { 15 | return make([]byte, size, cap) 16 | } 17 | clearFunc := func(b []byte) []byte { 18 | return b[:0] 19 | } 20 | return &BytePool{ 21 | p: NewPool(cacheSize, newFunc, clearFunc), 22 | } 23 | } 24 | 25 | func (p *BytePool) Get() []byte { 26 | return p.p.Get() 27 | } 28 | 29 | func (p *BytePool) Put(b []byte) { 30 | p.p.Put(b) 31 | } 32 | -------------------------------------------------------------------------------- /slices/reverse_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestReverse(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "number1", 19 | args: args{ 20 | slice: []int{1, 2, 3, 4, 5}, 21 | }, 22 | want: []int{5, 4, 3, 2, 1}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := Reverse(tt.args.slice); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("Reverse() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /container/stack/stack.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import "github.com/jiaxwu/gommon/container/list" 4 | 5 | type Stack[T any] struct { 6 | l *list.List[T] 7 | } 8 | 9 | func New[T any]() *Stack[T] { 10 | return &Stack[T]{l: list.New[T]()} 11 | } 12 | 13 | // 入栈 14 | func (s *Stack[T]) Push(elem T) { 15 | s.l.PushBack(elem) 16 | } 17 | 18 | // 出栈 19 | func (s *Stack[T]) Pop() T { 20 | return s.l.RemoveBack() 21 | } 22 | 23 | // 栈顶元素 24 | func (s *Stack[T]) Peek() T { 25 | return s.l.Back().Value 26 | } 27 | 28 | // 栈元素个数 29 | func (s *Stack[T]) Len() int { 30 | return s.l.Len() 31 | } 32 | 33 | // 栈是否为空 34 | func (s *Stack[T]) Empty() bool { 35 | return s.l.Empty() 36 | } 37 | -------------------------------------------------------------------------------- /slices/distinct_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestDistinct(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | }{ 17 | { 18 | name: "number1", 19 | args: args{ 20 | slice: []int{1, 3, 2, 1, 2, 3, 2, 1, 4}, 21 | }, 22 | want: []int{1, 3, 2, 4}, 23 | }, 24 | } 25 | for _, tt := range tests { 26 | t.Run(tt.name, func(t *testing.T) { 27 | if got := Distinct(tt.args.slice); !reflect.DeepEqual(got, tt.want) { 28 | t.Errorf("Distinct() = %v, want %v", got, tt.want) 29 | } 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /slices/for_each_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestForEach(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | action func(item int, index int, slice []int) 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | }{ 17 | { 18 | name: "number1", 19 | args: args{ 20 | slice: []int{4, 5, 6}, 21 | action: func(item int, index int, slice []int) { 22 | fmt.Printf("item: %d, index: %d\n", item, index) 23 | }, 24 | }, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | ForEach(tt.args.slice, tt.args.action) 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pool/fix/pool_test.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import "testing" 4 | 5 | const ( 6 | blocks = 64 7 | blockSize = 1024 8 | ) 9 | 10 | var block = make([]byte, blockSize) 11 | 12 | func BenchmarkBytePool(b *testing.B) { 13 | pool := NewBytePool(16, 0, blocks*blockSize) 14 | for n := 0; n < b.N; n++ { 15 | b := pool.Get() 16 | for i := 0; i < blocks; i++ { 17 | b = append(b, block...) 18 | } 19 | pool.Put(b) 20 | } 21 | } 22 | 23 | func BenchmarkBufferPool(b *testing.B) { 24 | pool := NewBufferPool(16, 0, blocks*blockSize) 25 | for n := 0; n < b.N; n++ { 26 | b := pool.Get() 27 | for i := 0; i < blocks; i++ { 28 | b.Write(block) 29 | } 30 | pool.Put(b) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /slices/unshift_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestUnshift(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | items []int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want []int 17 | }{ 18 | { 19 | name: "number", 20 | args: args{ 21 | slice: []int{3, 4, 5}, 22 | items: []int{1, 2}, 23 | }, 24 | want: []int{1, 2, 3, 4, 5}, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | if got := Unshift(tt.args.slice, tt.args.items...); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("Unshift() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /pool/buffer_pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type BufferPool struct { 8 | p *Pool[*bytes.Buffer] 9 | } 10 | 11 | func NewBufferPool(size, cap int) *BufferPool { 12 | if size > cap { 13 | panic("size must be less then cap") 14 | } 15 | newFunc := func() *bytes.Buffer { 16 | var b []byte 17 | if cap > 0 { 18 | b = make([]byte, size, cap) 19 | } 20 | return bytes.NewBuffer(b) 21 | } 22 | clearFunc := func(b *bytes.Buffer) *bytes.Buffer { 23 | b.Reset() 24 | return b 25 | } 26 | return &BufferPool{ 27 | p: New(newFunc, clearFunc), 28 | } 29 | } 30 | 31 | func (p *BufferPool) Get() *bytes.Buffer { 32 | return p.p.Get() 33 | } 34 | 35 | func (p *BufferPool) Put(b *bytes.Buffer) { 36 | p.p.Put(b) 37 | } 38 | -------------------------------------------------------------------------------- /pool/pool.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import "sync" 4 | 5 | // 创建新对象 6 | type NewFunc[T any] func() T 7 | 8 | // 清理对象 9 | type ClearFunc[T any] func(T) T 10 | 11 | type Pool[T any] struct { 12 | p sync.Pool 13 | clearFunc ClearFunc[T] 14 | } 15 | 16 | func New[T any](newFunc NewFunc[T], clearFunc ClearFunc[T]) *Pool[T] { 17 | if newFunc == nil { 18 | panic("must be provide NewFunc") 19 | } 20 | p := &Pool[T]{ 21 | clearFunc: clearFunc, 22 | } 23 | p.p.New = func() any { 24 | return newFunc() 25 | } 26 | return p 27 | } 28 | 29 | // 获取对象 30 | func (p *Pool[T]) Get() T { 31 | return p.p.Get().(T) 32 | } 33 | 34 | // 归还对象 35 | func (p *Pool[T]) Put(t T) { 36 | if p.clearFunc != nil { 37 | t = p.clearFunc(t) 38 | } 39 | p.p.Put(t) 40 | } 41 | -------------------------------------------------------------------------------- /slices/frequencies_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFrequencies(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want map[int]int 16 | }{ 17 | { 18 | name: "number1", 19 | args: args{ 20 | slice: []int{1, 2, 3, 4, 3, 2, 1, 5}, 21 | }, 22 | want: map[int]int{ 23 | 1: 2, 24 | 2: 2, 25 | 3: 2, 26 | 4: 1, 27 | 5: 1, 28 | }, 29 | }, 30 | } 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | if got := Frequencies(tt.args.slice); !reflect.DeepEqual(got, tt.want) { 34 | t.Errorf("Frequencies() = %v, want %v", got, tt.want) 35 | } 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pool/fix/buffer_pool.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type BufferPool struct { 8 | p *Pool[*bytes.Buffer] 9 | } 10 | 11 | func NewBufferPool(cacheSize, size, cap int) *BufferPool { 12 | if size > cap { 13 | panic("size must be less then cap") 14 | } 15 | newFunc := func() *bytes.Buffer { 16 | var b []byte 17 | if cap > 0 { 18 | b = make([]byte, size, cap) 19 | } 20 | return bytes.NewBuffer(b) 21 | } 22 | clearFunc := func(b *bytes.Buffer) *bytes.Buffer { 23 | b.Reset() 24 | return b 25 | } 26 | return &BufferPool{ 27 | p: NewPool(cacheSize, newFunc, clearFunc), 28 | } 29 | } 30 | 31 | func (p *BufferPool) Get() *bytes.Buffer { 32 | return p.p.Get() 33 | } 34 | 35 | func (p *BufferPool) Put(b *bytes.Buffer) { 36 | p.p.Put(b) 37 | } 38 | -------------------------------------------------------------------------------- /container/pqueue/pqueue.go: -------------------------------------------------------------------------------- 1 | package pqueue 2 | 3 | import "github.com/jiaxwu/gommon/container/heap" 4 | 5 | // 优先队列 6 | type PriorityQueue[T any] struct { 7 | h *heap.Heap[T] 8 | } 9 | 10 | func New[T any](h []T, less func(e1 T, e2 T) bool) *PriorityQueue[T] { 11 | return &PriorityQueue[T]{ 12 | h: heap.New(h, less), 13 | } 14 | } 15 | 16 | // 入队 17 | func (p *PriorityQueue[T]) Push(elem T) { 18 | p.h.Push(elem) 19 | } 20 | 21 | // 出队 22 | func (p *PriorityQueue[T]) Pop() T { 23 | return p.h.Pop() 24 | } 25 | 26 | // 队头元素 27 | func (p *PriorityQueue[T]) Peek() T { 28 | return p.h.Peek() 29 | } 30 | 31 | // 队列元素个数 32 | func (p *PriorityQueue[T]) Len() int { 33 | return p.h.Len() 34 | } 35 | 36 | // 队列是否为空 37 | func (p *PriorityQueue[T]) Empty() bool { 38 | return p.Len() == 0 39 | } 40 | -------------------------------------------------------------------------------- /slices/index_of_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestIndexOf(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | item int 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want int 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice: []int{3, 4, 5, 3, 2}, 19 | item: 4, 20 | }, 21 | want: 1, 22 | }, 23 | { 24 | name: "number2", 25 | args: args{ 26 | slice: []int{3, 4, 5, 3, 2}, 27 | item: 3, 28 | }, 29 | want: 0, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := IndexOf(tt.args.slice, tt.args.item); got != tt.want { 35 | t.Errorf("IndexOf() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /container/deque/deque.go: -------------------------------------------------------------------------------- 1 | package deque 2 | 3 | import "github.com/jiaxwu/gommon/container/list" 4 | 5 | type Deque[T any] struct { 6 | l *list.List[T] 7 | } 8 | 9 | func New[T any]() *Deque[T] { 10 | return &Deque[T]{l: list.New[T]()} 11 | } 12 | 13 | // 从队头入队 14 | func (d *Deque[T]) PushFront(elem T) { 15 | d.l.PushFront(elem) 16 | } 17 | 18 | // 从队尾入队 19 | func (d *Deque[T]) PushBack(elem T) { 20 | d.l.PushBack(elem) 21 | } 22 | 23 | // 从队头出队 24 | func (d *Deque[T]) PopFront() T { 25 | return d.l.RemoveFront() 26 | } 27 | 28 | // 从队尾出队 29 | func (d *Deque[T]) PopBack() T { 30 | return d.l.RemoveBack() 31 | } 32 | 33 | // Len 队列元素个数 34 | func (d *Deque[T]) Len() int { 35 | return d.l.Len() 36 | } 37 | 38 | // Empty 队列是否为空 39 | func (d *Deque[T]) Empty() bool { 40 | return d.l.Empty() 41 | } 42 | -------------------------------------------------------------------------------- /crypto/padding.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import ( 4 | "math" 5 | ) 6 | 7 | // 填充 8 | func PKCS5Padding(src []byte, blockSize int) []byte { 9 | if blockSize > math.MaxUint8 { 10 | panic("too large block size") 11 | } 12 | srcLen := len(src) 13 | paddingLen := blockSize - srcLen%blockSize 14 | dst := make([]byte, srcLen+paddingLen) 15 | copy(dst, src) 16 | for i := len(src); i < len(dst); i++ { 17 | dst[i] = byte(paddingLen) 18 | } 19 | return dst 20 | } 21 | 22 | // 移除填充 23 | func PKCS5Trimming(src []byte) []byte { 24 | paddingLen := src[len(src)-1] 25 | return src[:len(src)-int(paddingLen)] 26 | } 27 | 28 | // 填充目标长度 29 | func PKCS5DstLen(srcLen, blockSize int) int { 30 | paddingLen := blockSize - srcLen%blockSize 31 | return srcLen + paddingLen 32 | } 33 | -------------------------------------------------------------------------------- /slices/includes_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestIncludes(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | item int 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want bool 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice: []int{2, 1, 3, 4, 5, 6}, 19 | item: 4, 20 | }, 21 | want: true, 22 | }, 23 | { 24 | name: "number2", 25 | args: args{ 26 | slice: []int{2, 1, 3, 4, 5, 6}, 27 | item: 7, 28 | }, 29 | want: false, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := Includes(tt.args.slice, tt.args.item); got != tt.want { 35 | t.Errorf("Includes() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /slices/last_index_of_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestLastIndexOf(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | item int 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want int 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice: []int{3, 4, 5, 3, 2}, 19 | item: 3, 20 | }, 21 | want: 3, 22 | }, 23 | { 24 | name: "number4", 25 | args: args{ 26 | slice: []int{3, 4, 5, 3, 2}, 27 | item: 4, 28 | }, 29 | want: 1, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := LastIndexOf(tt.args.slice, tt.args.item); got != tt.want { 35 | t.Errorf("LastIndexOf() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gommon 2 | 一些平时项目中使用到的库。 3 | 4 | 欢迎issues,pr! 5 | 6 | 注:仓库代码测试用例比较简单,如需生产环境使用请充分测试。 7 | 8 | # cache 9 | 泛型LRU、LFU、FIFO、ARC、Random、NearlyLRU算法 10 | 11 | # cmd 12 | 命令执行 13 | 14 | # consistenthash 15 | 一致性哈希,参考groupcache的实现,进行了一点点修改 16 | 17 | # container 18 | 泛型容器 19 | 20 | # conv 21 | 类型转换 22 | 23 | # counter 24 | 计数器 25 | 26 | # crypto 27 | 加密算法 28 | 29 | # env 30 | 获取环境变量的工具 31 | 32 | # filter 33 | 过滤器,比如布隆过滤器 34 | 35 | # hash 36 | 泛型哈希函数 37 | 38 | # limiter 39 | 限流器 40 | 41 | # math 42 | 一些数值工具 43 | 44 | # mem 45 | 快速的泛型memset()操作 46 | 47 | # pool 48 | 对`sync.Pool`的泛型改造,`channel+select`实现的固定长度pool,分级对象池,以及`[]byte`和`bytes.Buffer`的字节对象池 49 | 50 | # slices 51 | 泛型slice工具 52 | 53 | # timer 54 | 定时器,比如泛型延迟队列、时间轮 55 | 56 | # validate 57 | 基于函数的参数校验,包含数值、字符串和slice类型校验函数 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /slices/shift_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestShift(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []int 16 | want1 int 17 | }{ 18 | { 19 | name: "number1", 20 | args: args{ 21 | slice: []int{3, 4, 5, 6}, 22 | }, 23 | want: []int{4, 5, 6}, 24 | want1: 3, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | got, got1 := Shift(tt.args.slice) 30 | if !reflect.DeepEqual(got, tt.want) { 31 | t.Errorf("Shift() got = %v, want %v", got, tt.want) 32 | } 33 | if !reflect.DeepEqual(got1, tt.want1) { 34 | t.Errorf("Shift() got1 = %v, want %v", got1, tt.want1) 35 | } 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /slices/sort_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestSort(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | condition func(item1, item2 int) bool 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want []int 17 | }{ 18 | { 19 | name: "number1", 20 | args: args{ 21 | slice: []int{3, 4, 2, 1, 5, 7, 6, 8}, 22 | condition: func(item1, item2 int) bool { 23 | return item1 < item2 24 | }, 25 | }, 26 | want: []int{1, 2, 3, 4, 5, 6, 7, 8}, 27 | }, 28 | } 29 | for _, tt := range tests { 30 | t.Run(tt.name, func(t *testing.T) { 31 | if got := Sort(tt.args.slice, tt.args.condition); !reflect.DeepEqual(got, tt.want) { 32 | t.Errorf("Sort() = %v, want %v", got, tt.want) 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /slices/filter_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFilter(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | filter func(item int, index int, slice []int) bool 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want []int 17 | }{ 18 | { 19 | name: "number1", 20 | args: args{ 21 | slice: []int{18, 19, 6, 3, 43, 1, 32}, 22 | filter: func(item int, index int, _ []int) bool { 23 | return item > 18 && index > 2 24 | }, 25 | }, 26 | want: []int{43, 32}, 27 | }, 28 | } 29 | for _, tt := range tests { 30 | t.Run(tt.name, func(t *testing.T) { 31 | if got := Filter(tt.args.slice, tt.args.filter); !reflect.DeepEqual(got, tt.want) { 32 | t.Errorf("Filter() = %v, want %v", got, tt.want) 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /slices/fill_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFill(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | itemFunc func(item int, index int, slice []int) int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want []int 17 | }{ 18 | { 19 | name: "number1", 20 | args: args{ 21 | slice: make([]int, 10), 22 | itemFunc: func(item int, index int, slice []int) int { 23 | return index * index 24 | }, 25 | }, 26 | want: []int{0, 1, 4, 9, 16, 25, 36, 49, 64, 81}, 27 | }, 28 | } 29 | for _, tt := range tests { 30 | t.Run(tt.name, func(t *testing.T) { 31 | if got := Fill(tt.args.slice, tt.args.itemFunc); !reflect.DeepEqual(got, tt.want) { 32 | t.Errorf("Fill() = %v, want %v", got, tt.want) 33 | } 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /slices/index_of_all_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestIndexOfAll(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | item int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want []int 17 | }{ 18 | { 19 | name: "number1", 20 | args: args{ 21 | slice: []int{3, 4, 5, 3, 2}, 22 | item: 4, 23 | }, 24 | want: []int{1}, 25 | }, 26 | { 27 | name: "number2", 28 | args: args{ 29 | slice: []int{3, 4, 5, 3, 2}, 30 | item: 3, 31 | }, 32 | want: []int{0, 3}, 33 | }, 34 | } 35 | for _, tt := range tests { 36 | t.Run(tt.name, func(t *testing.T) { 37 | if got := IndexOfAll(tt.args.slice, tt.args.item); !reflect.DeepEqual(got, tt.want) { 38 | t.Errorf("IndexOf() = %v, want %v", got, tt.want) 39 | } 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /limiter/sliding_log_limiter_test.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewSlidingLogLimiter(t *testing.T) { 9 | type args struct { 10 | smallWindow time.Duration 11 | strategies []*SlidingLogLimiterStrategy 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want *SlidingLogLimiter 17 | wantErr bool 18 | }{ 19 | { 20 | name: "60_5seconds", 21 | args: args{ 22 | smallWindow: time.Second, 23 | strategies: []*SlidingLogLimiterStrategy{ 24 | NewSlidingLogLimiterStrategy(10, time.Minute), 25 | NewSlidingLogLimiterStrategy(100, time.Hour), 26 | }, 27 | }, 28 | want: nil, 29 | }, 30 | } 31 | for _, tt := range tests { 32 | t.Run(tt.name, func(t *testing.T) { 33 | NewSlidingLogLimiter(tt.args.smallWindow, tt.args.strategies...) 34 | }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /slices/map_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestMap(t *testing.T) { 10 | type args struct { 11 | slice []int 12 | mapper func(item int, index int, slice []int) string 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want []string 18 | }{ 19 | { 20 | name: "intToString", 21 | args: args{ 22 | slice: []int{2, 3, 4, 13}, 23 | mapper: func(item int, index int, slice []int) string { 24 | return fmt.Sprintf("%d:%d", item, index) 25 | }, 26 | }, 27 | want: []string{"2:0", "3:1", "4:2", "13:3"}, 28 | }, 29 | } 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | if got := Map(tt.args.slice, tt.args.mapper); !reflect.DeepEqual(got, tt.want) { 33 | t.Errorf("Map() = %v, want %v", got, tt.want) 34 | } 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /container/heap/heap_test.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestHeapAll(t *testing.T) { 8 | h := New(nil, func(e1 int, e2 int) bool { 9 | return e1 > e2 10 | }) 11 | h.Push(5) 12 | h.Push(6) 13 | h.Push(3) 14 | h.Push(7) 15 | h.Push(2) 16 | h.Push(4) 17 | h.Push(8) 18 | h.Push(9) 19 | h.Push(1) 20 | 21 | v := h.Peek() 22 | if v != 9 { 23 | t.Errorf("Peek() = %v, want %v", v, 9) 24 | } 25 | 26 | i := 9 27 | for !h.Empty() { 28 | v := h.Pop() 29 | if i != v { 30 | t.Errorf("Pop() = %v, want %v", v, i) 31 | } 32 | i-- 33 | } 34 | } 35 | 36 | func TestHeapNew(t *testing.T) { 37 | h := New([]int{5, 6, 3, 7, 2, 4, 8, 9, 1}, func(e1 int, e2 int) bool { 38 | return e1 > e2 39 | }) 40 | 41 | i := 9 42 | for !h.Empty() { 43 | v := h.Pop() 44 | if i != v { 45 | t.Errorf("Pop() = %v, want %v", v, i) 46 | } 47 | i-- 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mem/mem_test.go: -------------------------------------------------------------------------------- 1 | package mem 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestMemset(t *testing.T) { 8 | n := 10 9 | arr := make([]int, n) 10 | for i := 0; i < n; i++ { 11 | arr[i] = i 12 | } 13 | 14 | setVals := []int{0, 2, 5, 10} 15 | 16 | for _, setVal := range setVals { 17 | Memset(arr, setVal) 18 | for i := 0; i < n; i++ { 19 | if arr[i] != setVal { 20 | t.Errorf("want %v, but %v", setVal, arr[i]) 21 | } 22 | } 23 | } 24 | } 25 | 26 | // 循环设置 27 | func Loopset[T any](arr []T, val T) { 28 | for i := 0; i < len(arr); i++ { 29 | arr[i] = val 30 | } 31 | } 32 | 33 | var a = make([]int, 1000) 34 | 35 | func BenchmarkLoopset(b *testing.B) { 36 | for i := 0; i < b.N; i++ { 37 | Loopset(a, 10) 38 | } 39 | } 40 | 41 | func BenchmarkMemset(b *testing.B) { 42 | for i := 0; i < b.N; i++ { 43 | Memset(a, 10) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /slices/to_slice_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | "testing" 7 | ) 8 | 9 | func TestToSlice(t *testing.T) { 10 | type args struct { 11 | dict map[string]int 12 | transform func(string, int) int 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want []int 18 | }{ 19 | { 20 | name: "number1", 21 | args: args{ 22 | dict: map[string]int{"1": 1, "2": 2, "3": 3, "4": 4}, 23 | transform: func(key string, value int) int { 24 | return value 25 | }, 26 | }, 27 | want: []int{ 28 | 1, 2, 3, 4, 29 | }, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | got := ToSlice(tt.args.dict, tt.args.transform) 35 | sort.Ints(got) 36 | sort.Ints(tt.want) 37 | if !reflect.DeepEqual(got, tt.want) { 38 | t.Errorf("ToSlice() = %v, want %v", got, tt.want) 39 | } 40 | }) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pool/fix/pool.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import "github.com/jiaxwu/gommon/pool" 4 | 5 | type Pool[T any] struct { 6 | cache chan T 7 | newFunc pool.NewFunc[T] 8 | clearFunc pool.ClearFunc[T] 9 | } 10 | 11 | // cacheSize: 对象池缓存长度 12 | func NewPool[T any](cacheSize int, newFunc pool.NewFunc[T], clearFunc pool.ClearFunc[T]) *Pool[T] { 13 | if newFunc == nil { 14 | panic("must be provide NewFunc") 15 | } 16 | if cacheSize < 1 { 17 | panic("cacheSize cannot less then 1") 18 | } 19 | return &Pool[T]{ 20 | cache: make(chan T, cacheSize), 21 | newFunc: newFunc, 22 | clearFunc: clearFunc, 23 | } 24 | } 25 | 26 | func (p *Pool[T]) Get() T { 27 | select { 28 | // 从channel读 29 | case t := <-p.cache: 30 | return t 31 | // 如果channel空则申请一个新的对象 32 | default: 33 | return p.newFunc() 34 | } 35 | } 36 | 37 | func (p *Pool[T]) Put(t T) { 38 | t = p.clearFunc(t) 39 | select { 40 | // 放入channel 41 | case p.cache <- t: 42 | // channel满了则丢弃 43 | default: 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /slices/every_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestEvery(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | condition func(item int, index int, slice []int) bool 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want bool 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice: []int{2, 2, 2, 2}, 19 | condition: func(item int, _ int, _ []int) bool { 20 | return item == 2 21 | }, 22 | }, 23 | want: true, 24 | }, 25 | { 26 | name: "number2", 27 | args: args{ 28 | slice: []int{2, 3, 4, 13}, 29 | condition: func(item int, _ int, _ []int) bool { 30 | return item == 2 31 | }, 32 | }, 33 | want: false, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := Every(tt.args.slice, tt.args.condition); got != tt.want { 39 | t.Errorf("Every() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /slices/any_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestAny(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | condition func(item int, index int, slice []int) bool 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want bool 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice: []int{2, 3, 4, 13}, 19 | condition: func(item int, index int, slice []int) bool { 20 | return item == 2 21 | }, 22 | }, 23 | want: true, 24 | }, 25 | { 26 | name: "number2", 27 | args: args{ 28 | slice: []int{2, 3, 4, 13}, 29 | condition: func(item int, index int, slice []int) bool { 30 | return item == 5 31 | }, 32 | }, 33 | want: false, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := Any(tt.args.slice, tt.args.condition); got != tt.want { 39 | t.Errorf("Any() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /limiter/fixed_window_limiter.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | // FixedWindowLimiter 固定窗口限流器 9 | type FixedWindowLimiter struct { 10 | limit int // 窗口请求上限 11 | window time.Duration // 窗口时间大小 12 | counter int // 计数器 13 | lastTime time.Time // 上一次请求的时间 14 | mutex sync.Mutex // 避免并发问题 15 | } 16 | 17 | func NewFixedWindowLimiter(limit int, window time.Duration) *FixedWindowLimiter { 18 | return &FixedWindowLimiter{ 19 | limit: limit, 20 | window: window, 21 | lastTime: time.Now(), 22 | } 23 | } 24 | 25 | func (l *FixedWindowLimiter) TryAcquire() bool { 26 | l.mutex.Lock() 27 | defer l.mutex.Unlock() 28 | // 获取当前时间 29 | now := time.Now() 30 | // 如果当前窗口失效,计数器清0,开启新的窗口 31 | if now.Sub(l.lastTime) > l.window { 32 | l.counter = 0 33 | l.lastTime = now 34 | } 35 | // 若到达窗口请求上限,请求失败 36 | if l.counter >= l.limit { 37 | return false 38 | } 39 | // 若没到窗口请求上限,计数器+1,请求成功 40 | l.counter++ 41 | return true 42 | } 43 | -------------------------------------------------------------------------------- /slices/find_index_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestFindIndex(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | condition func(item int, index int, slice []int) bool 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want int 14 | }{ 15 | { 16 | name: "findNumber1", 17 | args: args{ 18 | slice: []int{2, 3, 4, 13}, 19 | condition: func(item int, _ int, _ []int) bool { 20 | return item == 14 21 | }, 22 | }, 23 | want: -1, 24 | }, 25 | { 26 | name: "findNumber2", 27 | args: args{ 28 | slice: []int{2, 3, 4, 13}, 29 | condition: func(item int, _ int, _ []int) bool { 30 | return item == 4 31 | }, 32 | }, 33 | want: 2, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := FindIndex(tt.args.slice, tt.args.condition); got != tt.want { 39 | t.Errorf("FindIndex() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /timer/delayqueue/delayqueue_test.go: -------------------------------------------------------------------------------- 1 | package delayqueue 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestDelayQueue(t *testing.T) { 10 | times := []int{1, 2, 3} 11 | q := New[int]() 12 | for _, t := range times { 13 | q.Push(t, time.Microsecond*time.Duration(t)) 14 | } 15 | 16 | for _, time := range times { 17 | value, ok := q.Take(context.Background()) 18 | if !ok { 19 | t.Errorf("want %v, but %v", true, ok) 20 | } 21 | if value != time { 22 | t.Errorf("want %v, but %v", time, value) 23 | } 24 | } 25 | } 26 | 27 | func BenchmarkPushAndTake(b *testing.B) { 28 | q := New[int]() 29 | b.ResetTimer() 30 | 31 | for i := 0; i < b.N; i++ { 32 | q.Push(i, time.Duration(i)) 33 | } 34 | b.StopTimer() 35 | time.Sleep(time.Duration(b.N)) 36 | b.StartTimer() 37 | 38 | for i := 0; i < b.N; i++ { 39 | _, ok := q.Take(context.Background()) 40 | if !ok { 41 | b.Errorf("want %v, but %v", true, ok) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crypto/aes/aes_test.go: -------------------------------------------------------------------------------- 1 | package aes 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | Key = "thisis32bitlongpassphraseimusing" 10 | ) 11 | 12 | var ( 13 | Texts = []string{ 14 | "This is a secret", 15 | "abc", 16 | "666", 17 | "1", 18 | "myzzz", 19 | "", 20 | "zcxzcxdasas", 21 | "zcxczxczx2131", 22 | } 23 | ) 24 | 25 | func TestNormal(t *testing.T) { 26 | c := New([]byte(Key)) 27 | for _, text := range Texts { 28 | enc := c.Encrypt([]byte(text)) 29 | dec := c.Decrypt(enc) 30 | if !bytes.Equal(dec, []byte(text)) { 31 | t.Errorf("wand %s, but %s", text, dec) 32 | } 33 | } 34 | } 35 | 36 | func TestBase64(t *testing.T) { 37 | c := New([]byte(Key)) 38 | for _, text := range Texts { 39 | enc := c.EncryptToBase64(text) 40 | dec, err := c.DecryptFromBase64(enc) 41 | if err != nil { 42 | t.Errorf("err %v", err) 43 | } 44 | 45 | if dec != text { 46 | t.Errorf("wand %s, but %s", text, dec) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /slices/find_last_index_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestFindLastIndex(t *testing.T) { 6 | type args struct { 7 | slice []int 8 | condition func(item int, index int, slice []int) bool 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want int 14 | }{ 15 | { 16 | name: "findNumber1", 17 | args: args{ 18 | slice: []int{2, 3, 4, 13}, 19 | condition: func(item int, _ int, _ []int) bool { 20 | return item == 14 21 | }, 22 | }, 23 | want: -1, 24 | }, 25 | { 26 | name: "findNumber2", 27 | args: args{ 28 | slice: []int{2, 3, 4, 4}, 29 | condition: func(item int, _ int, _ []int) bool { 30 | return item == 4 31 | }, 32 | }, 33 | want: 3, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := FindLastIndex(tt.args.slice, tt.args.condition); got != tt.want { 39 | t.Errorf("FindIndex() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /slices/sort_stable_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | type Item struct { 9 | Index int 10 | Value int 11 | } 12 | 13 | func TestSortStable(t *testing.T) { 14 | type args struct { 15 | slice []Item 16 | condition func(item1, item2 Item) bool 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | want []Item 22 | }{ 23 | { 24 | name: "item1", 25 | args: args{ 26 | slice: []Item{{1, 1}, {2, 3}, {3, 5}, {4, 3}, 27 | {5, 4}, {6, 1}, {7, 1}, {8, 1}}, 28 | condition: func(item1, item2 Item) bool { 29 | return item1.Value < item2.Value 30 | }, 31 | }, 32 | want: []Item{{1, 1}, {6, 1}, {7, 1}, {8, 1}, 33 | {2, 3}, {4, 3}, {5, 4}, {3, 5}}, 34 | }, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | if got := SortStable(tt.args.slice, tt.args.condition); !reflect.DeepEqual(got, tt.want) { 39 | t.Errorf("SortStable() = %v, want %v", got, tt.want) 40 | } 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pool/level/pool.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // 创建新对象 8 | type NewFunc[T any] func(l int) T 9 | 10 | // 清理对象 11 | type ClearFunc[T any] func(T) T 12 | 13 | type LevelFunc[T any] func(t T) int 14 | 15 | // 分级的对象池 16 | type Pool[T any] struct { 17 | pools []sync.Pool 18 | levelFunc LevelFunc[T] 19 | clearFunc ClearFunc[T] 20 | } 21 | 22 | func New[T any](newFunc NewFunc[T], clearFunc ClearFunc[T], levelFunc LevelFunc[T], maxLevel int) *Pool[T] { 23 | if newFunc == nil { 24 | panic("must be provide NewFunc") 25 | } 26 | p := &Pool[T]{ 27 | clearFunc: clearFunc, 28 | levelFunc: levelFunc, 29 | pools: make([]sync.Pool, maxLevel+1), 30 | } 31 | for i := 0; i <= maxLevel; i++ { 32 | i0 := i 33 | p.pools[i0].New = func() any { 34 | return newFunc(i0) 35 | } 36 | } 37 | return p 38 | } 39 | 40 | // 获取对象 41 | func (p *Pool[T]) Get(l int) T { 42 | return p.pools[l].Get().(T) 43 | } 44 | 45 | // 归还对象 46 | func (p *Pool[T]) Put(b T) { 47 | if p.clearFunc != nil { 48 | b = p.clearFunc(b) 49 | } 50 | l := p.levelFunc(b) 51 | p.pools[l].Put(b) 52 | } 53 | -------------------------------------------------------------------------------- /hash/hash_test.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/jiaxwu/gommon/conv" 8 | ) 9 | 10 | type Key struct { 11 | S string 12 | B int 13 | } 14 | 15 | func TestStruct(t *testing.T) { 16 | h := New() 17 | a := h.Sum64(conv.MustMarshal(Key{ 18 | S: "ab", 19 | B: 6, 20 | })) 21 | b := h.Sum64(conv.MustMarshal(Key{ 22 | S: "ab", 23 | B: 6, 24 | })) 25 | if a != b { 26 | t.Errorf("want %v, but %v", a, b) 27 | } 28 | } 29 | 30 | func TestString(t *testing.T) { 31 | h := New() 32 | a := h.Sum64String("bz") 33 | b := h.Sum64String("bz") 34 | if a != b { 35 | t.Errorf("want %v, but %v", a, b) 36 | } 37 | } 38 | 39 | func Benchmark64(b *testing.B) { 40 | buf := make([]byte, 8192) 41 | for length := 1; length <= cap(buf); length *= 2 { 42 | b.Run(strconv.Itoa(length), func(b *testing.B) { 43 | h := New() 44 | buf = buf[:length] 45 | b.SetBytes(int64(length)) 46 | b.ReportAllocs() 47 | b.ResetTimer() 48 | for i := 0; i < b.N; i++ { 49 | h.Sum64(buf) 50 | } 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /container/ringbuffer/docs/use.drawio: -------------------------------------------------------------------------------- 1 | 7VZdb5swFP01fmzFRyDw2Hx1kzotWiatfXThAlYMZsZpSH797GAHCMm6tupDqyVSZB9fX3PPOdcBudO8vuW4zL6xGChyrLhG7gw5zigI5K8Cdg3gjDWQchI3kN0CK7IHDVoa3ZAYql6gYIwKUvbBiBUFRKKHYc7Zth+WMNo/tcQpDIBVhOkQ/UVikTVo4Ixb/AuQNDMn237YrOTYBOtKqgzHbNuB3Dlyp5wx0YzyegpUcWd4afYtLqweH4xDIf5lQ71f+A/3P3/8vreWwV1RQzZfXuksT5hudMGLr4vvaB6iMEDhAs09JBUMdbWV2BlethkRsCpxpOZbKT1yJ5nIqZzZclitQUSZniSE0imjjB+2uomnvipIcLaGzop/+KgdrBAdvPlIfFizKQC4gLoDaQ5ugeUg+E6G6FXH0npoQ46M07atvCNPY1lHWl9jWDsqPaZuSZcDzfsLNHAGGgzYlt4p1TChUN8oV0suoIj1cBZRXFUk6ktg6nGtt+txkXeIe+0zZL3D6jlSDcaBYkGe+k13jml9wpIR+SRHUW37RFQ3vPb6SSq24RHofd02OU0VPptKYJ6CGKQ6iH8s/fV+MLV0DEGKgSUU83f4Ud62PdUxJWmhLCGVAinjRHUGkffZjV7ISRyrHBMOFdnjx0M+ZZFS1XOo0Jsgb3ZqGqrOmuBonXK2KWLjkoIVcNYgf7X6abce72/9PL0r8lwXX1nX1mjsvc08JoQlSQXvI+Xwfv3f2y/sbd8/05DB65p7PHo+13t39/C6ZxvxOdrbvvRv/PH6W07bV7MmvH2/ded/AA== -------------------------------------------------------------------------------- /slices/find_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFind(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | condition func(item int, index int, slice []int) bool 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want int 17 | want1 bool 18 | }{ 19 | { 20 | name: "findNumber1", 21 | args: args{ 22 | slice: []int{2, 3, 4, 13}, 23 | condition: func(item int, _ int, _ []int) bool { 24 | return item == 14 25 | }, 26 | }, 27 | want: 0, 28 | want1: false, 29 | }, 30 | { 31 | name: "findNumber2", 32 | args: args{ 33 | slice: []int{2, 3, 4, 13}, 34 | condition: func(item int, _ int, _ []int) bool { 35 | return item == 4 36 | }, 37 | }, 38 | want: 4, 39 | want1: true, 40 | }, 41 | } 42 | for _, tt := range tests { 43 | t.Run(tt.name, func(t *testing.T) { 44 | got, got1 := Find(tt.args.slice, tt.args.condition) 45 | if !reflect.DeepEqual(got, tt.want) { 46 | t.Errorf("Find() got = %v, want %v", got, tt.want) 47 | } 48 | if got1 != tt.want1 { 49 | t.Errorf("Find() got1 = %v, want %v", got1, tt.want1) 50 | } 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /slices/find_last_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestFindLast(t *testing.T) { 9 | type args struct { 10 | slice []int 11 | condition func(item int, index int, slice []int) bool 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want int 17 | want1 bool 18 | }{ 19 | { 20 | name: "findNumber1", 21 | args: args{ 22 | slice: []int{2, 3, 4, 4}, 23 | condition: func(item int, _ int, _ []int) bool { 24 | return item == 14 25 | }, 26 | }, 27 | want: 0, 28 | want1: false, 29 | }, 30 | { 31 | name: "findNumber2", 32 | args: args{ 33 | slice: []int{2, 3, 4, 4}, 34 | condition: func(item int, _ int, _ []int) bool { 35 | return item == 4 36 | }, 37 | }, 38 | want: 4, 39 | want1: true, 40 | }, 41 | } 42 | for _, tt := range tests { 43 | t.Run(tt.name, func(t *testing.T) { 44 | got, got1 := FindLast(tt.args.slice, tt.args.condition) 45 | if !reflect.DeepEqual(got, tt.want) { 46 | t.Errorf("Find() got = %v, want %v", got, tt.want) 47 | } 48 | if got1 != tt.want1 { 49 | t.Errorf("Find() got1 = %v, want %v", got1, tt.want1) 50 | } 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /limiter/token_bucket_limiter.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | // TokenBucketLimiter 令牌桶限流器 9 | type TokenBucketLimiter struct { 10 | capacity int // 容量 11 | currentTokens int // 令牌数量 12 | rate int // 发放令牌速率/秒 13 | lastTime time.Time // 上次发放令牌时间 14 | mutex sync.Mutex // 避免并发问题 15 | } 16 | 17 | func NewTokenBucketLimiter(capacity, rate int) *TokenBucketLimiter { 18 | return &TokenBucketLimiter{ 19 | capacity: capacity, 20 | rate: rate, 21 | lastTime: time.Now(), 22 | } 23 | } 24 | 25 | func (l *TokenBucketLimiter) TryAcquire() bool { 26 | l.mutex.Lock() 27 | defer l.mutex.Unlock() 28 | 29 | // 尝试发放令牌 30 | now := time.Now() 31 | // 距离上次发放令牌的时间 32 | interval := now.Sub(l.lastTime) 33 | if interval >= time.Second { 34 | // 当前令牌数量+距离上次发放令牌的时间(秒)*发放令牌速率 35 | l.currentTokens = minInt(l.capacity, l.currentTokens+int(interval/time.Second)*l.rate) 36 | l.lastTime = now 37 | } 38 | 39 | // 如果没有令牌,请求失败 40 | if l.currentTokens == 0 { 41 | return false 42 | } 43 | // 如果有令牌,当前令牌-1,请求成功 44 | l.currentTokens-- 45 | return true 46 | } 47 | 48 | func minInt(a, b int) int { 49 | if a < b { 50 | return a 51 | } 52 | return b 53 | } 54 | -------------------------------------------------------------------------------- /limiter/fixed_window_limiter_test.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewFixedWindowLimiter(t *testing.T) { 9 | type args struct { 10 | limit int 11 | window time.Duration 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want *FixedWindowLimiter 17 | }{ 18 | { 19 | name: "100_second", 20 | args: args{ 21 | limit: 100, 22 | window: time.Second, 23 | }, 24 | want: nil, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | l := NewFixedWindowLimiter(tt.args.limit, tt.args.window) 30 | successCount := 0 31 | for i := 0; i < tt.args.limit*2; i++ { 32 | if l.TryAcquire() { 33 | successCount++ 34 | } 35 | } 36 | if successCount != tt.args.limit { 37 | t.Errorf("NewFixedWindowLimiter() = %v, want %v", successCount, tt.args.limit) 38 | } 39 | time.Sleep(time.Second) 40 | successCount = 0 41 | for i := 0; i < tt.args.limit*2; i++ { 42 | if l.TryAcquire() { 43 | successCount++ 44 | } 45 | } 46 | if successCount != tt.args.limit { 47 | t.Errorf("NewFixedWindowLimiter() = %v, want %v", successCount, tt.args.limit) 48 | } 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /container/ringbuffer/unbounded/unbounded_test.go: -------------------------------------------------------------------------------- 1 | package unbounded 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRingAll(t *testing.T) { 8 | n := 10 9 | r := New[int](0) 10 | for i := 1; i <= n; i++ { 11 | r.Push(i) 12 | } 13 | 14 | v := r.Peek() 15 | if v != 1 { 16 | t.Errorf("Peek() = %v, want %v", v, 1) 17 | } 18 | 19 | i := 1 20 | for !r.Empty() { 21 | v := r.Pop() 22 | if i != v { 23 | t.Errorf("Pop() = %v, want %v", v, i) 24 | } 25 | i++ 26 | } 27 | } 28 | 29 | func TestRingMAll(t *testing.T) { 30 | r := New[int](0) 31 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 32 | 33 | v := r.Peek() 34 | if v != 1 { 35 | t.Errorf("Peek() = %v, want %v", v, 1) 36 | } 37 | 38 | i := 1 39 | for !r.Empty() { 40 | v := r.MPop(1) 41 | if i != v[0] { 42 | t.Errorf("Pop() = %v, want %v", v, i) 43 | } 44 | i++ 45 | } 46 | 47 | r.Reset() 48 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 49 | i = 1 50 | dst := make([]int, 1) 51 | for !r.Empty() { 52 | r.MPopCopy(dst) 53 | if i != dst[0] { 54 | t.Errorf("Pop() = %v, want %v", v, i) 55 | } 56 | i++ 57 | } 58 | } 59 | 60 | func BenchmarkPushPop(b *testing.B) { 61 | r := New[int](0) 62 | for i := 0; i < b.N; i++ { 63 | r.Push(i) 64 | } 65 | for i := 0; i < b.N; i++ { 66 | r.Pop() 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /limiter/leaky_bucket_limiter.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | // LeakyBucketLimiter 漏桶限流器 9 | type LeakyBucketLimiter struct { 10 | peakLevel int // 最高水位 11 | currentLevel int // 当前水位 12 | currentVelocity int // 水流速度/秒 13 | lastTime time.Time // 上次放水时间 14 | mutex sync.Mutex // 避免并发问题 15 | } 16 | 17 | func NewLeakyBucketLimiter(peakLevel, currentVelocity int) *LeakyBucketLimiter { 18 | return &LeakyBucketLimiter{ 19 | peakLevel: peakLevel, 20 | currentVelocity: currentVelocity, 21 | lastTime: time.Now(), 22 | } 23 | } 24 | 25 | func (l *LeakyBucketLimiter) TryAcquire() bool { 26 | l.mutex.Lock() 27 | defer l.mutex.Unlock() 28 | 29 | // 尝试放水 30 | now := time.Now() 31 | // 距离上次放水的时间 32 | interval := now.Sub(l.lastTime) 33 | if interval >= time.Second { 34 | // 当前水位-距离上次放水的时间(秒)*水流速度 35 | l.currentLevel = maxInt(0, l.currentLevel-int(interval/time.Second)*l.currentVelocity) 36 | l.lastTime = now 37 | } 38 | 39 | // 若到达最高水位,请求失败 40 | if l.currentLevel >= l.peakLevel { 41 | return false 42 | } 43 | // 若没有到达最高水位,当前水位+1,请求成功 44 | l.currentLevel++ 45 | return true 46 | } 47 | 48 | func maxInt(a, b int) int { 49 | if a > b { 50 | return a 51 | } 52 | return b 53 | } 54 | -------------------------------------------------------------------------------- /container/ringbuffer/fix/fix_test.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestRingAll(t *testing.T) { 8 | n := 10 9 | r := New[int](uint64(n)) 10 | for i := 1; i <= n; i++ { 11 | r.Push(i) 12 | } 13 | 14 | v := r.Peek() 15 | if v != 1 { 16 | t.Errorf("Peek() = %v, want %v", v, 1) 17 | } 18 | 19 | i := 1 20 | for !r.Empty() { 21 | v := r.Pop() 22 | if i != v { 23 | t.Errorf("Pop() = %v, want %v", v, i) 24 | } 25 | i++ 26 | } 27 | } 28 | 29 | func TestRingMAll(t *testing.T) { 30 | n := 10 31 | r := New[int](uint64(n)) 32 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 33 | 34 | v := r.Peek() 35 | if v != 1 { 36 | t.Errorf("Peek() = %v, want %v", v, 1) 37 | } 38 | 39 | i := 1 40 | for !r.Empty() { 41 | v := r.MPop(1) 42 | if i != v[0] { 43 | t.Errorf("Pop() = %v, want %v", v, i) 44 | } 45 | i++ 46 | } 47 | 48 | r.Reset() 49 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 50 | i = 1 51 | dst := make([]int, 1) 52 | for !r.Empty() { 53 | r.MPopCopy(dst) 54 | if i != dst[0] { 55 | t.Errorf("Pop() = %v, want %v", v, i) 56 | } 57 | i++ 58 | } 59 | } 60 | 61 | func BenchmarkPushPop(b *testing.B) { 62 | r := New[int](uint64(b.N)) 63 | for i := 0; i < b.N; i++ { 64 | r.Push(i) 65 | } 66 | for i := 0; i < b.N; i++ { 67 | r.Pop() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /counter/qps/qps_test.go: -------------------------------------------------------------------------------- 1 | package qps 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | const windowCnt = 100 9 | 10 | func TestAdd(t *testing.T) { 11 | q := New(windowCnt) 12 | addTimes := 100000 13 | for i := 0; i < addTimes; i++ { 14 | q.Add() 15 | } 16 | w := q.Get() 17 | if w.TotalCnt != int64(addTimes) { 18 | t.Errorf("totalCnt: %d, expected: %d", w.TotalCnt, addTimes) 19 | } 20 | } 21 | 22 | func TestAdd_SleepOneSecond(t *testing.T) { 23 | q := New(windowCnt) 24 | addTimes := 100000 25 | for i := 0; i < addTimes; i++ { 26 | q.Add() 27 | } 28 | time.Sleep(time.Second) 29 | w := q.Get() 30 | if w.TotalCnt != 0 { 31 | t.Errorf("totalCnt: %d, expected: %d", w.TotalCnt, 0) 32 | } 33 | } 34 | 35 | func BenchmarkAdd(b *testing.B) { 36 | q := New(windowCnt) 37 | for n := 0; n < b.N; n++ { 38 | q.Add() 39 | } 40 | } 41 | 42 | func BenchmarkAddSince(b *testing.B) { 43 | q := New(windowCnt) 44 | for n := 0; n < b.N; n++ { 45 | q.AddSince(time.Now()) 46 | } 47 | } 48 | 49 | func BenchmarkAddUseTime(b *testing.B) { 50 | q := New(windowCnt) 51 | for n := 0; n < b.N; n++ { 52 | now := time.Now() 53 | q.AddUseTime(time.Since(now)) 54 | } 55 | } 56 | 57 | func BenchmarkAddGet(b *testing.B) { 58 | q := New(windowCnt) 59 | for n := 0; n < b.N; n++ { 60 | q.Add() 61 | q.Get() 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pool/pool_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | blocks = 64 10 | blockSize = 1024 11 | ) 12 | 13 | var block = make([]byte, blockSize) 14 | 15 | func BenchmarkByte(b *testing.B) { 16 | for n := 0; n < b.N; n++ { 17 | var b []byte 18 | for i := 0; i < blocks; i++ { 19 | b = append(b, block...) 20 | } 21 | } 22 | } 23 | 24 | func BenchmarkMake(b *testing.B) { 25 | for n := 0; n < b.N; n++ { 26 | b := make([]byte, 0, blocks*blockSize) 27 | for i := 0; i < blocks; i++ { 28 | b = append(b, block...) 29 | } 30 | } 31 | } 32 | 33 | func BenchmarkBuffer(b *testing.B) { 34 | for n := 0; n < b.N; n++ { 35 | b := bytes.NewBuffer(make([]byte, 0, blocks*blockSize)) 36 | for i := 0; i < blocks; i++ { 37 | b.Write(block) 38 | } 39 | } 40 | } 41 | 42 | func BenchmarkBytePool(b *testing.B) { 43 | pool := NewBytePool(0, blocks*blockSize) 44 | for n := 0; n < b.N; n++ { 45 | b := pool.Get() 46 | for i := 0; i < blocks; i++ { 47 | b = append(b, block...) 48 | } 49 | pool.Put(b) 50 | } 51 | } 52 | 53 | func BenchmarkBufferPool(b *testing.B) { 54 | pool := NewBufferPool(0, blocks*blockSize) 55 | for n := 0; n < b.N; n++ { 56 | b := pool.Get() 57 | for i := 0; i < blocks; i++ { 58 | b.Write(block) 59 | } 60 | pool.Put(b) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /limiter/token_bucket_limiter_test.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewTokenBucketLimiter(t *testing.T) { 9 | type args struct { 10 | capacity int 11 | rate int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want *TokenBucketLimiter 17 | }{ 18 | { 19 | name: "60", 20 | args: args{ 21 | capacity: 60, 22 | rate: 10, 23 | }, 24 | want: nil, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | l := NewTokenBucketLimiter(tt.args.capacity, tt.args.rate) 30 | time.Sleep(time.Second) 31 | successCount := 0 32 | for i := 0; i < tt.args.rate; i++ { 33 | if l.TryAcquire() { 34 | successCount++ 35 | } 36 | } 37 | if successCount != tt.args.rate { 38 | t.Errorf("NewTokenBucketLimiter() got = %v, want %v", successCount, tt.args.rate) 39 | return 40 | } 41 | 42 | successCount = 0 43 | for i := 0; i < tt.args.capacity; i++ { 44 | if l.TryAcquire() { 45 | successCount++ 46 | } 47 | time.Sleep(time.Second / 10) 48 | } 49 | if successCount != tt.args.capacity-tt.args.rate { 50 | t.Errorf("NewTokenBucketLimiter() got = %v, want %v", successCount, tt.args.capacity-tt.args.rate) 51 | return 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /crypto/query/query_test.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | Key = "thisis32bitlongpassphraseimusing" 10 | SliceSize = 4 11 | ) 12 | 13 | var ( 14 | Texts = []string{ 15 | // "This is a secret", 16 | // "abcz", 17 | // "666dd", 18 | // "14512512", 19 | // "myzzz", 20 | // "dasdasz", 21 | // "zcxzcxdasas", 22 | // "zcxczxczx2131", 23 | "012345678901", 24 | } 25 | ) 26 | 27 | func TestNormal(t *testing.T) { 28 | c := New([]byte(Key), SliceSize) 29 | for _, text := range Texts { 30 | enc, err := c.Encrypt([]byte(text)) 31 | if err != nil { 32 | t.Errorf("err %v", err) 33 | } 34 | dec, err := c.Decrypt(enc) 35 | if err != nil { 36 | t.Errorf("err %v", err) 37 | } 38 | if !bytes.Equal(dec, []byte(text)) { 39 | t.Errorf("wand %s, but %s", text, dec) 40 | } 41 | } 42 | } 43 | 44 | func TestBase64(t *testing.T) { 45 | c := New([]byte(Key), SliceSize) 46 | for _, text := range Texts { 47 | enc, err := c.EncryptToBase64(text) 48 | if err != nil { 49 | t.Errorf("err %v", err) 50 | } 51 | dec, err := c.DecryptFromBase64(enc) 52 | if err != nil { 53 | t.Errorf("err %v", err) 54 | } 55 | if dec != text { 56 | t.Errorf("wand %s, but %s", text, dec) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pool/level/byte_pool.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | type BytePool struct { 8 | p *Pool[[]byte] 9 | levels []int 10 | perLevel int 11 | maxLevel int 12 | } 13 | 14 | // 比如level=5,则等级为perLevel、perLevel*2、perLevel*4、perLevel*8、perLevel*16和更大 15 | func NewBytePool(perLevel, maxLevel int) *BytePool { 16 | levels := make([]int, maxLevel) 17 | levels[0] = perLevel 18 | for i := 1; i < maxLevel; i++ { 19 | levels[i] = levels[i-1] * 2 20 | } 21 | newFunc := func(l int) []byte { 22 | if l == maxLevel { 23 | return nil 24 | } 25 | return make([]byte, 0, levels[l]) 26 | } 27 | clearFunc := func(b []byte) []byte { 28 | return b[:0] 29 | } 30 | p := &BytePool{ 31 | perLevel: perLevel, 32 | maxLevel: maxLevel, 33 | levels: levels, 34 | } 35 | levelFunc := func(b []byte) int { 36 | return p.level(cap(b)) 37 | } 38 | p.p = New(newFunc, clearFunc, levelFunc, maxLevel) 39 | return p 40 | } 41 | 42 | func (p *BytePool) level(capacity int) int { 43 | return sort.SearchInts(p.levels, capacity) 44 | } 45 | 46 | // 获取字节数组 47 | func (p *BytePool) Get(capacity int) []byte { 48 | // 计算等级 49 | l := p.level(capacity) 50 | // 尝试获取 51 | b := p.p.Get(l) 52 | // 获取不到则新创建一个 53 | if cap(b) < capacity { 54 | return make([]byte, capacity) 55 | } 56 | return b 57 | } 58 | 59 | // 归还字节数组 60 | func (p *BytePool) Put(b []byte) { 61 | p.p.Put(b) 62 | } 63 | -------------------------------------------------------------------------------- /limiter/leaky_bucket_limiter_test.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewLeakyBucketLimiter(t *testing.T) { 9 | type args struct { 10 | peakLevel int 11 | currentVelocity int 12 | } 13 | tests := []struct { 14 | name string 15 | args args 16 | want *LeakyBucketLimiter 17 | wantErr bool 18 | }{ 19 | { 20 | name: "60", 21 | args: args{ 22 | peakLevel: 60, 23 | currentVelocity: 10, 24 | }, 25 | want: nil, 26 | }, 27 | } 28 | for _, tt := range tests { 29 | t.Run(tt.name, func(t *testing.T) { 30 | l := NewLeakyBucketLimiter(tt.args.peakLevel, tt.args.currentVelocity) 31 | successCount := 0 32 | for i := 0; i < tt.args.peakLevel; i++ { 33 | if l.TryAcquire() { 34 | successCount++ 35 | } 36 | } 37 | if successCount != tt.args.peakLevel { 38 | t.Errorf("NewLeakyBucketLimiter() got = %v, want %v", successCount, tt.args.peakLevel) 39 | return 40 | } 41 | 42 | successCount = 0 43 | for i := 0; i < tt.args.peakLevel; i++ { 44 | if l.TryAcquire() { 45 | successCount++ 46 | } 47 | time.Sleep(time.Second / 10) 48 | } 49 | if successCount != tt.args.peakLevel-tt.args.currentVelocity { 50 | t.Errorf("NewLeakyBucketLimiter() got = %v, want %v", successCount, tt.args.peakLevel-tt.args.currentVelocity) 51 | return 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /slices/to_map_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestToMap(t *testing.T) { 10 | type args struct { 11 | slice []int 12 | keyFunc func(int, int, []int) int 13 | valueFunc func(int, int, []int) any 14 | } 15 | tests := []struct { 16 | name string 17 | args args 18 | want map[int]any 19 | }{ 20 | { 21 | name: "number1", 22 | args: args{ 23 | slice: []int{2, 3, 4, 13}, 24 | keyFunc: func(item int, index int, slice []int) int { 25 | return item 26 | }, 27 | valueFunc: func(item int, index int, slice []int) any { 28 | return item 29 | }, 30 | }, 31 | want: map[int]any{ 32 | 2: 2, 3: 3, 4: 4, 13: 13, 33 | }, 34 | }, 35 | { 36 | name: "number2", 37 | args: args{ 38 | slice: []int{2, 3, 4, 13}, 39 | keyFunc: func(item int, index int, slice []int) int { 40 | return item 41 | }, 42 | valueFunc: func(item int, index int, slice []int) any { 43 | return fmt.Sprint(item) 44 | }, 45 | }, 46 | want: map[int]any{ 47 | 2: "2", 3: "3", 4: "4", 13: "13", 48 | }, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | got := ToMap(tt.args.slice, tt.args.keyFunc, tt.args.valueFunc) 54 | gotJson, _ := json.Marshal(&got) 55 | wantJson, _ := json.Marshal(&tt.want) 56 | if string(gotJson) != string(wantJson) { 57 | t.Errorf("ToMap() = %v, want %v", got, tt.want) 58 | } 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pool/level/buffer_pool.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | import ( 4 | "bytes" 5 | "sort" 6 | ) 7 | 8 | type BufferPool struct { 9 | p *Pool[*bytes.Buffer] 10 | levels []int 11 | perLevel int 12 | maxLevel int 13 | } 14 | 15 | // 比如level=5,则等级为perLevel、perLevel*2、perLevel*4、perLevel*8、perLevel*16和更大 16 | func NewBufferPool(perLevel, maxLevel int) *BufferPool { 17 | levels := make([]int, maxLevel) 18 | levels[0] = perLevel 19 | for i := 1; i < maxLevel; i++ { 20 | levels[i] = levels[i-1] * 2 21 | } 22 | newFunc := func(l int) *bytes.Buffer { 23 | if l == maxLevel { 24 | return nil 25 | } 26 | return bytes.NewBuffer(make([]byte, 0, levels[l])) 27 | } 28 | clearFunc := func(b *bytes.Buffer) *bytes.Buffer { 29 | b.Reset() 30 | return b 31 | } 32 | p := &BufferPool{ 33 | perLevel: perLevel, 34 | maxLevel: maxLevel, 35 | levels: levels, 36 | } 37 | levelFunc := func(b *bytes.Buffer) int { 38 | return p.level(b.Cap()) 39 | } 40 | p.p = New(newFunc, clearFunc, levelFunc, maxLevel) 41 | return p 42 | } 43 | 44 | func (p *BufferPool) level(capacity int) int { 45 | return sort.SearchInts(p.levels, capacity) 46 | } 47 | 48 | // 获取字节Buffer 49 | func (p *BufferPool) Get(capacity int) *bytes.Buffer { 50 | // 计算等级 51 | l := p.level(capacity) 52 | // 尝试获取 53 | b := p.p.Get(l) 54 | // 获取不到则新创建一个 55 | if b.Cap() < capacity { 56 | return bytes.NewBuffer(make([]byte, capacity)) 57 | } 58 | return b 59 | } 60 | 61 | // 归还字节Buffer 62 | func (p *BufferPool) Put(b *bytes.Buffer) { 63 | p.p.Put(b) 64 | } 65 | -------------------------------------------------------------------------------- /crypto/aes/aes.go: -------------------------------------------------------------------------------- 1 | package aes 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "encoding/base64" 7 | 8 | "github.com/jiaxwu/gommon/crypto" 9 | ) 10 | 11 | // AES+CBC+PKCS5Padding加密,添加一些便捷方法 12 | type Cipher struct { 13 | block cipher.Block 14 | iv []byte 15 | } 16 | 17 | func New(key []byte) *Cipher { 18 | block, err := aes.NewCipher(key) 19 | if err != nil { 20 | panic(err) 21 | } 22 | return &Cipher{ 23 | block: block, 24 | iv: key[:block.BlockSize()], 25 | } 26 | } 27 | 28 | // 加密 29 | func (c *Cipher) Encrypt(src []byte) []byte { 30 | encrypter := cipher.NewCBCEncrypter(c.block, c.iv) 31 | src = crypto.PKCS5Padding(src, c.BlockSize()) 32 | dst := make([]byte, len(src)) 33 | encrypter.CryptBlocks(dst, src) 34 | return dst 35 | } 36 | 37 | // 加密到Base64 38 | func (c *Cipher) EncryptToBase64(src string) string { 39 | dst := c.Encrypt([]byte(src)) 40 | return base64.RawStdEncoding.EncodeToString(dst) 41 | } 42 | 43 | // 解密 44 | func (c *Cipher) Decrypt(src []byte) []byte { 45 | decrypter := cipher.NewCBCDecrypter(c.block, c.iv) 46 | dst := make([]byte, len(src)) 47 | decrypter.CryptBlocks(dst, src) 48 | return crypto.PKCS5Trimming(dst) 49 | } 50 | 51 | // 从Base64解密 52 | func (c *Cipher) DecryptFromBase64(src string) (string, error) { 53 | dst, err := base64.RawStdEncoding.DecodeString(src) 54 | if err != nil { 55 | return "", err 56 | } 57 | return string(c.Decrypt(dst)), nil 58 | } 59 | 60 | func (c *Cipher) BlockSize() int { 61 | return c.block.BlockSize() 62 | } 63 | -------------------------------------------------------------------------------- /container/ringbuffer/docs/impl.drawio: -------------------------------------------------------------------------------- 1 | 7Vltb5swEP41/riIYCDwERK6F3VTt05a+tEDJ3glOHOchfTXzzamYCBp165rFC2tIvu588V+7s65IwBOV+VbhtbZR5riHNhWWgI4A7bt+q54l8C+AiB0KmDJSFpB4wa4JndYg5ZGtyTFG0ORU5pzsjbBhBYFTriBIcbozlRb0Nz81DVa4h5wnaC8j34jKc8q1LcnDf4Ok2VWf/LYCyrJCtXK+iSbDKV014JgDOCUUcqr0aqc4lxyV/NSrbs4IL3fGMMFf8yC8u7Cu5l//fJzbl35l0WJs/jqjbbyC+VbfWC9Wb6vGdhlhOPrNUrkfCecDGCU8VUuZmMxXJA8n9KcMqUNF678E/iGM3qLWxJPveQKWvAWXr3kilvMk0yb7Z+u3ipmHJctSJ/2LaYrzNleqGipbWnmdeg5dUztGkd6GspaPqwxpENneW+5YVcMNMF/QLb9MNkiSNZyuMhxGcrwFVTgItXDWZKjzYYkpgfq40DrSe54HO04NfKkT3qLVHeA1BpjOEec/DKza4hp/QlXlIidND6Fpk9dp+OsDd2yBOtV7WzoGrKD0WRsxoffscURW2Les6V8f3/yp4dDHY6teCBFLyIk8Zfou7hVDaejnCwLGRHCUVi4NJJ5QcS9FWrBiqSptBExvCF36LuyJyNkLc+jTuhGwJ0JJJfmI5TcLhndFmkdJAUt8MPxcTTSu7l6f0/r/RhX4VAOv7FGljNxnxc7tQpdLDb4ZVz5iHv0f2ofT23o/qXUhk4w8r3XTe3+VU+3/Dxye3zoi/gMkhvGM0bsTx/m89X79JPPr6LPP069RhrwQy/JD9dI3uvVSINk9xPHOR+2oX9ibMMe2/CM2LZPjG2nx7Z7Pmw7zomx7fbY9s6HbfcVe9tBtr0e25MzYvvUviWDHreylLzWU8p4Rpe0QHncoFGDXlK61uT8wJzv9VM3tOXU9AguCZ+3xjeyzBy5ejYrddWpJvt6UogDzhtFOb1py5plalavG/BZpzEZbF6qpuAIVToMq4L/WFk7HAF/VpaKZg7tWwq6Hj/Y80y6aex0nuE9oA+D4/rdxyUdfTGodvx36+X+cw0QuyCYgtAHcQD8MfBnEvFjIPo0MQjFYApiT+qIEkkqW1IaOyDyFaIGYagGwkJk6kxBECokAkGsLPsgEqsmIJzK/wE7F3KVX4kiZUcpSETtR9qZSFzasaUoUKIoVsuVKLyoRbPBfHzh1u7gxTbchpnpc/haOXgLWqOxaNKNeKrL9We2/mNn5JtrXq6bm/SC0z+fLyrv35W8Ytr8dlE5p/kBCMa/AQ== -------------------------------------------------------------------------------- /limiter/sliding_window_limiter.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // SlidingWindowLimiter 滑动窗口限流器 10 | type SlidingWindowLimiter struct { 11 | limit int // 窗口请求上限 12 | window int64 // 窗口时间大小 13 | smallWindow int64 // 小窗口时间大小 14 | smallWindows int64 // 小窗口数量 15 | counters map[int64]int // 小窗口计数器 16 | mutex sync.Mutex // 避免并发问题 17 | } 18 | 19 | func NewSlidingWindowLimiter(limit int, window, smallWindow time.Duration) (*SlidingWindowLimiter, error) { 20 | // 窗口时间必须能够被小窗口时间整除 21 | if window%smallWindow != 0 { 22 | return nil, errors.New("window cannot be split by integers") 23 | } 24 | 25 | return &SlidingWindowLimiter{ 26 | limit: limit, 27 | window: int64(window), 28 | smallWindow: int64(smallWindow), 29 | smallWindows: int64(window / smallWindow), 30 | counters: make(map[int64]int), 31 | }, nil 32 | } 33 | 34 | func (l *SlidingWindowLimiter) TryAcquire() bool { 35 | l.mutex.Lock() 36 | defer l.mutex.Unlock() 37 | 38 | // 获取当前小窗口值 39 | currentSmallWindow := time.Now().UnixNano() / l.smallWindow * l.smallWindow 40 | // 获取起始小窗口值 41 | startSmallWindow := currentSmallWindow - l.smallWindow*(l.smallWindows-1) 42 | 43 | // 计算当前窗口的请求总数 44 | var count int 45 | for smallWindow, counter := range l.counters { 46 | if smallWindow < startSmallWindow { 47 | delete(l.counters, smallWindow) 48 | } else { 49 | count += counter 50 | } 51 | } 52 | 53 | // 若到达窗口请求上限,请求失败 54 | if count >= l.limit { 55 | return false 56 | } 57 | // 若没到窗口请求上限,当前小窗口计数器+1,请求成功 58 | l.counters[currentSmallWindow]++ 59 | return true 60 | } 61 | -------------------------------------------------------------------------------- /env/env.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strconv" 7 | 8 | "golang.org/x/exp/constraints" 9 | ) 10 | 11 | // 环境变量不存在 12 | var ErrNotPresent = errors.New("env not present") 13 | 14 | // 获取环境变量,可以设置解析和校验函数 15 | func GetEnv[T any](name string, parseAndValidate func(s, name string) (T, error)) (T, error) { 16 | return getEnv(name, parseAndValidate) 17 | } 18 | 19 | // 获取环境变量,字符串 20 | func GetEnvString(name string) (string, error) { 21 | return GetEnv(name, func(s, name string) (string, error) { 22 | return s, nil 23 | }) 24 | } 25 | 26 | // 获取环境变量,数值 27 | func GetEnvNumber[T constraints.Integer](name string) (T, error) { 28 | return GetEnv(name, func(s, name string) (T, error) { 29 | i, err := strconv.Atoi(s) 30 | if err != nil { 31 | return 0, err 32 | } 33 | return T(i), nil 34 | }) 35 | } 36 | 37 | // 获取环境变量,不能不存在或为空 38 | func MustGetEnv[T any](name string, parseAndValidate func(s, name string) (T, error)) T { 39 | t, err := GetEnv(name, parseAndValidate) 40 | if err != nil { 41 | panic(err) 42 | } 43 | return t 44 | } 45 | 46 | // 获取环境变量,不能不存在或为空,字符串 47 | func MustGetEnvString(name string) string { 48 | s, err := GetEnvString(name) 49 | if err != nil { 50 | panic(err) 51 | } 52 | return s 53 | } 54 | 55 | // 获取环境变量,不能不存在或为空,数值 56 | func MustGetEnvNumber[T constraints.Integer](name string) T { 57 | n, err := GetEnvNumber[T](name) 58 | if err != nil { 59 | panic(err) 60 | } 61 | return n 62 | } 63 | 64 | func getEnv[T any](name string, parseAndValidate func(s, name string) (T, error)) (T, error) { 65 | s, ok := os.LookupEnv(name) 66 | if !ok { 67 | var t T 68 | return t, ErrNotPresent 69 | } 70 | return parseAndValidate(s, name) 71 | } 72 | -------------------------------------------------------------------------------- /container/ringbuffer/docs/logic.drawio: -------------------------------------------------------------------------------- 1 | 7ZrNcqM4EICfhmMoBAibY5zM7hxmqrKVw8wcMci2KjJyCRHbefqVQAIkOeP4N5vZkKoU3YgGdX/dqJV40d1y8zfLVovvtEDEC4Ni40X3XhiCZCR+S8W2VaRKnjNcqCG94hG/IKUMlLbGBaqMgZxSwvHKVOa0LFHODV3GGF2bw2aUmE9dZXPkKB7zjLjaH7jgi1Y7Dke9/ivC84V+MkjS9soy04PVTKpFVtD1QBV98aI7Rilvz5abO0Sk67Rf2vv+euVq92IMlfwtN/z8+q1e39zc3r7Mqh//fIdTWsAb2Fp5zkitJqxelm+1B54R41g45Fs2ReSBVphjWopLU8o5XXrRRA+4JXguL3C6EtoFXxIhAHEqZr6SxpabuUTEn2YVzv2ClrV48UlDSgjlOM7oU+fm5s4nxHMtMFqXBSqUNMOE3FFCWfOW0QzKn87I4ErSHPIOWvKBvj2EXrlAzAJtXvUt6CImQEd0iTjbiiHqhjhWQVaQQ43vumcGBEq3GPKilZnidN7Z7kMpTlQ0D4hssj+yqCxuZYoIqaQlMoMmZs62P4UQ+GMYacWvRgFgqBX30gNBJ22H0gNiWEwGMa3cYN5aBHGq5F/qCUDJvT0pbAeCbS2v2XNHgwHK4Wi0rkGFUwssAIT7aM1ytD+jeMbmiO8b5wI1AAbu4EXrGCIZx8/m6+5iSD3hgWIxkZ5XEVGD1xSaJtppqruGRcUy1IGuDcUWz60fHEMN0t20j6c8PZVyheRN4AdBaDAZp+nBTHZJA4Q5YCZNnCbHJM0n5idgnpiYJwEUcekPy+CboberfXJd6HXOnaO2K8p7TFOxQDuptivOdR591vVrAh+nfmKxmfrRkZhDG/PUH10XdHA+0AG06jGIx6eBrpZFg0VM/An7Vat77KdOfT+2pkfQt9ZEQgPSwXFV9EdvIJ8Q0QVL4tcLzNHjKmtiuhY9lpkFWbVqW+MZ3kjYDmu09gF3WuOUQNvpnt04/Q6hs7dNINzveN3PzgjaqNozGZShnGSVaHDNGOj5hMFRuS3Md+EK/cQ82utq7yTyx3FkHAeUhvdKZTi2PjVjK75HL81sQ5f+YrkbKrh0M1cEotlOsfJUbaDkIijyC+HsrCxxUUgbE4Yq/JJNG3sSqJWcTzNDOPHgvdAQaX6S5U/zJps1U+oT+Wrmqm00ZdrrNq+GpPwmb17Nc9lfAZieBoseQmezCl0mfG/YNfnM/vPvRsS72jL9UY+DczRt8cisDAm4cmVwv+i05v+L0pD8EaVh7MQPONETPuOXC1xWc1q1eQ4utPwau8uvcEfihxdbfrkbem4F/nBeji0vJ+/s5dDdQIr+PC/vaCWu62V39yL++F62KwZ4by+7DRv8+F5O/2tejhwvJx/ey51XL18xhNj/pb9dkvT/LRF9+Rc= -------------------------------------------------------------------------------- /slices/reduce_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func TestReduce(t *testing.T) { 10 | type args struct { 11 | slice []int 12 | reduce func(total int, item int, index int, slice []int) int 13 | init int 14 | } 15 | tests := []struct { 16 | name string 17 | args args 18 | want int 19 | }{ 20 | { 21 | name: "number1", 22 | args: args{ 23 | slice: []int{3, 4, 5, 3}, 24 | reduce: func(total int, item int, index int, slice []int) int { 25 | return total + item 26 | }, 27 | init: 0, 28 | }, 29 | want: 15, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := Reduce(tt.args.slice, tt.args.reduce, tt.args.init); !reflect.DeepEqual(got, tt.want) { 35 | t.Errorf("Reduce() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | 41 | func TestReduce2(t *testing.T) { 42 | type args struct { 43 | slice []int 44 | reduce func(total string, item int, index int, slice []int) string 45 | init string 46 | } 47 | tests := []struct { 48 | name string 49 | args args 50 | want string 51 | }{ 52 | { 53 | name: "numberToString", 54 | args: args{ 55 | slice: []int{1, 2, 3, 4, 5}, 56 | reduce: func(total string, item int, index int, slice []int) string { 57 | return total + strconv.Itoa(item) 58 | }, 59 | init: "", 60 | }, 61 | want: "12345", 62 | }, 63 | } 64 | for _, tt := range tests { 65 | t.Run(tt.name, func(t *testing.T) { 66 | if got := Reduce(tt.args.slice, tt.args.reduce, tt.args.init); !reflect.DeepEqual(got, tt.want) { 67 | t.Errorf("Reduce() = %v, want %v", got, tt.want) 68 | } 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /validate/string.go: -------------------------------------------------------------------------------- 1 | package validate 2 | 3 | // 字符长度在范围内 4 | func Len(s string, min, max int, errFunc func(min, max int) error) error { 5 | if len(s) < min || len(s) > max { 6 | return errFunc(min, max) 7 | } 8 | return nil 9 | } 10 | 11 | // 字符串转字符集 12 | func StringToCharset(s string) map[rune]struct{} { 13 | m := map[rune]struct{}{} 14 | for _, c := range s { 15 | m[c] = struct{}{} 16 | } 17 | return m 18 | } 19 | 20 | // 字符串在字符集里面 21 | func InCharset(s string, charset map[rune]struct{}, errFunc func(invalidChar rune) error) error { 22 | for _, c := range s { 23 | if _, ok := charset[c]; !ok { 24 | return errFunc(c) 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | // 字符串包含字符集 31 | func IncludeCharset(s string, charset map[rune]struct{}, errFunc func() error) error { 32 | if !includeCharset(s, charset) { 33 | return errFunc() 34 | } 35 | return nil 36 | } 37 | 38 | func includeCharset(s string, charset map[rune]struct{}) bool { 39 | for _, c := range s { 40 | if _, ok := charset[c]; ok { 41 | return true 42 | } 43 | } 44 | return false 45 | } 46 | 47 | // 字符串包含所有字符集 48 | func IncludeCharsets(s string, charsets []map[rune]struct{}, errFunc func(notIncludedCharset map[rune]struct{}) error) error { 49 | for _, charset := range charsets { 50 | if !includeCharset(s, charset) { 51 | return errFunc(charset) 52 | } 53 | } 54 | return nil 55 | } 56 | 57 | // 字符串包含字符集数量 58 | func IncludeCharsetsCount(s string, min, max int, charsets []map[rune]struct{}, errFunc func(min, max, count int) error) error { 59 | cnt := 0 60 | for _, charset := range charsets { 61 | if includeCharset(s, charset) { 62 | cnt++ 63 | } 64 | } 65 | if cnt < min || cnt > max { 66 | return errFunc(min, max, cnt) 67 | } 68 | return nil 69 | } 70 | -------------------------------------------------------------------------------- /math/math.go: -------------------------------------------------------------------------------- 1 | package math 2 | 3 | import ( 4 | "math" 5 | "math/bits" 6 | 7 | "golang.org/x/exp/constraints" 8 | ) 9 | 10 | // 把数n分成m份 11 | // 返回每一份大小和最后一份的大小 12 | func Split(n, m uint) (uint, uint) { 13 | per := (n + m - 1) / m 14 | last := n % per 15 | if last == 0 { 16 | last = per 17 | } 18 | return per, last 19 | } 20 | 21 | // 最大值 22 | func Max[T constraints.Ordered](a, b T) T { 23 | if a > b { 24 | return a 25 | } 26 | return b 27 | } 28 | 29 | // 最小值 30 | func Min[T constraints.Ordered](a, b T) T { 31 | if a < b { 32 | return a 33 | } 34 | return b 35 | } 36 | 37 | // 绝对值 38 | func Abs[T constraints.Float | constraints.Signed](a T) T { 39 | if a < 0 { 40 | return -a 41 | } 42 | return a 43 | } 44 | 45 | // Log2 46 | // 修改更加高效算法 47 | func Log2[T constraints.Integer | constraints.Float](a T) T { 48 | return T(math.Log2(float64(a))) 49 | } 50 | 51 | // IsPowOf2 check if a value is a power of two 52 | // Determine whether some value is a power of two, where zero is 53 | // not considered a power of two. 54 | func IsPowOf2[T constraints.Unsigned](n T) bool { 55 | return n != 0 && ((n & (n - 1)) == 0) 56 | } 57 | 58 | // RoundUpPowOf2 round up to nearest power of two 59 | func RoundUpPowOf2[T constraints.Unsigned](n T) T { 60 | return 1 << FindLastBitSet(n-1) 61 | } 62 | 63 | // RoundDownPowOf2 round down to nearest power of two 64 | func RoundDownPowOf2[T constraints.Unsigned](n T) T { 65 | return 1 << (FindLastBitSet(n) - 1) 66 | } 67 | 68 | // FindLastBitSet find last (most-significant) bit set 69 | // This is defined the same way as ffs. 70 | // Note FindLastBitSet(0) = 0, FindLastBitSet(1) = 1, FindLastBitSet(0x80000000) = 32. 71 | func FindLastBitSet[T constraints.Unsigned](x T) int { 72 | return bits.Len64(uint64(x)) 73 | } 74 | -------------------------------------------------------------------------------- /slices/reduce_right_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import ( 4 | "reflect" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func TestReduceRight(t *testing.T) { 10 | type args struct { 11 | slice []int 12 | reduce func(total int, item int, index int, slice []int) int 13 | init int 14 | } 15 | tests := []struct { 16 | name string 17 | args args 18 | want int 19 | }{ 20 | { 21 | name: "number1", 22 | args: args{ 23 | slice: []int{3, 4, 5, 3}, 24 | reduce: func(total int, item int, index int, slice []int) int { 25 | return total + item 26 | }, 27 | init: 0, 28 | }, 29 | want: 15, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := ReduceRight(tt.args.slice, tt.args.reduce, tt.args.init); !reflect.DeepEqual(got, tt.want) { 35 | t.Errorf("ReduceRight() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | 41 | func TestReduceRight2(t *testing.T) { 42 | type args struct { 43 | slice []int 44 | reduce func(total string, item int, index int, slice []int) string 45 | init string 46 | } 47 | tests := []struct { 48 | name string 49 | args args 50 | want string 51 | }{ 52 | { 53 | name: "number1", 54 | args: args{ 55 | slice: []int{1, 2, 3, 4, 5}, 56 | reduce: func(total string, item int, index int, slice []int) string { 57 | return total + strconv.Itoa(item) 58 | }, 59 | init: "", 60 | }, 61 | want: "54321", 62 | }, 63 | } 64 | for _, tt := range tests { 65 | t.Run(tt.name, func(t *testing.T) { 66 | if got := ReduceRight(tt.args.slice, tt.args.reduce, tt.args.init); !reflect.DeepEqual(got, tt.want) { 67 | t.Errorf("ReduceRight() = %v, want %v", got, tt.want) 68 | } 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /timer/timingwheel/timingwheel_test.go: -------------------------------------------------------------------------------- 1 | package timingwheel 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func genD(i int) time.Duration { 10 | return time.Duration(i%10000) * time.Millisecond 11 | } 12 | 13 | func BenchmarkTimingWheel(b *testing.B) { 14 | tw := New(1, 20) 15 | go func() { 16 | tw.Run(context.Background()) 17 | }() 18 | 19 | cases := []struct { 20 | name string 21 | N int // the data size (i.e. number of existing timers) 22 | }{ 23 | {"N-1m", 1000000}, 24 | // {"N-5m", 5000000}, 25 | // {"N-10m", 10000000}, 26 | } 27 | for _, c := range cases { 28 | b.Run(c.name, func(b *testing.B) { 29 | base := make([]*Timer, c.N) 30 | for i := 0; i < len(base); i++ { 31 | base[i] = tw.AfterFunc(genD(i), func() {}) 32 | } 33 | b.ResetTimer() 34 | 35 | for i := 0; i < b.N; i++ { 36 | tw.AfterFunc(time.Second, func() {}).Stop() 37 | } 38 | 39 | b.StopTimer() 40 | for i := 0; i < len(base); i++ { 41 | base[i].Stop() 42 | } 43 | }) 44 | } 45 | } 46 | 47 | func BenchmarkStandardTimer(b *testing.B) { 48 | cases := []struct { 49 | name string 50 | N int // the data size (i.e. number of existing timers) 51 | }{ 52 | {"N-1m", 1000000}, 53 | // {"N-5m", 5000000}, 54 | // {"N-10m", 10000000}, 55 | } 56 | for _, c := range cases { 57 | b.Run(c.name, func(b *testing.B) { 58 | base := make([]*time.Timer, c.N) 59 | for i := 0; i < len(base); i++ { 60 | base[i] = time.AfterFunc(genD(i), func() {}) 61 | } 62 | b.ResetTimer() 63 | 64 | for i := 0; i < b.N; i++ { 65 | time.AfterFunc(time.Second, func() {}).Stop() 66 | } 67 | 68 | b.StopTimer() 69 | for i := 0; i < len(base); i++ { 70 | base[i].Stop() 71 | } 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /pool/level/pool_test.go: -------------------------------------------------------------------------------- 1 | package level 2 | 3 | import ( 4 | "math/rand" 5 | "sync/atomic" 6 | "testing" 7 | 8 | "github.com/jiaxwu/gommon/math" 9 | 10 | "github.com/jiaxwu/gommon/pool" 11 | ) 12 | 13 | const ( 14 | blocks = 64 15 | blockSize = 1024 16 | ) 17 | 18 | var block = make([]byte, blockSize) 19 | 20 | func BenchmarkBytePoolRandomBlcoks(b *testing.B) { 21 | var eqCap int64 22 | pool := pool.NewBytePool(0, 0) 23 | for n := 0; n < b.N; n++ { 24 | blocks := rand.Intn(blocks) + 1 25 | needCap := blocks * blockSize 26 | b := pool.Get() 27 | if cap(b) >= needCap && cap(b) <= needCap*2 { 28 | atomic.AddInt64(&eqCap, 1) 29 | } 30 | for i := 0; i < blocks; i++ { 31 | b = append(b, block...) 32 | } 33 | pool.Put(b) 34 | } 35 | b.Logf("eq cap cnt: %d", eqCap) 36 | } 37 | 38 | func BenchmarkLevelBytePoolRandomBlcoks(b *testing.B) { 39 | var eqCap int64 40 | var pool = NewBytePool(blockSize, math.Log2(blocks)+1) 41 | for n := 0; n < b.N; n++ { 42 | blocks := rand.Intn(blocks) + 1 43 | needCap := blocks * blockSize 44 | b := pool.Get(needCap) 45 | if cap(b) >= needCap && cap(b) <= needCap*2 { 46 | atomic.AddInt64(&eqCap, 1) 47 | } 48 | for i := 0; i < blocks; i++ { 49 | b = append(b, block...) 50 | } 51 | pool.Put(b) 52 | } 53 | b.Logf("eq cap cnt: %d", eqCap) 54 | } 55 | 56 | func BenchmarkLevelBufferPoolRandomBlcoks(b *testing.B) { 57 | var eqCap int64 58 | var pool = NewBufferPool(blockSize, math.Log2(blocks)+1) 59 | for n := 0; n < b.N; n++ { 60 | blocks := rand.Intn(blocks) + 1 61 | needCap := blocks * blockSize 62 | b := pool.Get(needCap) 63 | if b.Cap() >= needCap && b.Cap() <= needCap*2 { 64 | atomic.AddInt64(&eqCap, 1) 65 | } 66 | for i := 0; i < blocks; i++ { 67 | b.Write(block) 68 | } 69 | pool.Put(b) 70 | } 71 | b.Logf("eq cap cnt: %d", eqCap) 72 | } 73 | -------------------------------------------------------------------------------- /limiter/sliding_window_limiter_test.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewSlidingWindowLimiter(t *testing.T) { 9 | type args struct { 10 | limit int 11 | window time.Duration 12 | smallWindow time.Duration 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want *SlidingWindowLimiter 18 | wantErr bool 19 | }{ 20 | { 21 | name: "60_5seconds", 22 | args: args{ 23 | limit: 60, 24 | window: time.Second * 5, 25 | smallWindow: time.Second, 26 | }, 27 | want: nil, 28 | }, 29 | } 30 | for _, tt := range tests { 31 | t.Run(tt.name, func(t *testing.T) { 32 | l, err := NewSlidingWindowLimiter(tt.args.limit, tt.args.window, tt.args.smallWindow) 33 | if err != nil { 34 | t.Errorf("NewSlidingWindowLimiter() error = %v", err) 35 | return 36 | } 37 | successCount := 0 38 | for i := 0; i < tt.args.limit/2; i++ { 39 | if l.TryAcquire() { 40 | successCount++ 41 | } 42 | } 43 | if successCount != tt.args.limit/2 { 44 | t.Errorf("NewSlidingWindowLimiter() got = %v, want %v", successCount, tt.args.limit/2) 45 | return 46 | } 47 | 48 | time.Sleep(time.Second * 2) 49 | successCount = 0 50 | for i := 0; i < tt.args.limit-tt.args.limit/2; i++ { 51 | if l.TryAcquire() { 52 | successCount++ 53 | } 54 | } 55 | if successCount != tt.args.limit-tt.args.limit/2 { 56 | t.Errorf("NewSlidingWindowLimiter() got = %v, want %v", successCount, tt.args.limit-tt.args.limit/2) 57 | } 58 | 59 | time.Sleep(time.Second * 3) 60 | successCount = 0 61 | for i := 0; i < tt.args.limit/2; i++ { 62 | if l.TryAcquire() { 63 | successCount++ 64 | } 65 | } 66 | if successCount != tt.args.limit/2 { 67 | t.Errorf("NewSlidingWindowLimiter() got = %v, want %v", successCount, tt.args.limit/2) 68 | return 69 | } 70 | 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /cache/random/random_test.go: -------------------------------------------------------------------------------- 1 | package random 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | // random_test.go:49: cachePercentage=0.1%, count=206048, hitCount=26140, hitRate=12.69% 10 | // random_test.go:49: cachePercentage=0.3%, count=206048, hitCount=55157, hitRate=26.77% 11 | // random_test.go:49: cachePercentage=0.5%, count=206048, hitCount=80166, hitRate=38.91% 12 | // random_test.go:49: cachePercentage=0.7%, count=206048, hitCount=100820, hitRate=48.93% 13 | // random_test.go:49: cachePercentage=1.0%, count=206048, hitCount=124787, hitRate=60.56% 14 | // random_test.go:49: cachePercentage=2.0%, count=206048, hitCount=168247, hitRate=81.65% 15 | // random_test.go:49: cachePercentage=3.0%, count=206048, hitCount=181681, hitRate=88.17% 16 | // random_test.go:49: cachePercentage=5.0%, count=206048, hitCount=191012, hitRate=92.70% 17 | // random_test.go:49: cachePercentage=10.0%, count=206048, hitCount=192842, hitRate=93.59% 18 | func TestHitRate(t *testing.T) { 19 | dataset, err := os.ReadFile("../dataset") 20 | if err != nil { 21 | t.Errorf("read dataset error %v", err) 22 | } 23 | reqs := strings.Split(string(dataset), ",") 24 | testHitRate(t, reqs, 0.001) 25 | testHitRate(t, reqs, 0.003) 26 | testHitRate(t, reqs, 0.005) 27 | testHitRate(t, reqs, 0.007) 28 | testHitRate(t, reqs, 0.01) 29 | testHitRate(t, reqs, 0.02) 30 | testHitRate(t, reqs, 0.03) 31 | testHitRate(t, reqs, 0.05) 32 | testHitRate(t, reqs, 0.1) 33 | } 34 | 35 | func testHitRate(t *testing.T, reqs []string, cachePercentage float64) { 36 | count := len(reqs) 37 | n := int(float64(count) * cachePercentage) 38 | c := New[string, int](n) 39 | hitCount := 0 40 | for _, req := range reqs { 41 | _, exists := c.Get(req) 42 | if exists { 43 | hitCount++ 44 | } else { 45 | c.Put(req, 0) 46 | } 47 | } 48 | hitRate := float64(hitCount) / float64(count) 49 | t.Logf("cachePercentage=%.1f%%, count=%v, hitCount=%v, hitRate=%.2f%%", cachePercentage*100, count, hitCount, hitRate*100) 50 | // t.Logf("|%.1f%%| %v|%.2f%%|", cachePercentage*100, hitCount, hitRate*100) 51 | } 52 | -------------------------------------------------------------------------------- /container/ringbuffer/round/round_test.go: -------------------------------------------------------------------------------- 1 | package round 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jiaxwu/gommon/container/ringbuffer/fix" 7 | ) 8 | 9 | func TestRingAll(t *testing.T) { 10 | n := 10 11 | r := New[int](uint64(n)) 12 | for i := 1; i <= n; i++ { 13 | r.Push(i) 14 | } 15 | 16 | v := r.Peek() 17 | if v != 1 { 18 | t.Errorf("Peek() = %v, want %v", v, 1) 19 | } 20 | 21 | i := 1 22 | for !r.Empty() { 23 | v := r.Pop() 24 | if i != v { 25 | t.Errorf("Pop() = %v, want %v", v, i) 26 | } 27 | i++ 28 | } 29 | } 30 | 31 | func TestRingMAll(t *testing.T) { 32 | n := 10 33 | r := New[int](uint64(n)) 34 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 35 | 36 | v := r.Peek() 37 | if v != 1 { 38 | t.Errorf("Peek() = %v, want %v", v, 1) 39 | } 40 | 41 | i := 1 42 | for !r.Empty() { 43 | v := r.MPop(1) 44 | if i != v[0] { 45 | t.Errorf("Pop() = %v, want %v", v, i) 46 | } 47 | i++ 48 | } 49 | 50 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 51 | i = 1 52 | for !r.Empty() { 53 | v := r.MPop(1) 54 | if i != v[0] { 55 | t.Errorf("Pop() = %v, want %v", v, i) 56 | } 57 | i++ 58 | } 59 | 60 | r.Reset() 61 | r.MPush(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 62 | i = 1 63 | dst := make([]int, 1) 64 | for !r.Empty() { 65 | r.MPopCopy(dst) 66 | if i != dst[0] { 67 | t.Errorf("Pop() = %v, want %v", v, i) 68 | } 69 | i++ 70 | } 71 | } 72 | 73 | const ( 74 | RoundFixSize = 100000 75 | ) 76 | 77 | func BenchmarkRoundPushPop(b *testing.B) { 78 | for i := 0; i < b.N; i++ { 79 | r := New[int](RoundFixSize) 80 | for j := 0; j < RoundFixSize; j++ { 81 | r.Push(j) 82 | } 83 | for j := 0; j < RoundFixSize; j++ { 84 | r.Pop() 85 | } 86 | } 87 | } 88 | 89 | func BenchmarkFixPushPop(b *testing.B) { 90 | for i := 0; i < b.N; i++ { 91 | r := fix.New[int](RoundFixSize) 92 | for j := 0; j < RoundFixSize; j++ { 93 | r.Push(j) 94 | } 95 | for j := 0; j < RoundFixSize; j++ { 96 | r.Pop() 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /container/meap/meap_test.go: -------------------------------------------------------------------------------- 1 | package meap 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAll(t *testing.T) { 8 | h := New(func(e1, e2 Entry[int, int]) bool { 9 | return e1.Value > e2.Value 10 | }) 11 | h.Push(1, 5) 12 | h.Push(2, 6) 13 | h.Push(3, 3) 14 | h.Push(4, 7) 15 | h.Push(5, 2) 16 | h.Push(6, 4) 17 | h.Push(7, 8) 18 | h.Push(8, 9) 19 | h.Push(9, 1) 20 | 21 | e := h.Peek() 22 | if e.Value != 9 { 23 | t.Errorf("Peek() = %v, want %v", e.Value, 9) 24 | } 25 | 26 | i := 9 27 | for !h.Empty() { 28 | e := h.Pop() 29 | if i != e.Value { 30 | t.Errorf("Pop() = %v, want %v", e.Value, i) 31 | } 32 | i-- 33 | } 34 | 35 | h.Push(9, 1) 36 | h.Push(9, 2) 37 | e = h.Peek() 38 | if e.Value != 2 { 39 | t.Errorf("Peek() = %v, want %v", e.Value, 2) 40 | } 41 | } 42 | 43 | func TestRemove(t *testing.T) { 44 | h := New(func(e1, e2 Entry[int, int]) bool { 45 | return e1.Value > e2.Value 46 | }) 47 | h.Push(1, 5) 48 | h.Push(2, 6) 49 | h.Push(3, 3) 50 | h.Push(4, 7) 51 | h.Push(5, 2) 52 | h.Push(6, 4) 53 | h.Push(7, 8) 54 | h.Push(8, 9) 55 | h.Push(9, 1) 56 | 57 | e := h.Peek() 58 | if e.Value != 9 { 59 | t.Errorf("Peek() = %v, want %v", e.Value, 9) 60 | } 61 | 62 | v, ok := h.Get(5) 63 | if !ok || v != 2 { 64 | t.Errorf("Get() = %v, want %v", v, 2) 65 | } 66 | 67 | h.Remove(5) 68 | 69 | v, ok = h.Get(5) 70 | if ok || v != 0 { 71 | t.Errorf("Get() = %v, want %v", v, 0) 72 | } 73 | 74 | i := 9 75 | for !h.Empty() { 76 | e := h.Pop() 77 | if i != e.Value { 78 | t.Errorf("Pop() = %v, want %v", e.Value, i) 79 | } 80 | if i == 3 { 81 | i-- 82 | } 83 | i-- 84 | } 85 | } 86 | 87 | func Fuzz(f *testing.F) { 88 | seeds := [][]int{{1, 2}, {4, 6}, {3, 2}} 89 | for _, seed := range seeds { 90 | f.Add(seed[0], seed[1]) 91 | } 92 | h := New(func(e1, e2 Entry[int, int]) bool { 93 | return e1.Value > e2.Value 94 | }) 95 | m := map[int]bool{} 96 | f.Fuzz(func(t *testing.T, key, value int) { 97 | if m[key] { 98 | h.Remove(key) 99 | delete(m, key) 100 | return 101 | } 102 | m[key] = true 103 | h.Push(key, value) 104 | }) 105 | } 106 | -------------------------------------------------------------------------------- /slices/equal_test.go: -------------------------------------------------------------------------------- 1 | package slices 2 | 3 | import "testing" 4 | 5 | func TestEqual(t *testing.T) { 6 | type args struct { 7 | slice1 []int 8 | slice2 []int 9 | } 10 | tests := []struct { 11 | name string 12 | args args 13 | want bool 14 | }{ 15 | { 16 | name: "number1", 17 | args: args{ 18 | slice1: []int{1, 2, 3}, 19 | slice2: []int{1, 2, 3}, 20 | }, 21 | want: true, 22 | }, 23 | { 24 | name: "number2", 25 | args: args{ 26 | slice1: []int{1, 2, 3}, 27 | slice2: []int{2, 2, 3}, 28 | }, 29 | want: false, 30 | }, 31 | { 32 | name: "number3", 33 | args: args{ 34 | slice1: []int{1, 2, 3}, 35 | slice2: []int{1, 2, 3, 4}, 36 | }, 37 | want: false, 38 | }, 39 | } 40 | for _, tt := range tests { 41 | t.Run(tt.name, func(t *testing.T) { 42 | if got := Equal(tt.args.slice1, tt.args.slice2); got != tt.want { 43 | t.Errorf("Equal() = %v, want %v", got, tt.want) 44 | } 45 | }) 46 | } 47 | } 48 | 49 | func TestEqualFunc(t *testing.T) { 50 | type args struct { 51 | slice1 []int 52 | slice2 []int 53 | f func(item1, item2 int) bool 54 | } 55 | tests := []struct { 56 | name string 57 | args args 58 | want bool 59 | }{ 60 | { 61 | name: "number1", 62 | args: args{ 63 | slice1: []int{1, 2, 3}, 64 | slice2: []int{1, 2, 3}, 65 | f: func(item1, item2 int) bool { 66 | return item1 == item2 67 | }, 68 | }, 69 | want: true, 70 | }, 71 | { 72 | name: "number2", 73 | args: args{ 74 | slice1: []int{1, 2, 3}, 75 | slice2: []int{2, 2, 3}, 76 | f: func(item1, item2 int) bool { 77 | return item1 == item2 78 | }, 79 | }, 80 | want: false, 81 | }, 82 | { 83 | name: "number3", 84 | args: args{ 85 | slice1: []int{1, 2, 3}, 86 | slice2: []int{1, 2, 3, 4}, 87 | f: func(item1, item2 int) bool { 88 | return item1 == item2 89 | }, 90 | }, 91 | want: false, 92 | }, 93 | } 94 | for _, tt := range tests { 95 | t.Run(tt.name, func(t *testing.T) { 96 | if got := EqualFunc(tt.args.slice1, tt.args.slice2, tt.args.f); got != tt.want { 97 | t.Errorf("EqualFunc() = %v, want %v", got, tt.want) 98 | } 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /filter/bloom/bloom_test.go: -------------------------------------------------------------------------------- 1 | package bloom 2 | 3 | import ( 4 | "encoding/binary" 5 | "hash/fnv" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | func TestNew(t *testing.T) { 11 | f := New(10, 0.01) 12 | f.AddString("10") 13 | f.AddString("44") 14 | f.AddString("66") 15 | if !f.ContainsString("10") { 16 | t.Errorf("want %v, but %v", true, f.ContainsString("10")) 17 | } 18 | if !f.ContainsString("44") { 19 | t.Errorf("want %v, but %v", true, f.ContainsString("10")) 20 | } 21 | if !f.ContainsString("66") { 22 | t.Errorf("want %v, but %v", true, f.ContainsString("10")) 23 | } 24 | if f.ContainsString("55") { 25 | t.Errorf("want %v, but %v", false, f.ContainsString("10")) 26 | } 27 | } 28 | 29 | func TestFalsePositiveRate(t *testing.T) { 30 | capacity := uint64(10000000) 31 | rounds := uint64(10000000) 32 | falsePositiveRate := 0.01 33 | f := New(capacity, falsePositiveRate) 34 | // 加入过滤器一些元素 35 | for i := uint64(0); i < capacity; i++ { 36 | h := fnv.New64() 37 | b := make([]byte, 8) 38 | binary.BigEndian.PutUint64(b, i) 39 | h.Write(b) 40 | f.Add(h.Sum64()) 41 | } 42 | // 查询不存在的元素,计算错误率 43 | falsePositiveCount := 0 44 | for i := uint64(0); i < rounds; i++ { 45 | // 加上容量保证这个元素一定不是之前加入过滤器的 46 | h := fnv.New64() 47 | b := make([]byte, 8) 48 | binary.BigEndian.PutUint64(b, i+capacity+1) 49 | h.Write(b) 50 | if f.Contains(h.Sum64()) { 51 | falsePositiveCount++ 52 | } 53 | } 54 | t.Log(falsePositiveCount) 55 | fpRate := float64(falsePositiveCount) / (float64(rounds)) 56 | if !(fpRate >= falsePositiveRate*(0.9) && fpRate <= falsePositiveRate*(1.1)) { 57 | t.Errorf("fpRate not accuracy %v", fpRate) 58 | } 59 | } 60 | 61 | func FuzzAddAndContains(f *testing.F) { 62 | seeds := []string{"abc", "bbb", "0", "1", ""} 63 | for _, seed := range seeds { 64 | f.Add(seed) 65 | } 66 | bf := New(1000000, 0.01) 67 | f.Fuzz(func(t *testing.T, a string) { 68 | bf.AddString(a) 69 | if !bf.ContainsString(a) { 70 | t.Errorf("want %v, but %v", true, false) 71 | } 72 | }) 73 | } 74 | 75 | func BenchmarkAddAndContains(b *testing.B) { 76 | buf := make([]byte, 8192) 77 | for length := 1; length <= cap(buf); length *= 2 { 78 | b.Run(strconv.Itoa(length), func(b *testing.B) { 79 | f := New(uint64(b.N), 0.0001) 80 | buf = buf[:length] 81 | b.SetBytes(int64(length)) 82 | b.ReportAllocs() 83 | b.ResetTimer() 84 | for i := 0; i < b.N; i++ { 85 | f.AddBytes(buf) 86 | f.ContainsBytes(buf) 87 | } 88 | }) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /timer/timingwheel/bucket.go: -------------------------------------------------------------------------------- 1 | package timingwheel 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | "unsafe" 7 | 8 | "github.com/jiaxwu/gommon/container/list" 9 | ) 10 | 11 | // 返回给用户的定时器 12 | type Timer struct { 13 | expiration int64 // 到期时间 14 | task func() // 任务 15 | b unsafe.Pointer // 所属时间轮的桶 16 | elem *list.Element[*Timer] // 为了能从链表中删除 17 | } 18 | 19 | func (t *Timer) Stop() bool { 20 | stoped := false 21 | for b := t.getBucket(); b != nil; b = t.getBucket() { 22 | stoped = b.remove(t) 23 | } 24 | return stoped 25 | } 26 | 27 | func (t *Timer) getBucket() *bucket { 28 | return (*bucket)(atomic.LoadPointer(&t.b)) 29 | } 30 | 31 | func (t *Timer) setBucket(b *bucket) { 32 | atomic.StorePointer(&t.b, unsafe.Pointer(b)) 33 | } 34 | 35 | // 时间轮上的一个桶 36 | // 会带着一组相同范围过期时间的元素 37 | type bucket struct { 38 | expiration int64 // 到期时间 39 | timers *list.List[*Timer] // 定时器列表 40 | mutex sync.Mutex 41 | } 42 | 43 | // 创建定时器 44 | func newBucket() *bucket { 45 | return &bucket{ 46 | expiration: -1, 47 | timers: list.New[*Timer](), 48 | } 49 | } 50 | 51 | // 添加定时器 52 | // 会设置定时器的elem和bucket 53 | // 表示这个定时器所属的桶 54 | func (b *bucket) add(t *Timer) { 55 | b.mutex.Lock() 56 | defer b.mutex.Unlock() 57 | elem := b.timers.PushBack(t) 58 | t.elem = elem 59 | t.setBucket(b) 60 | } 61 | 62 | // 移除定时器 63 | func (b *bucket) remove(t *Timer) bool { 64 | b.mutex.Lock() 65 | defer b.mutex.Unlock() 66 | if t.getBucket() != b { 67 | return false 68 | } 69 | b.timers.Remove(t.elem) 70 | t.setBucket(nil) 71 | t.elem = nil 72 | return true 73 | } 74 | 75 | // 添加到上一级定时器或执行任务 76 | func (b *bucket) flush(addOrRun func(t *Timer)) { 77 | b.mutex.Lock() 78 | defer b.mutex.Unlock() 79 | 80 | for elem := b.timers.Front(); elem != nil; { 81 | next := elem.Next() 82 | t := elem.Value 83 | if t.getBucket() == b { 84 | t.setBucket(nil) 85 | t.elem = nil 86 | } 87 | addOrRun(t) 88 | elem = next 89 | } 90 | 91 | // 设置过期时间表示没有加入到延迟队列 92 | b.setExpiration(-1) 93 | b.timers.Clear() 94 | } 95 | 96 | func (b *bucket) getExpiration() int64 { 97 | return atomic.LoadInt64(&b.expiration) 98 | } 99 | 100 | // 返回true表示设置成功 101 | // 否则表示没变化 102 | func (b *bucket) setExpiration(expiration int64) bool { 103 | return atomic.SwapInt64(&b.expiration, expiration) != expiration 104 | } 105 | -------------------------------------------------------------------------------- /container/heap/heapn.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | // n叉堆 4 | type HeapN[T any] struct { 5 | n int 6 | h []T 7 | lessFunc LessFunc[T] 8 | } 9 | 10 | func NewN[T any](n int, lessFunc LessFunc[T]) *HeapN[T] { 11 | if n < 2 { 12 | panic("n must be greater than 2") 13 | } 14 | return &HeapN[T]{ 15 | n: n, 16 | lessFunc: lessFunc, 17 | } 18 | } 19 | 20 | // 移除堆顶元素 21 | func (h *HeapN[T]) Pop() T { 22 | n := h.Len() - 1 23 | h.swap(0, n) 24 | h.down(0, n) 25 | return h.pop() 26 | } 27 | 28 | // 获取堆顶元素 29 | func (h *HeapN[T]) Peek() T { 30 | return h.h[0] 31 | } 32 | 33 | // 添加元素到堆 34 | func (h *HeapN[T]) Push(x T) { 35 | h.push(x) 36 | h.up(h.Len() - 1) 37 | } 38 | 39 | // 堆长度 40 | func (h *HeapN[T]) Len() int { 41 | return len(h.h) 42 | } 43 | 44 | // 堆是否为空 45 | func (h *HeapN[T]) Empty() bool { 46 | return h.Len() == 0 47 | } 48 | 49 | // Remove removes and returns the element at index i from the heap. 50 | // The complexity is O(log n) where n = h.Len(). 51 | func (h *HeapN[T]) Remove(i int) T { 52 | n := h.Len() - 1 53 | if n != i { 54 | h.swap(i, n) 55 | if !h.down(i, n) { 56 | h.up(i) 57 | } 58 | } 59 | return h.pop() 60 | } 61 | 62 | // Fix re-establishes the heap ordering after the element at index i has changed its value. 63 | // Changing the value of the element at index i and then calling Fix is equivalent to, 64 | // but less expensive than, calling Remove(h, i) followed by a Push of the new value. 65 | // The complexity is O(log n) where n = h.Len(). 66 | func (h *HeapN[T]) Fix(i int) { 67 | if !h.down(i, h.Len()) { 68 | h.up(i) 69 | } 70 | } 71 | 72 | func (h *HeapN[T]) up(j int) { 73 | for { 74 | i := (j - 1) / h.n // parent 75 | if i == j || !h.less(j, i) { 76 | break 77 | } 78 | h.swap(i, j) 79 | j = i 80 | } 81 | } 82 | 83 | func (h *HeapN[T]) down(i0, n int) bool { 84 | i := i0 85 | for { 86 | k := h.n*i + 1 87 | if k >= n || k < 0 { // j1 < 0 after int overflow 88 | break 89 | } 90 | j := k // first child 91 | for j1 := k + 1; j1 < k+4 && j1 < n; j1++ { 92 | if h.less(j1, j) { 93 | j = j1 94 | } 95 | } 96 | if !h.less(j, i) { 97 | break 98 | } 99 | h.swap(i, j) 100 | i = j 101 | } 102 | return i > i0 103 | } 104 | 105 | func (h *HeapN[T]) less(i, j int) bool { 106 | return h.lessFunc(h.h[i], h.h[j]) 107 | } 108 | 109 | func (h *HeapN[T]) swap(i, j int) { 110 | h.h[i], h.h[j] = h.h[j], h.h[i] 111 | } 112 | 113 | func (h *HeapN[T]) push(x T) { 114 | h.h = append(h.h, x) 115 | } 116 | 117 | func (h *HeapN[T]) pop() T { 118 | elem := h.h[h.Len()-1] 119 | h.h = h.h[:h.Len()-1] 120 | return elem 121 | } 122 | -------------------------------------------------------------------------------- /container/reap/reap_test.go: -------------------------------------------------------------------------------- 1 | package meap 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/jiaxwu/gommon/container/heap" 7 | "github.com/jiaxwu/gommon/container/meap" 8 | ) 9 | 10 | func TestAll(t *testing.T) { 11 | h := New(func(e1, e2 int) bool { 12 | return e1 > e2 13 | }) 14 | h.Push(5) 15 | h.Push(6) 16 | h.Push(3) 17 | h.Push(7) 18 | h.Push(2) 19 | h.Push(4) 20 | h.Push(8) 21 | h.Push(9) 22 | h.Push(1) 23 | 24 | e := h.Peek() 25 | if e != 9 { 26 | t.Errorf("Peek() = %v, want %v", e, 9) 27 | } 28 | 29 | i := 9 30 | for !h.Empty() { 31 | e := h.Pop() 32 | if i != e { 33 | t.Errorf("Pop() = %v, want %v", e, i) 34 | } 35 | i-- 36 | } 37 | } 38 | 39 | func TestRemove(t *testing.T) { 40 | h := New(func(e1, e2 int) bool { 41 | return e1 > e2 42 | }) 43 | entry := h.Push(5) 44 | h.Push(6) 45 | h.Push(3) 46 | h.Push(7) 47 | h.Push(2) 48 | h.Push(4) 49 | h.Push(8) 50 | h.Push(9) 51 | h.Push(1) 52 | 53 | e := h.Peek() 54 | if e != 9 { 55 | t.Errorf("Peek() = %v, want %v", e, 9) 56 | } 57 | 58 | h.Remove(entry) 59 | 60 | i := 9 61 | for !h.Empty() { 62 | e := h.Pop() 63 | if i != e { 64 | t.Errorf("Pop() = %v, want %v", e, i) 65 | } 66 | if i == 6 { 67 | i-- 68 | } 69 | i-- 70 | } 71 | 72 | entry = h.Push(1) 73 | h.SetValue(entry, 2) 74 | e = h.Peek() 75 | if e != 2 { 76 | t.Errorf("Peek() = %v, want %v", e, 2) 77 | } 78 | } 79 | 80 | func Fuzz(f *testing.F) { 81 | seeds := []int{1, 2, 4, 6, 3, 2} 82 | for _, seed := range seeds { 83 | f.Add(seed) 84 | } 85 | h := New(func(e1, e2 int) bool { 86 | return e1 > e2 87 | }) 88 | m := map[int]*Entry[int]{} 89 | f.Fuzz(func(t *testing.T, value int) { 90 | if m[value] != nil { 91 | h.Remove(m[value]) 92 | delete(m, value) 93 | return 94 | } 95 | m[value] = h.Push(value) 96 | }) 97 | } 98 | 99 | func BenchmarkHeapPushAndPop(b *testing.B) { 100 | q := heap.New(nil, func(e1, e2 int) bool { 101 | return e1 < e2 102 | }) 103 | for i := 0; i < b.N; i++ { 104 | q.Push(i) 105 | } 106 | 107 | for i := 0; i < b.N; i++ { 108 | q.Pop() 109 | } 110 | } 111 | 112 | func BenchmarkReapPushAndPop(b *testing.B) { 113 | q := New(func(e1, e2 int) bool { 114 | return e1 < e2 115 | }) 116 | for i := 0; i < b.N; i++ { 117 | q.Push(i) 118 | } 119 | 120 | for i := 0; i < b.N; i++ { 121 | q.Pop() 122 | } 123 | } 124 | 125 | func BenchmarkMeapPushAndPop(b *testing.B) { 126 | q := meap.New(func(e1, e2 meap.Entry[int, int]) bool { 127 | return e1.Value < e2.Value 128 | }) 129 | for i := 0; i < b.N; i++ { 130 | q.Push(i, i) 131 | } 132 | 133 | for i := 0; i < b.N; i++ { 134 | q.Pop() 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /counter/qps/qps.go: -------------------------------------------------------------------------------- 1 | package qps 2 | 3 | import ( 4 | "log" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | // 窗口信息 10 | type Window struct { 11 | TotalCnt int64 // 总次数 12 | TotalTime time.Duration // 总时间 13 | } 14 | 15 | // 平均耗时 16 | func (w *Window) AvgTime() time.Duration { 17 | return w.TotalTime / time.Duration(w.TotalCnt) 18 | } 19 | 20 | // 基于滑动窗口的QPS统计 21 | type QPS struct { 22 | windowCnt int64 // 窗口数量 23 | windows map[int64]Window // 窗口 24 | mut sync.Mutex // 避免并发问题 25 | } 26 | 27 | // windowCnt: 一秒分为多少个窗口,越细越准确,但是消耗越大,且必须能够把窗口整除 28 | func New(windowCnt int64) *QPS { 29 | // 窗口时间必须能够被窗口数量整除 30 | if time.Second%time.Duration(windowCnt) != 0 { 31 | log.Fatal("window cannot be split by integers") 32 | } 33 | return &QPS{ 34 | windowCnt: windowCnt, 35 | windows: make(map[int64]Window), 36 | } 37 | } 38 | 39 | // 记录QPS 40 | func (q *QPS) Add() { 41 | q.mut.Lock() 42 | defer q.mut.Unlock() 43 | q.add(0) 44 | } 45 | 46 | // 记录QPS和使用时间 47 | func (q *QPS) AddSince(start time.Time) { 48 | q.mut.Lock() 49 | defer q.mut.Unlock() 50 | q.add(time.Since(start)) 51 | } 52 | 53 | // 记录QPS和使用时间 54 | func (q *QPS) AddUseTime(useTime time.Duration) { 55 | q.mut.Lock() 56 | defer q.mut.Unlock() 57 | q.add(useTime) 58 | } 59 | 60 | func (q *QPS) add(useTime time.Duration) { 61 | // 当前窗口计数器+1 62 | curWindowTime := q.curWindowTime() 63 | window := q.windows[curWindowTime] 64 | window.TotalCnt++ 65 | window.TotalTime += useTime 66 | q.windows[curWindowTime] = window 67 | startWindowTime := q.startWindowTime() 68 | // 删除过期窗口 69 | for windowTime := range q.windows { 70 | if windowTime < startWindowTime { 71 | delete(q.windows, windowTime) 72 | } 73 | } 74 | } 75 | 76 | // 获取QPS信息 77 | func (q *QPS) Get() Window { 78 | q.mut.Lock() 79 | defer q.mut.Unlock() 80 | startWindowTime := q.startWindowTime() 81 | // 计算当前窗口的请求总数 82 | var w Window 83 | for windowTime, window := range q.windows { 84 | if windowTime < startWindowTime { 85 | delete(q.windows, windowTime) 86 | } else { 87 | w.TotalCnt += window.TotalCnt 88 | w.TotalTime += window.TotalTime 89 | } 90 | } 91 | return w 92 | } 93 | 94 | // 窗口时间大小 95 | func (q *QPS) WindowSize() time.Duration { 96 | return time.Second / time.Duration(q.windowCnt) 97 | } 98 | 99 | // 当前窗口时间 100 | func (q *QPS) curWindowTime() int64 { 101 | windowSize := int64(q.WindowSize()) 102 | return time.Now().UnixNano() / windowSize * windowSize 103 | } 104 | 105 | // 起始窗口时间 106 | func (q *QPS) startWindowTime() int64 { 107 | windowSize := int64(q.WindowSize()) 108 | return time.Now().UnixNano()/windowSize*windowSize - windowSize*(q.windowCnt-1) 109 | } 110 | -------------------------------------------------------------------------------- /timer/timingwheel/delayqueue.go: -------------------------------------------------------------------------------- 1 | package timingwheel 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/jiaxwu/gommon/container/heap" 10 | ) 11 | 12 | // 延迟队列 13 | type delayQueue struct { 14 | h *heap.Heap[*bucket] 15 | mutex sync.Mutex // 保证并发安全 16 | sleeping int32 // 用于Push()和Take()之间通知是否有需要唤醒 17 | wakeup chan struct{} // 唤醒通道 18 | } 19 | 20 | // 创建延迟队列 21 | func newDelayQueue() *delayQueue { 22 | return &delayQueue{ 23 | h: heap.New(nil, func(b1, b2 *bucket) bool { 24 | return b1.getExpiration() < b2.getExpiration() 25 | }), 26 | wakeup: make(chan struct{}), 27 | } 28 | } 29 | 30 | // 添加延迟元素到队列 31 | func (q *delayQueue) push(b *bucket) { 32 | q.mutex.Lock() 33 | defer q.mutex.Unlock() 34 | q.h.Push(b) 35 | // 唤醒等待的协程 36 | // 这里表示新添加的元素到期时间是最早的,或者原来队列为空 37 | // 因此必须唤醒等待的协程,因为可以拿到更早到期的元素 38 | if q.h.Peek() == b { 39 | if atomic.CompareAndSwapInt32(&q.sleeping, 1, 0) { 40 | q.wakeup <- struct{}{} 41 | } 42 | } 43 | } 44 | 45 | // 等待直到有元素到期 46 | // 或者ctx被关闭 47 | func (q *delayQueue) take(ctx context.Context, nowF func() int64) *bucket { 48 | for { 49 | var t *time.Timer 50 | q.mutex.Lock() 51 | // 有元素 52 | if !q.h.Empty() { 53 | // 获取元素 54 | entry := q.h.Peek() 55 | expiration := entry.getExpiration() 56 | now := nowF() 57 | if now > expiration { 58 | q.h.Pop() 59 | q.mutex.Unlock() 60 | return entry 61 | } 62 | // 到期时间,使用time.NewTimer()才能够调用Stop(),从而释放定时器 63 | t = time.NewTimer(time.Duration(now-expiration) * time.Millisecond) 64 | } 65 | // 走到这里表示需要等待了,则需要告诉Push()在有新元素时要通知 66 | atomic.StoreInt32(&q.sleeping, 1) 67 | q.mutex.Unlock() 68 | 69 | // 不为空,需要同时等待元素到期,并且除非t到期,否则都需要关闭t避免泄露 70 | if t != nil { 71 | select { 72 | case <-q.wakeup: // 新的更快到期元素 73 | t.Stop() 74 | case <-t.C: // 首元素到期 75 | if atomic.SwapInt32(&q.sleeping, 0) == 0 { 76 | // 避免Push()的协程被阻塞 77 | <-q.wakeup 78 | } 79 | case <-ctx.Done(): // 被关闭 80 | t.Stop() 81 | return nil 82 | } 83 | } else { 84 | select { 85 | case <-q.wakeup: // 新的更快到期元素 86 | case <-ctx.Done(): // 被关闭 87 | return nil 88 | } 89 | } 90 | } 91 | } 92 | 93 | // 返回一个通道,输出到期元素 94 | // size是通道缓存大小 95 | func (q *delayQueue) channel(ctx context.Context, size int, nowF func() int64) <-chan *bucket { 96 | out := make(chan *bucket, size) 97 | go func() { 98 | for { 99 | entry := q.take(ctx, nowF) 100 | if entry == nil { 101 | close(out) 102 | return 103 | } 104 | out <- entry 105 | } 106 | }() 107 | return out 108 | } 109 | -------------------------------------------------------------------------------- /consistenthash/consistenthash.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Google Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // Package consistenthash provides an implementation of a ring hash. 15 | 16 | // 基于 https://github.com/golang/groupcache/blob/master/consistenthash/consistenthash_test.go 的实现,添加Reset方法,添加注释 17 | 18 | package consistenthash 19 | 20 | import ( 21 | "hash/crc32" 22 | "sort" 23 | "strconv" 24 | ) 25 | 26 | // 哈希函数 27 | type Hash func(data []byte) uint32 28 | 29 | // 哈希环 30 | // 注意,非线程安全,业务需要自行加锁 31 | type HashRing struct { 32 | hash Hash 33 | // 每个真实节点的虚拟节点数量 34 | replicas int 35 | // 哈希环,按照节点哈希值排序 36 | ring []int 37 | // 节点哈希值到真实节点字符串,哈希映射的逆过程 38 | nodes map[int]string 39 | } 40 | 41 | func New(replicas int, fn Hash) *HashRing { 42 | r := &HashRing{ 43 | replicas: replicas, 44 | hash: fn, 45 | nodes: make(map[int]string), 46 | } 47 | if r.hash == nil { 48 | r.hash = crc32.ChecksumIEEE 49 | } 50 | return r 51 | } 52 | 53 | // 哈希环上是否有节点 54 | func (r *HashRing) Empty() bool { 55 | return len(r.ring) == 0 56 | } 57 | 58 | // 添加新节点到哈希环 59 | // 注意,如果加入的节点已经存在,会导致哈希环上面重复,如果不确定是否存在请使用Reset 60 | func (m *HashRing) Add(nodes ...string) { 61 | for _, node := range nodes { 62 | // 每个节点创建多个虚拟节点 63 | for i := 0; i < m.replicas; i++ { 64 | // 每个虚拟节点计算哈希值 65 | hash := int(m.hash([]byte(strconv.Itoa(i) + node))) 66 | // 加入哈希环 67 | m.ring = append(m.ring, hash) 68 | // 哈希值到真实节点字符串映射 69 | m.nodes[hash] = node 70 | } 71 | } 72 | // 哈希环排序 73 | sort.Ints(m.ring) 74 | } 75 | 76 | // 先清空哈希环再设置 77 | func (r *HashRing) Reset(nodes ...string) { 78 | // 先清空 79 | r.ring = nil 80 | r.nodes = map[int]string{} 81 | // 再重置 82 | r.Add(nodes...) 83 | } 84 | 85 | // 获取Key对应的节点 86 | func (r *HashRing) Get(key string) string { 87 | // 如果哈希环位空,则直接返回 88 | if r.Empty() { 89 | return "" 90 | } 91 | 92 | // 计算Key哈希值 93 | hash := int(r.hash([]byte(key))) 94 | 95 | // 二分查找第一个大于等于Key哈希值的节点 96 | idx := sort.Search(len(r.ring), func(i int) bool { return r.ring[i] >= hash }) 97 | 98 | // 这里是特殊情况,也就是数组没有大于等于Key哈希值的节点 99 | // 但是逻辑上这是一个环,因此第一个节点就是目标节点 100 | if idx == len(r.ring) { 101 | idx = 0 102 | } 103 | 104 | // 返回哈希值对应的真实节点字符串 105 | return r.nodes[r.ring[idx]] 106 | } 107 | -------------------------------------------------------------------------------- /container/ringbuffer/fix/fix.go: -------------------------------------------------------------------------------- 1 | package fix 2 | 3 | import ( 4 | "math" 5 | 6 | mmath "github.com/jiaxwu/gommon/math" 7 | ) 8 | 9 | // 最大长度 10 | const MaxSize = math.MaxInt64 11 | 12 | // 固定长度 13 | // 单生产者和单消费者情况下是线程安全性的,但是不能用Reset()方法 14 | type Ring[T any] struct { 15 | in uint64 // 写索引 16 | out uint64 // 读索引 17 | size uint64 // 长度 18 | data []T // 数据 19 | } 20 | 21 | func New[T any](size uint64) *Ring[T] { 22 | if size == 0 { 23 | panic("size must be greater than 0") 24 | } 25 | if size > MaxSize { 26 | panic("size is too large") 27 | } 28 | 29 | return &Ring[T]{ 30 | size: size, 31 | data: make([]T, size), 32 | } 33 | } 34 | 35 | // 弹出队头元素 36 | func (r *Ring[T]) Pop() T { 37 | if r.Empty() { 38 | panic("ring emtpy") 39 | } 40 | out := r.out % r.size 41 | r.out++ 42 | return r.data[out] 43 | } 44 | 45 | // 队头元素 46 | func (r *Ring[T]) Peek() T { 47 | if r.Empty() { 48 | panic("ring emtpy") 49 | } 50 | return r.data[r.out%r.size] 51 | } 52 | 53 | // 插入元素到队尾 54 | func (r *Ring[T]) Push(e T) { 55 | if r.Full() { 56 | panic("ring full") 57 | } 58 | in := r.in % r.size 59 | r.in++ 60 | r.data[in] = e 61 | } 62 | 63 | // 写入队尾 64 | func (r *Ring[T]) MPush(elems ...T) { 65 | size := uint64(len(elems)) 66 | // 不能大于剩余长度 67 | if size > r.Avail() { 68 | size = r.Avail() 69 | elems = elems[:size] 70 | } 71 | if size == 0 { 72 | return 73 | } 74 | in := r.in % r.size 75 | copied := copy(r.data[in:], elems) 76 | copy(r.data, elems[copied:]) 77 | r.in += size 78 | } 79 | 80 | // 从队头读取 81 | func (r *Ring[T]) MPop(size uint64) []T { 82 | if size > r.Len() { 83 | size = r.Len() 84 | } 85 | if size == 0 { 86 | return nil 87 | } 88 | out := r.out % r.size 89 | elems := make([]T, size) 90 | copied := copy(elems, r.data[out:]) 91 | copy(elems[copied:], r.data) 92 | r.out += size 93 | return elems 94 | } 95 | 96 | // 从队头读取,填充到dst里 97 | func (r *Ring[T]) MPopCopy(dst []T) { 98 | out := r.out % r.size 99 | dst = dst[:mmath.Min(uint64(len(dst)), r.Len())] 100 | copied := copy(dst, r.data[out:]) 101 | copied += copy(dst[copied:], r.data) 102 | r.out += uint64(copied) 103 | } 104 | 105 | // 重置读写指针 106 | func (r *Ring[T]) Reset() { 107 | r.in = 0 108 | r.out = 0 109 | r.data = make([]T, r.size) 110 | } 111 | 112 | // 总长度 113 | func (r *Ring[T]) Cap() uint64 { 114 | return r.size 115 | } 116 | 117 | // 使用长度 118 | func (r *Ring[T]) Len() uint64 { 119 | return r.in - r.out 120 | } 121 | 122 | // 可用长度 123 | func (r *Ring[T]) Avail() uint64 { 124 | return r.Cap() - r.Len() 125 | } 126 | 127 | // 是否为空 128 | func (r *Ring[T]) Empty() bool { 129 | return r.in == r.out 130 | } 131 | 132 | // 是否满了 133 | func (r *Ring[T]) Full() bool { 134 | return r.Avail() == 0 135 | } 136 | -------------------------------------------------------------------------------- /container/ringbuffer/round/round.go: -------------------------------------------------------------------------------- 1 | package round 2 | 3 | import "github.com/jiaxwu/gommon/math" 4 | 5 | // 最大长度 6 | const MaxSize = 1 << 62 7 | 8 | // 固定长度,且长度向上取2的平方 9 | // 单生产者和单消费者情况下是线程安全性的,但是不能用Reset()方法 10 | type Ring[T any] struct { 11 | in uint64 // 写索引 12 | out uint64 // 读索引 13 | mask uint64 // 掩码,用于取索引,代替%size 14 | size uint64 // 长度 15 | data []T // 数据 16 | } 17 | 18 | func New[T any](size uint64) *Ring[T] { 19 | if size == 0 { 20 | panic("size must be greater than 0") 21 | } 22 | size = math.RoundUpPowOf2(size) 23 | if size > MaxSize { 24 | panic("size is too large") 25 | } 26 | 27 | return &Ring[T]{ 28 | size: size, 29 | mask: size - 1, 30 | data: make([]T, size), 31 | } 32 | } 33 | 34 | // 弹出队头元素 35 | func (r *Ring[T]) Pop() T { 36 | if r.Empty() { 37 | panic("ring emtpy") 38 | } 39 | out := r.out & r.mask 40 | r.out++ 41 | return r.data[out] 42 | } 43 | 44 | // 队头元素 45 | func (r *Ring[T]) Peek() T { 46 | if r.Empty() { 47 | panic("ring emtpy") 48 | } 49 | return r.data[r.out%r.size] 50 | } 51 | 52 | // 插入元素到队尾 53 | func (r *Ring[T]) Push(e T) { 54 | if r.Full() { 55 | panic("ring full") 56 | } 57 | in := r.in & r.mask 58 | r.in++ 59 | r.data[in] = e 60 | } 61 | 62 | // 写入队尾 63 | func (r *Ring[T]) MPush(elems ...T) { 64 | size := uint64(len(elems)) 65 | // 不能大于剩余长度 66 | if size > r.Avail() { 67 | size = r.Avail() 68 | elems = elems[:size] 69 | } 70 | if size == 0 { 71 | return 72 | } 73 | in := r.in & r.mask 74 | copied := copy(r.data[in:], elems) 75 | copy(r.data, elems[copied:]) 76 | r.in += size 77 | } 78 | 79 | // 从队头读取 80 | func (r *Ring[T]) MPop(size uint64) []T { 81 | if size > r.Len() { 82 | size = r.Len() 83 | } 84 | if size == 0 { 85 | return nil 86 | } 87 | out := r.out & r.mask 88 | elems := make([]T, size) 89 | copied := copy(elems, r.data[out:]) 90 | copy(elems[copied:], r.data) 91 | r.out += size 92 | return elems 93 | } 94 | 95 | // 从队头读取,填充到dst里 96 | func (r *Ring[T]) MPopCopy(dst []T) { 97 | out := r.out & r.mask 98 | dst = dst[:math.Min(uint64(len(dst)), r.Len())] 99 | copied := copy(dst, r.data[out:]) 100 | copied += copy(dst[copied:], r.data) 101 | r.out += uint64(copied) 102 | } 103 | 104 | // 重置读写指针 105 | func (r *Ring[T]) Reset() { 106 | r.in = 0 107 | r.out = 0 108 | r.data = make([]T, r.size) 109 | } 110 | 111 | // 总长度 112 | func (r *Ring[T]) Cap() uint64 { 113 | return r.size 114 | } 115 | 116 | // 使用长度 117 | func (r *Ring[T]) Len() uint64 { 118 | return r.in - r.out 119 | } 120 | 121 | // 可用长度 122 | func (r *Ring[T]) Avail() uint64 { 123 | return r.Cap() - r.Len() 124 | } 125 | 126 | // 是否为空 127 | func (r *Ring[T]) Empty() bool { 128 | return r.in == r.out 129 | } 130 | 131 | // 是否满了 132 | func (r *Ring[T]) Full() bool { 133 | return r.Avail() == 0 134 | } 135 | -------------------------------------------------------------------------------- /filter/bloom/bloom.go: -------------------------------------------------------------------------------- 1 | package bloom 2 | 3 | import ( 4 | "hash/fnv" 5 | "math" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | // uint64的位数 11 | const uint64Bits = 64 12 | 13 | // 布隆过滤器 14 | // https://llimllib.github.io/bloomfilter-tutorial/ 15 | // https://github.com/bits-and-blooms/bloom/blob/master/bloom.go 16 | type Filter struct { 17 | bits []uint64 // bit数组 18 | bitCnt uint64 // bit位数 19 | seeds []uint64 // 哈希种子 20 | } 21 | 22 | // capacity:容量 23 | // falsePositiveRate:误判率 24 | func New(capacity uint64, falsePositiveRate float64) *Filter { 25 | // bit数量 26 | factor := -math.Log(falsePositiveRate) / (math.Ln2 * math.Ln2) 27 | bitCnt := uint64(math.Ceil(float64(capacity) * factor)) 28 | // 这里扩大到最后一个uint64大小,避免浪费 29 | bitCnt = (bitCnt + uint64Bits - 1) / uint64Bits * uint64Bits 30 | // 哈希函数数量 31 | seedCnt := int(math.Ceil(math.Ln2 * float64(bitCnt) / float64(capacity))) 32 | seeds := make([]uint64, seedCnt) 33 | source := rand.New(rand.NewSource(time.Now().UnixNano())) 34 | for i := 0; i < seedCnt; i++ { 35 | seeds[i] = source.Uint64() 36 | } 37 | return &Filter{ 38 | bits: make([]uint64, bitCnt/uint64Bits), 39 | bitCnt: bitCnt, 40 | seeds: seeds, 41 | } 42 | } 43 | 44 | // 添加元素 45 | func (f *Filter) Add(hash uint64) { 46 | for _, seed := range f.seeds { 47 | index, offset := f.pos(hash, seed) 48 | f.bits[index] |= 1 << offset 49 | } 50 | } 51 | 52 | // 添加元素 53 | func (f *Filter) AddBytes(b []byte) { 54 | f.Add(f.hash(b)) 55 | } 56 | 57 | // 添加元素 58 | // 字符串类型 59 | func (f *Filter) AddString(s string) { 60 | f.AddBytes([]byte(s)) 61 | } 62 | 63 | // 元素是否存在 64 | // true表示可能存在 65 | func (f *Filter) Contains(hash uint64) bool { 66 | for _, seed := range f.seeds { 67 | index, offset := f.pos(hash, seed) 68 | mask := uint64(1) << offset 69 | // 判断这一位是否位1 70 | if (f.bits[index] & mask) != mask { 71 | return false 72 | } 73 | } 74 | return true 75 | } 76 | 77 | // 元素是否存在 78 | // true表示可能存在 79 | func (f *Filter) ContainsBytes(b []byte) bool { 80 | return f.Contains(f.hash(b)) 81 | } 82 | 83 | // 元素是否存在 84 | // 字符串类型 85 | func (f *Filter) ContainsString(s string) bool { 86 | return f.ContainsBytes([]byte(s)) 87 | } 88 | 89 | // 清空过滤器 90 | func (f *Filter) Clear() { 91 | for i := range f.bits { 92 | f.bits[i] = 0 93 | } 94 | } 95 | 96 | // bit位数 97 | func (f *Filter) Len() uint64 { 98 | return f.bitCnt 99 | } 100 | 101 | // 获取对应元素下标和偏移 102 | func (f *Filter) pos(h, seed uint64) (uint64, uint64) { 103 | // 按照位计算的偏移 104 | bitsIndex := (h ^ seed) % f.bitCnt 105 | // 因为一个元素64位,因此需要转换 106 | index := bitsIndex / uint64Bits 107 | // 在一个元素里面的偏移 108 | offset := bitsIndex % uint64Bits 109 | return index, offset 110 | } 111 | 112 | // 计算哈希值 113 | func (f *Filter) hash(b []byte) uint64 { 114 | fnvHash := fnv.New64() 115 | fnvHash.Write(b) 116 | return fnvHash.Sum64() 117 | } 118 | -------------------------------------------------------------------------------- /consistenthash/consistenthash_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Google Inc. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | package consistenthash 15 | 16 | import ( 17 | "fmt" 18 | "strconv" 19 | "testing" 20 | ) 21 | 22 | func TestHashing(t *testing.T) { 23 | 24 | // Override the hash function to return easier to reason about values. Assumes 25 | // the keys can be converted to an integer. 26 | hash := New(3, func(key []byte) uint32 { 27 | i, err := strconv.Atoi(string(key)) 28 | if err != nil { 29 | panic(err) 30 | } 31 | return uint32(i) 32 | }) 33 | 34 | // Given the above hash function, this will give replicas with "hashes": 35 | // 2, 4, 6, 12, 14, 16, 22, 24, 26 36 | hash.Add("6", "4", "2") 37 | 38 | testCases := map[string]string{ 39 | "2": "2", 40 | "11": "2", 41 | "23": "4", 42 | "27": "2", 43 | } 44 | 45 | for k, v := range testCases { 46 | if hash.Get(k) != v { 47 | t.Errorf("Asking for %s, should have yielded %s", k, v) 48 | } 49 | } 50 | 51 | // Adds 8, 18, 28 52 | hash.Add("8") 53 | 54 | // 27 should now map to 8. 55 | testCases["27"] = "8" 56 | 57 | for k, v := range testCases { 58 | if hash.Get(k) != v { 59 | t.Errorf("Asking for %s, should have yielded %s", k, v) 60 | } 61 | } 62 | 63 | } 64 | 65 | func TestConsistency(t *testing.T) { 66 | hash1 := New(1, nil) 67 | hash2 := New(1, nil) 68 | 69 | hash1.Add("Bill", "Bob", "Bonny") 70 | hash2.Add("Bob", "Bonny", "Bill") 71 | 72 | if hash1.Get("Ben") != hash2.Get("Ben") { 73 | t.Errorf("Fetching 'Ben' from both hashes should be the same") 74 | } 75 | 76 | hash2.Add("Becky", "Ben", "Bobby") 77 | 78 | if hash1.Get("Ben") != hash2.Get("Ben") || 79 | hash1.Get("Bob") != hash2.Get("Bob") || 80 | hash1.Get("Bonny") != hash2.Get("Bonny") { 81 | t.Errorf("Direct matches should always return the same entry") 82 | } 83 | 84 | } 85 | 86 | func BenchmarkGet8(b *testing.B) { benchmarkGet(b, 8) } 87 | func BenchmarkGet32(b *testing.B) { benchmarkGet(b, 32) } 88 | func BenchmarkGet128(b *testing.B) { benchmarkGet(b, 128) } 89 | func BenchmarkGet512(b *testing.B) { benchmarkGet(b, 512) } 90 | 91 | func benchmarkGet(b *testing.B, shards int) { 92 | 93 | hash := New(50, nil) 94 | 95 | var buckets []string 96 | for i := 0; i < shards; i++ { 97 | buckets = append(buckets, fmt.Sprintf("shard-%d", i)) 98 | } 99 | 100 | hash.Add(buckets...) 101 | 102 | b.ResetTimer() 103 | 104 | for i := 0; i < b.N; i++ { 105 | hash.Get(buckets[i&(shards-1)]) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /container/heap/heap.go: -------------------------------------------------------------------------------- 1 | package heap 2 | 3 | type LessFunc[T any] func(e1 T, e2 T) bool 4 | 5 | type Heap[T any] struct { 6 | h []T 7 | lessFunc LessFunc[T] 8 | } 9 | 10 | func New[T any](h []T, lessFunc LessFunc[T]) *Heap[T] { 11 | heap := &Heap[T]{ 12 | h: h, 13 | lessFunc: lessFunc, 14 | } 15 | heap.init() 16 | return heap 17 | } 18 | 19 | // 移除堆顶元素 20 | func (h *Heap[T]) Pop() T { 21 | n := h.Len() - 1 22 | h.swap(0, n) 23 | h.down(0, n) 24 | return h.pop() 25 | } 26 | 27 | // 获取堆顶元素 28 | func (h *Heap[T]) Peek() T { 29 | return h.h[0] 30 | } 31 | 32 | // 添加元素到堆 33 | func (h *Heap[T]) Push(x T) { 34 | h.push(x) 35 | h.up(h.Len() - 1) 36 | } 37 | 38 | // 堆长度 39 | func (h *Heap[T]) Len() int { 40 | return len(h.h) 41 | } 42 | 43 | // 堆是否为空 44 | func (h *Heap[T]) Empty() bool { 45 | return h.Len() == 0 46 | } 47 | 48 | // Remove removes and returns the element at index i from the heap. 49 | // The complexity is O(log n) where n = h.Len(). 50 | func (h *Heap[T]) Remove(i int) T { 51 | n := h.Len() - 1 52 | if n != i { 53 | h.swap(i, n) 54 | if !h.down(i, n) { 55 | h.up(i) 56 | } 57 | } 58 | return h.pop() 59 | } 60 | 61 | // Fix re-establishes the heap ordering after the element at index i has changed its value. 62 | // Changing the value of the element at index i and then calling Fix is equivalent to, 63 | // but less expensive than, calling Remove(h, i) followed by a Push of the new value. 64 | // The complexity is O(log n) where n = h.Len(). 65 | func (h *Heap[T]) Fix(i int) { 66 | if !h.down(i, h.Len()) { 67 | h.up(i) 68 | } 69 | } 70 | 71 | // Init establishes the heap invariants required by the other routines in this package. 72 | // Init is idempotent with respect to the heap invariants 73 | // and may be called whenever the heap invariants may have been invalidated. 74 | // The complexity is O(n) where n = h.Len(). 75 | func (h *Heap[T]) init() { 76 | // heapify 77 | n := h.Len() 78 | for i := n/2 - 1; i >= 0; i-- { 79 | h.down(i, n) 80 | } 81 | } 82 | 83 | func (h *Heap[T]) up(j int) { 84 | for { 85 | i := (j - 1) / 2 // parent 86 | if i == j || !h.less(j, i) { 87 | break 88 | } 89 | h.swap(i, j) 90 | j = i 91 | } 92 | } 93 | 94 | func (h *Heap[T]) down(i0, n int) bool { 95 | i := i0 96 | for { 97 | j1 := 2*i + 1 98 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 99 | break 100 | } 101 | j := j1 // left child 102 | if j2 := j1 + 1; j2 < n && h.less(j2, j1) { 103 | j = j2 // = 2*i + 2 // right child 104 | } 105 | if !h.less(j, i) { 106 | break 107 | } 108 | h.swap(i, j) 109 | i = j 110 | } 111 | return i > i0 112 | } 113 | 114 | func (h *Heap[T]) less(i, j int) bool { 115 | return h.lessFunc(h.h[i], h.h[j]) 116 | } 117 | 118 | func (h *Heap[T]) swap(i, j int) { 119 | h.h[i], h.h[j] = h.h[j], h.h[i] 120 | } 121 | 122 | func (h *Heap[T]) push(x T) { 123 | h.h = append(h.h, x) 124 | } 125 | 126 | func (h *Heap[T]) pop() T { 127 | elem := h.h[h.Len()-1] 128 | h.h = h.h[:h.Len()-1] 129 | return elem 130 | } 131 | -------------------------------------------------------------------------------- /crypto/query/query.go: -------------------------------------------------------------------------------- 1 | package query 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | 7 | "github.com/jiaxwu/gommon/crypto" 8 | "github.com/jiaxwu/gommon/crypto/aes" 9 | ) 10 | 11 | // 不满一个切片 12 | var ErrNotFullSlice = errors.New("not full slice") 13 | 14 | // 支持模糊查询的加密 15 | // 基于AES+CBC+PKCS5Padding 16 | type Cipher struct { 17 | sliceSize int // 切片长度 18 | c *aes.Cipher 19 | } 20 | 21 | func New(key []byte, sliceSize int) *Cipher { 22 | if sliceSize <= 0 { 23 | panic("slice size must be greater than 0") 24 | } 25 | return &Cipher{ 26 | sliceSize: sliceSize, 27 | c: aes.New(key), 28 | } 29 | } 30 | 31 | // 加密 32 | func (c *Cipher) Encrypt(src []byte) ([]byte, error) { 33 | return c.encrypt(src, false) 34 | } 35 | 36 | // 加密到Base64 37 | func (c *Cipher) EncryptToBase64(src string) (string, error) { 38 | dst, err := c.encrypt([]byte(src), true) 39 | if err != nil { 40 | return "", err 41 | } 42 | return string(dst), nil 43 | } 44 | 45 | func (c *Cipher) encrypt(src []byte, isBase64 bool) ([]byte, error) { 46 | srcLen := len(src) 47 | if srcLen < c.sliceSize { 48 | return nil, ErrNotFullSlice 49 | } 50 | sliceCnt := srcLen - c.sliceSize + 1 // 切片数量 51 | paddingSliceLen := c.paddingSliceLen(isBase64) // 填充后切片长度 52 | dstLen := sliceCnt * paddingSliceLen // 目标串长度 53 | dst := make([]byte, dstLen) 54 | for i := 0; i+c.sliceSize <= srcLen; i++ { 55 | encrypt := c.c.Encrypt(src[i : i+c.sliceSize]) 56 | if isBase64 { 57 | base64.RawStdEncoding.Encode(dst[paddingSliceLen*i:], encrypt) 58 | } else { 59 | copy(dst[paddingSliceLen*i:], encrypt) 60 | } 61 | } 62 | return dst, nil 63 | } 64 | 65 | // 解密 66 | func (c *Cipher) Decrypt(src []byte) ([]byte, error) { 67 | return c.decrypt(src, false) 68 | } 69 | 70 | // 从Base64解密 71 | func (c *Cipher) DecryptFromBase64(src string) (string, error) { 72 | dst, err := c.decrypt([]byte(src), true) 73 | return string(dst), err 74 | } 75 | 76 | func (c *Cipher) decrypt(src []byte, isBase64 bool) ([]byte, error) { 77 | paddingSliceLen := c.paddingSliceLen(isBase64) // 填充后切片长度 78 | srcLen := len(src) 79 | if srcLen < paddingSliceLen || srcLen%paddingSliceLen != 0 { 80 | return nil, ErrNotFullSlice 81 | } 82 | sliceCnt := srcLen / paddingSliceLen // 切片数量 83 | dstLen := sliceCnt + c.sliceSize - 1 // 目标串长度 84 | dst := make([]byte, dstLen) 85 | var decoded []byte 86 | if isBase64 { 87 | decoded = make([]byte, base64.RawStdEncoding.DecodedLen(paddingSliceLen)) 88 | } 89 | for i := 0; i < sliceCnt; i++ { 90 | if isBase64 { 91 | _, err := base64.RawStdEncoding.Decode(decoded, src[paddingSliceLen*i:paddingSliceLen*(i+1)]) 92 | if err != nil { 93 | return nil, err 94 | } 95 | copy(dst[i:], c.c.Decrypt(decoded)) 96 | } else { 97 | copy(dst[i:], c.c.Decrypt(src[paddingSliceLen*i:paddingSliceLen*(i+1)])) 98 | } 99 | } 100 | return dst, nil 101 | } 102 | 103 | // 填充后切片长度 104 | func (c *Cipher) paddingSliceLen(isBase64 bool) int { 105 | paddingSliceLen := crypto.PKCS5DstLen(c.sliceSize, c.c.BlockSize()) 106 | if isBase64 { 107 | paddingSliceLen = base64.RawStdEncoding.EncodedLen(paddingSliceLen) 108 | } 109 | return paddingSliceLen 110 | } 111 | -------------------------------------------------------------------------------- /container/reap/reap.go: -------------------------------------------------------------------------------- 1 | package meap 2 | 3 | type Entry[T any] struct { 4 | value T 5 | index int 6 | } 7 | 8 | // 值 9 | func (e *Entry[T]) Value() T { 10 | return e.value 11 | } 12 | 13 | type LessFunc[T any] func(e1, e2 T) bool 14 | 15 | // reap=r[emovable]+[h]eap 16 | // 可以通过Entry实现log(n)删除任意元素的堆 17 | type Reap[T any] struct { 18 | h []*Entry[T] 19 | lessFunc LessFunc[T] 20 | } 21 | 22 | func New[T any](lessFunc LessFunc[T]) *Reap[T] { 23 | return &Reap[T]{ 24 | lessFunc: lessFunc, 25 | } 26 | } 27 | 28 | // 移除堆顶元素 29 | func (h *Reap[T]) Pop() T { 30 | n := h.Len() - 1 31 | h.swap(0, n) 32 | h.down(0, n) 33 | return h.pop() 34 | } 35 | 36 | // 获取堆顶元素 37 | func (h *Reap[T]) Peek() T { 38 | return h.h[0].value 39 | } 40 | 41 | // 添加元素到堆 42 | func (h *Reap[T]) Push(value T) *Entry[T] { 43 | entry := h.push(value) 44 | h.up(h.Len() - 1) 45 | return entry 46 | } 47 | 48 | // 移除堆里对应的元素 49 | func (h *Reap[T]) Remove(e *Entry[T]) bool { 50 | // 不能已经被删除 51 | if e.index == -1 { 52 | return false 53 | } 54 | // 删除元素 55 | i := e.index 56 | n := h.Len() - 1 57 | if n != i { 58 | h.swap(i, n) 59 | if !h.down(i, n) { 60 | h.up(i) 61 | } 62 | } 63 | h.pop() 64 | return true 65 | } 66 | 67 | // 设置元素的值,会重新调整堆 68 | func (h *Reap[T]) SetValue(e *Entry[T], value T) bool { 69 | // 不能已经被删除 70 | if e.index == -1 { 71 | return false 72 | } 73 | // 重新设置值并调整 74 | e.value = value 75 | h.fix(e.index) 76 | return true 77 | } 78 | 79 | // 堆长度 80 | func (h *Reap[T]) Len() int { 81 | return len(h.h) 82 | } 83 | 84 | // 堆是否为空 85 | func (h *Reap[T]) Empty() bool { 86 | return h.Len() == 0 87 | } 88 | 89 | // Fix re-establishes the heap ordering after the element at index i has changed its value. 90 | // Changing the value of the element at index i and then calling Fix is equivalent to, 91 | // but less expensive than, calling Remove(h, i) followed by a Push of the new value. 92 | // The complexity is O(log n) where n = h.Len(). 93 | func (h *Reap[T]) fix(i int) { 94 | if !h.down(i, h.Len()) { 95 | h.up(i) 96 | } 97 | } 98 | 99 | func (h *Reap[T]) up(j int) { 100 | for { 101 | i := (j - 1) / 2 // parent 102 | if i == j || !h.less(j, i) { 103 | break 104 | } 105 | h.swap(i, j) 106 | j = i 107 | } 108 | } 109 | 110 | func (h *Reap[T]) down(i0, n int) bool { 111 | i := i0 112 | for { 113 | j1 := 2*i + 1 114 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 115 | break 116 | } 117 | j := j1 // left child 118 | if j2 := j1 + 1; j2 < n && h.less(j2, j1) { 119 | j = j2 // = 2*i + 2 // right child 120 | } 121 | if !h.less(j, i) { 122 | break 123 | } 124 | h.swap(i, j) 125 | i = j 126 | } 127 | return i > i0 128 | } 129 | 130 | func (h *Reap[T]) less(i, j int) bool { 131 | return h.lessFunc(h.h[i].value, h.h[j].value) 132 | } 133 | 134 | // swap两个元素的时候 135 | func (h *Reap[T]) swap(i, j int) { 136 | h.h[i], h.h[j] = h.h[j], h.h[i] 137 | h.h[i].index = i 138 | h.h[j].index = j 139 | } 140 | 141 | // 添加一个元素到堆的末尾 142 | func (h *Reap[T]) push(value T) *Entry[T] { 143 | entry := &Entry[T]{ 144 | value: value, 145 | index: h.Len(), 146 | } 147 | h.h = append(h.h, entry) 148 | return entry 149 | } 150 | 151 | // 从堆的末尾移除元素 152 | func (h *Reap[T]) pop() T { 153 | elem := h.h[h.Len()-1] 154 | h.h = h.h[:h.Len()-1] 155 | // 标记元素已经被删除 156 | elem.index = -1 157 | return elem.value 158 | } 159 | -------------------------------------------------------------------------------- /container/ringbuffer/unbounded/unbounded.go: -------------------------------------------------------------------------------- 1 | package unbounded 2 | 3 | import ( 4 | "math" 5 | 6 | mmath "github.com/jiaxwu/gommon/math" 7 | ) 8 | 9 | // 最大长度 10 | const MaxSize = math.MaxInt64 11 | 12 | // 动态扩展长度 13 | // 非线程安全,请加锁 14 | type Ring[T any] struct { 15 | in uint64 // 写索引 16 | out uint64 // 读索引 17 | size uint64 // 长度 18 | data []T // 数据 19 | } 20 | 21 | func New[T any](initSize uint64) *Ring[T] { 22 | if initSize > MaxSize { 23 | panic("size is too large") 24 | } 25 | 26 | return &Ring[T]{ 27 | size: initSize, 28 | data: make([]T, initSize), 29 | } 30 | } 31 | 32 | // 弹出队头元素 33 | func (r *Ring[T]) Pop() T { 34 | if r.Empty() { 35 | panic("ring emtpy") 36 | } 37 | out := r.out % r.size 38 | r.out++ 39 | return r.data[out] 40 | } 41 | 42 | // 队头元素 43 | func (r *Ring[T]) Peek() T { 44 | if r.Empty() { 45 | panic("ring emtpy") 46 | } 47 | return r.data[r.out%r.size] 48 | } 49 | 50 | // 插入元素到队尾 51 | func (r *Ring[T]) Push(e T) { 52 | if r.Full() { 53 | r.Grow(r.Cap() + 1) 54 | } 55 | in := r.in % r.size 56 | r.in++ 57 | r.data[in] = e 58 | } 59 | 60 | // 写入队尾 61 | func (r *Ring[T]) MPush(elems ...T) { 62 | size := uint64(len(elems)) 63 | if size == 0 { 64 | return 65 | } 66 | if size > r.Avail() { 67 | r.Grow(r.Cap() + size) 68 | } 69 | in := r.in % r.size 70 | copied := copy(r.data[in:], elems) 71 | copy(r.data, elems[copied:]) 72 | r.in += size 73 | } 74 | 75 | // 从队头读取 76 | func (r *Ring[T]) MPop(size uint64) []T { 77 | if size > r.Len() { 78 | size = r.Len() 79 | } 80 | if size == 0 { 81 | return nil 82 | } 83 | out := r.out % r.size 84 | elems := make([]T, size) 85 | copied := copy(elems, r.data[out:]) 86 | copy(elems[copied:], r.data) 87 | r.out += size 88 | return elems 89 | } 90 | 91 | // 从队头读取,填充到dst里 92 | func (r *Ring[T]) MPopCopy(dst []T) { 93 | out := r.out % r.size 94 | dst = dst[:mmath.Min(uint64(len(dst)), r.Len())] 95 | copied := copy(dst, r.data[out:]) 96 | copied += copy(dst[copied:], r.data) 97 | r.out += uint64(copied) 98 | } 99 | 100 | // 重置读写指针 101 | func (r *Ring[T]) Reset() { 102 | r.in = 0 103 | r.out = 0 104 | r.data = make([]T, r.size) 105 | } 106 | 107 | // 总长度 108 | func (r *Ring[T]) Cap() uint64 { 109 | return r.size 110 | } 111 | 112 | // 使用长度 113 | func (r *Ring[T]) Len() uint64 { 114 | return r.in - r.out 115 | } 116 | 117 | // 可用长度 118 | func (r *Ring[T]) Avail() uint64 { 119 | return r.Cap() - r.Len() 120 | } 121 | 122 | // 是否为空 123 | func (r *Ring[T]) Empty() bool { 124 | return r.in == r.out 125 | } 126 | 127 | // 是否满了 128 | func (r *Ring[T]) Full() bool { 129 | return r.Avail() == 0 130 | } 131 | 132 | // 扩容 133 | func (r *Ring[T]) Grow(minSize uint64) { 134 | size := mmath.Max(r.size*2, minSize) 135 | if size > MaxSize { 136 | panic("size is too large") 137 | } 138 | if size < 2 { 139 | size = 2 140 | } 141 | // 还没容量,直接申请,因为不需要迁移元素 142 | if r.size == 0 { 143 | r.data = make([]T, size) 144 | r.size = size 145 | return 146 | } 147 | data := make([]T, size) 148 | out := r.out % r.size 149 | len := r.Len() 150 | copied := copy(data[:len], r.data[out:]) 151 | copy(data[copied:len], r.data) 152 | r.out = 0 153 | r.in = len 154 | r.size = size 155 | r.data = data 156 | } 157 | -------------------------------------------------------------------------------- /counter/cm/cm4_test.go: -------------------------------------------------------------------------------- 1 | package cm 2 | 3 | import ( 4 | "math" 5 | "strconv" 6 | "testing" 7 | ) 8 | 9 | func TestCounter4(t *testing.T) { 10 | cm := New4(1000, 1, 0.001) 11 | cm.AddString("10", 1) 12 | cm.AddString("51151", 1) 13 | cm.AddString("321", 1) 14 | cm.AddString("10", 1) 15 | cm.AddString("10", 1) 16 | cm.AddString("321", 1) 17 | if cm.EstimateString("10") != 3 { 18 | t.Errorf("want %v, but %d", 3, cm.EstimateString("10")) 19 | } 20 | if cm.EstimateString("321") != 2 { 21 | t.Errorf("want %v, but %d", 2, cm.EstimateString("321")) 22 | } 23 | if cm.EstimateString("51151") != 1 { 24 | t.Errorf("want %v, but %d", 1, cm.EstimateString("1")) 25 | } 26 | 27 | cm.AddString("10", 100) 28 | if cm.EstimateString("10") != 15 { 29 | t.Errorf("want %v, but %d", 15, cm.EstimateString("10")) 30 | } 31 | cm.AddString("10", 254) 32 | if cm.EstimateString("10") != 15 { 33 | t.Errorf("want %v, but %d", 15, cm.EstimateString("10")) 34 | } 35 | cm.AddString("5", 100) 36 | if cm.EstimateString("5") != 15 { 37 | t.Errorf("want %v, but %d", 15, cm.EstimateString("5")) 38 | } 39 | cm.AddString("1", 100) 40 | if cm.EstimateString("1") != 15 { 41 | t.Errorf("want %v, but %d", 15, cm.EstimateString("1")) 42 | } 43 | 44 | cm.Attenuation(2) 45 | if cm.EstimateString("10") != 7 { 46 | t.Errorf("want %v, but %d", 7, cm.EstimateString("10")) 47 | } 48 | if cm.EstimateString("5") != 7 { 49 | t.Errorf("want %v, but %d", 7, cm.EstimateString("5")) 50 | } 51 | if cm.EstimateString("1") != 7 { 52 | t.Errorf("want %v, but %d", 7, cm.EstimateString("1")) 53 | } 54 | } 55 | 56 | func TestCounter4ExpectedErrorAndErrorRate(t *testing.T) { 57 | capacity := uint64(1000000) 58 | errorRange := uint8(1) 59 | errorRate := 0.001 60 | cm := New4(capacity, errorRange, errorRate) 61 | // 添加计数值 62 | for i := uint64(0); i < capacity; i++ { 63 | cm.Add(i, 1) 64 | } 65 | // 评估 66 | errorCount := 0 67 | errorSum := 0 68 | for i := uint64(0); i < capacity; i++ { 69 | val := cm.Estimate(i) 70 | if val > 1+errorRange { 71 | errorCount++ 72 | errorSum += int(val) - 1 73 | } 74 | } 75 | estimateErrorRate := float64(errorCount) / float64(capacity) 76 | estimateError := float64(errorSum) / math.Max(1, float64(errorCount)) 77 | if estimateErrorRate > errorRate { 78 | t.Errorf("errorRate not accuracy %v", estimateErrorRate) 79 | } 80 | if estimateError > float64(errorRange) { 81 | t.Errorf("errorRange not accuracy %v", estimateError) 82 | } 83 | } 84 | 85 | func BenchmarkCounter4AddAndEstimateBytes(b *testing.B) { 86 | buf := make([]byte, 8192) 87 | for length := 1; length <= cap(buf); length *= 2 { 88 | b.Run(strconv.Itoa(length), func(b *testing.B) { 89 | f := New4(uint64(b.N), 10, 0.0001) 90 | buf = buf[:length] 91 | b.SetBytes(int64(length)) 92 | b.ReportAllocs() 93 | b.ResetTimer() 94 | for i := 0; i < b.N; i++ { 95 | f.AddBytes(buf, 1) 96 | f.EstimateBytes(buf) 97 | } 98 | }) 99 | } 100 | } 101 | 102 | func BenchmarkCounter4AddAndEstimate(b *testing.B) { 103 | for length := 1; length <= 8192; length *= 2 { 104 | b.Run(strconv.Itoa(length), func(b *testing.B) { 105 | f := New[uint8](uint64(b.N), 10, 0.0001) 106 | b.ReportAllocs() 107 | b.ResetTimer() 108 | for i := 0; i < b.N; i++ { 109 | f.Add(uint64(i), 1) 110 | f.Estimate(uint64(i)) 111 | } 112 | }) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /limiter/sliding_log_limiter.go: -------------------------------------------------------------------------------- 1 | package limiter 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sort" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // ViolationStrategyError 违背策略错误 12 | type ViolationStrategyError struct { 13 | Limit int // 窗口请求上限 14 | Window time.Duration // 窗口时间大小 15 | } 16 | 17 | func (e *ViolationStrategyError) Error() string { 18 | return fmt.Sprintf("violation strategy that limit = %d and window = %d", e.Limit, e.Window) 19 | } 20 | 21 | // SlidingLogLimiterStrategy 滑动日志限流器的策略 22 | type SlidingLogLimiterStrategy struct { 23 | limit int // 窗口请求上限 24 | window int64 // 窗口时间大小 25 | smallWindows int64 // 小窗口数量 26 | } 27 | 28 | func NewSlidingLogLimiterStrategy(limit int, window time.Duration) *SlidingLogLimiterStrategy { 29 | return &SlidingLogLimiterStrategy{ 30 | limit: limit, 31 | window: int64(window), 32 | } 33 | } 34 | 35 | // SlidingLogLimiter 滑动日志限流器 36 | type SlidingLogLimiter struct { 37 | strategies []*SlidingLogLimiterStrategy // 滑动日志限流器策略列表 38 | smallWindow int64 // 小窗口时间大小 39 | counters map[int64]int // 小窗口计数器 40 | mutex sync.Mutex // 避免并发问题 41 | } 42 | 43 | func NewSlidingLogLimiter(smallWindow time.Duration, strategies ...*SlidingLogLimiterStrategy) (*SlidingLogLimiter, error) { 44 | // 复制策略避免被修改 45 | strategies = append(make([]*SlidingLogLimiterStrategy, 0, len(strategies)), strategies...) 46 | 47 | // 不能不设置策略 48 | if len(strategies) == 0 { 49 | return nil, errors.New("must be set strategies") 50 | } 51 | 52 | // 排序策略,窗口时间大的排前面,相同窗口上限大的排前面 53 | sort.Slice(strategies, func(i, j int) bool { 54 | a, b := strategies[i], strategies[j] 55 | if a.window == b.window { 56 | return a.limit > b.limit 57 | } 58 | return a.window > b.window 59 | }) 60 | 61 | for i, strategy := range strategies { 62 | // 随着窗口时间变小,窗口上限也应该变小 63 | if i > 0 { 64 | if strategy.limit >= strategies[i-1].limit { 65 | return nil, errors.New("the smaller window should be the smaller limit") 66 | } 67 | } 68 | // 窗口时间必须能够被小窗口时间整除 69 | if strategy.window%int64(smallWindow) != 0 { 70 | return nil, errors.New("window cannot be split by integers") 71 | } 72 | strategy.smallWindows = strategy.window / int64(smallWindow) 73 | } 74 | 75 | return &SlidingLogLimiter{ 76 | strategies: strategies, 77 | smallWindow: int64(smallWindow), 78 | counters: make(map[int64]int), 79 | }, nil 80 | } 81 | 82 | func (l *SlidingLogLimiter) TryAcquire() error { 83 | l.mutex.Lock() 84 | defer l.mutex.Unlock() 85 | 86 | // 获取当前小窗口值 87 | currentSmallWindow := time.Now().UnixNano() / l.smallWindow * l.smallWindow 88 | // 获取每个策略的起始小窗口值 89 | startSmallWindows := make([]int64, len(l.strategies)) 90 | for i, strategy := range l.strategies { 91 | startSmallWindows[i] = currentSmallWindow - l.smallWindow*(strategy.smallWindows-1) 92 | } 93 | 94 | // 计算每个策略当前窗口的请求总数 95 | counts := make([]int, len(l.strategies)) 96 | for smallWindow, counter := range l.counters { 97 | if smallWindow < startSmallWindows[0] { 98 | delete(l.counters, smallWindow) 99 | continue 100 | } 101 | for i := range l.strategies { 102 | if smallWindow >= startSmallWindows[i] { 103 | counts[i] += counter 104 | } 105 | } 106 | } 107 | 108 | // 若到达对应策略窗口请求上限,请求失败,返回违背的策略 109 | for i, strategy := range l.strategies { 110 | if counts[i] >= strategy.limit { 111 | return &ViolationStrategyError{ 112 | Limit: strategy.limit, 113 | Window: time.Duration(strategy.window), 114 | } 115 | } 116 | } 117 | 118 | // 若没到窗口请求上限,当前小窗口计数器+1,请求成功 119 | l.counters[currentSmallWindow]++ 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /cache/random/random.go: -------------------------------------------------------------------------------- 1 | package random 2 | 3 | import "github.com/jiaxwu/gommon/cache" 4 | 5 | // 随机 6 | // 优点:实现简单 7 | // 非线程安全,请根据业务加锁 8 | type Cache[K comparable, V any] struct { 9 | entries map[K]V 10 | capacity int 11 | onEvict cache.OnEvict[K, V] 12 | } 13 | 14 | func New[K comparable, V any](capacity int) *Cache[K, V] { 15 | if capacity < 1 { 16 | panic("too small capacity") 17 | } 18 | return &Cache[K, V]{ 19 | entries: make(map[K]V), 20 | capacity: capacity, 21 | } 22 | } 23 | 24 | // 设置 OnEvict 25 | func (c *Cache[K, V]) SetOnEvict(onEvict cache.OnEvict[K, V]) { 26 | c.onEvict = onEvict 27 | } 28 | 29 | // 添加或更新元素 30 | // 返回被淘汰的元素 31 | func (c *Cache[K, V]) Put(key K, value V) *cache.Entry[K, V] { 32 | // 如果 key 已经存在,直接设置新值 33 | if _, ok := c.entries[key]; ok { 34 | c.entries[key] = value 35 | return nil 36 | } 37 | 38 | // 如果已经到达最大尺寸,先剔除一个元素 39 | var evicted *cache.Entry[K, V] 40 | if c.Full() { 41 | evicted = c.Evict() 42 | } 43 | 44 | // 添加元素 45 | c.entries[key] = value 46 | return evicted 47 | } 48 | 49 | // 获取元素 50 | func (c *Cache[K, V]) Get(key K) (V, bool) { 51 | return c.Peek(key) 52 | } 53 | 54 | // 获取元素 55 | func (c *Cache[K, V]) Peek(key K) (V, bool) { 56 | value, ok := c.entries[key] 57 | return value, ok 58 | } 59 | 60 | // 是否包含元素 61 | func (c *Cache[K, V]) Contains(key K) bool { 62 | _, ok := c.entries[key] 63 | return ok 64 | } 65 | 66 | // 获取缓存的Keys 67 | func (c *Cache[K, V]) Keys() []K { 68 | keys := make([]K, c.Len()) 69 | i := 0 70 | for key := range c.entries { 71 | keys[i] = key 72 | i++ 73 | } 74 | return keys 75 | } 76 | 77 | // 获取缓存的Values 78 | func (c *Cache[K, V]) Values() []V { 79 | values := make([]V, c.Len()) 80 | i := 0 81 | for _, value := range c.entries { 82 | values[i] = value 83 | i++ 84 | } 85 | return values 86 | } 87 | 88 | // 获取缓存的Entries 89 | func (c *Cache[K, V]) Entries() []*cache.Entry[K, V] { 90 | entries := make([]*cache.Entry[K, V], c.Len()) 91 | i := 0 92 | for key, value := range c.entries { 93 | entries[i] = &cache.Entry[K, V]{ 94 | Key: key, 95 | Value: value, 96 | } 97 | i++ 98 | } 99 | return entries 100 | } 101 | 102 | // 移除元素 103 | func (c *Cache[K, V]) Remove(key K) bool { 104 | if _, ok := c.entries[key]; ok { 105 | delete(c.entries, key) 106 | return true 107 | } 108 | return false 109 | } 110 | 111 | // 淘汰元素 112 | func (c *Cache[K, V]) Evict() *cache.Entry[K, V] { 113 | for key, value := range c.entries { 114 | delete(c.entries, key) 115 | // 回调 116 | if c.onEvict != nil { 117 | c.onEvict(&cache.Entry[K, V]{ 118 | Key: key, 119 | Value: value, 120 | }) 121 | } 122 | return &cache.Entry[K, V]{ 123 | Key: key, 124 | Value: value, 125 | } 126 | } 127 | return nil 128 | } 129 | 130 | // 清空缓存 131 | func (c *Cache[K, V]) Clear(needOnEvict bool) { 132 | // 触发回调 133 | if needOnEvict && c.onEvict != nil { 134 | for key, value := range c.entries { 135 | c.onEvict(&cache.Entry[K, V]{ 136 | Key: key, 137 | Value: value, 138 | }) 139 | } 140 | } 141 | 142 | // 清空 143 | c.entries = make(map[K]V) 144 | } 145 | 146 | // 改变容量 147 | func (c *Cache[K, V]) Resize(capacity int, needOnEvict bool) { 148 | diff := c.Len() - capacity 149 | if diff < 0 { 150 | diff = 0 151 | } 152 | for i := 0; i < diff; i++ { 153 | c.Evict() 154 | } 155 | c.capacity = capacity 156 | } 157 | 158 | // 元素个数 159 | func (c *Cache[K, V]) Len() int { 160 | return len(c.entries) 161 | } 162 | 163 | // 容量 164 | func (c *Cache[K, V]) Cap() int { 165 | return c.capacity 166 | } 167 | 168 | // 缓存满了 169 | func (c *Cache[K, V]) Full() bool { 170 | return c.Len() == c.Cap() 171 | } 172 | -------------------------------------------------------------------------------- /container/meap/meap.go: -------------------------------------------------------------------------------- 1 | package meap 2 | 3 | type Entry[K comparable, V any] struct { 4 | Key K 5 | Value V 6 | } 7 | 8 | type LessFunc[K comparable, V any] func(e1 Entry[K, V], e2 Entry[K, V]) bool 9 | 10 | // meap=m[ap]+[h]eap 11 | // 可以log(n)删除任意元素的堆 12 | // 可以支持随机查询 13 | // 是堆和map的结合 14 | // 也就是带有map的特性和堆的特性 15 | type Meap[K comparable, V any] struct { 16 | h []Entry[K, V] 17 | m map[K]int 18 | lessFunc LessFunc[K, V] 19 | } 20 | 21 | func New[K comparable, V any](lessFunc LessFunc[K, V]) *Meap[K, V] { 22 | return &Meap[K, V]{ 23 | m: make(map[K]int), 24 | lessFunc: lessFunc, 25 | } 26 | } 27 | 28 | // 移除堆顶元素 29 | func (h *Meap[K, V]) Pop() Entry[K, V] { 30 | n := h.Len() - 1 31 | h.swap(0, n) 32 | h.down(0, n) 33 | return h.pop() 34 | } 35 | 36 | // 获取堆顶元素 37 | func (h *Meap[K, V]) Peek() Entry[K, V] { 38 | return h.h[0] 39 | } 40 | 41 | // 获取元素 42 | func (h *Meap[K, V]) Get(key K) (V, bool) { 43 | index, ok := h.m[key] 44 | if !ok { 45 | var v V 46 | return v, ok 47 | } 48 | return h.h[index].Value, true 49 | } 50 | 51 | // 添加元素到堆 52 | func (h *Meap[K, V]) Push(key K, value V) { 53 | // 如果堆中已经包含这个元素 54 | // 更新值并调整堆 55 | if h.Contains(key) { 56 | index := h.m[key] 57 | h.h[index].Value = value 58 | h.fix(index) 59 | return 60 | } 61 | 62 | // 否则添加元素 63 | h.push(key, value) 64 | h.up(h.Len() - 1) 65 | } 66 | 67 | // 堆长度 68 | func (h *Meap[K, V]) Len() int { 69 | return len(h.h) 70 | } 71 | 72 | // 堆是否为空 73 | func (h *Meap[K, V]) Empty() bool { 74 | return h.Len() == 0 75 | } 76 | 77 | // 移除堆里对应Key的元素 78 | func (h *Meap[K, V]) Remove(key K) { 79 | i, ok := h.m[key] 80 | if !ok { 81 | return 82 | } 83 | 84 | n := h.Len() - 1 85 | if n != i { 86 | h.swap(i, n) 87 | if !h.down(i, n) { 88 | h.up(i) 89 | } 90 | } 91 | h.pop() 92 | } 93 | 94 | // 是否包含这个元素 95 | func (h *Meap[K, V]) Contains(key K) bool { 96 | _, ok := h.m[key] 97 | return ok 98 | } 99 | 100 | // Fix re-establishes the heap ordering after the element at index i has changed its value. 101 | // Changing the value of the element at index i and then calling Fix is equivalent to, 102 | // but less expensive than, calling Remove(h, i) followed by a Push of the new value. 103 | // The complexity is O(log n) where n = h.Len(). 104 | func (h *Meap[K, V]) fix(i int) { 105 | if !h.down(i, h.Len()) { 106 | h.up(i) 107 | } 108 | } 109 | 110 | func (h *Meap[K, V]) up(j int) { 111 | for { 112 | i := (j - 1) / 2 // parent 113 | if i == j || !h.less(j, i) { 114 | break 115 | } 116 | h.swap(i, j) 117 | j = i 118 | } 119 | } 120 | 121 | func (h *Meap[K, V]) down(i0, n int) bool { 122 | i := i0 123 | for { 124 | j1 := 2*i + 1 125 | if j1 >= n || j1 < 0 { // j1 < 0 after int overflow 126 | break 127 | } 128 | j := j1 // left child 129 | if j2 := j1 + 1; j2 < n && h.less(j2, j1) { 130 | j = j2 // = 2*i + 2 // right child 131 | } 132 | if !h.less(j, i) { 133 | break 134 | } 135 | h.swap(i, j) 136 | i = j 137 | } 138 | return i > i0 139 | } 140 | 141 | func (h *Meap[K, V]) less(i, j int) bool { 142 | return h.lessFunc(h.h[i], h.h[j]) 143 | } 144 | 145 | // swap两个元素的时候 146 | // 两个元素在map里的下标也要交换 147 | func (h *Meap[K, V]) swap(i, j int) { 148 | h.h[i], h.h[j] = h.h[j], h.h[i] 149 | h.m[h.h[i].Key] = i 150 | h.m[h.h[j].Key] = j 151 | } 152 | 153 | // 添加一个元素到堆的末尾 154 | func (h *Meap[K, V]) push(key K, value V) { 155 | h.m[key] = h.Len() 156 | h.h = append(h.h, Entry[K, V]{ 157 | Key: key, 158 | Value: value, 159 | }) 160 | } 161 | 162 | // 从堆的末尾移除元素 163 | func (h *Meap[K, V]) pop() Entry[K, V] { 164 | elem := h.h[h.Len()-1] 165 | h.h = h.h[:h.Len()-1] 166 | delete(h.m, elem.Key) 167 | return elem 168 | } 169 | -------------------------------------------------------------------------------- /counter/cm/cm.go: -------------------------------------------------------------------------------- 1 | package cm 2 | 3 | import ( 4 | "hash/fnv" 5 | "math" 6 | "math/rand" 7 | "time" 8 | 9 | mmath "github.com/jiaxwu/gommon/math" 10 | "github.com/jiaxwu/gommon/mem" 11 | "golang.org/x/exp/constraints" 12 | ) 13 | 14 | // Count-Min Sketch 计数器,原理类似于布隆过滤器,根据哈希映射到多个位置,然后在对应位置进行计数 15 | // 读取时拿对应位置最小的 16 | // 适合需要一个比较小的计数,而且不需要这个计数一定准确的情况 17 | // 可以减少空间消耗 18 | // https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.591.8351&rep=rep1&type=pdf 19 | type Counter[T constraints.Unsigned] struct { 20 | counters [][]T 21 | counterCnt uint64 // 计数器个数 22 | seeds []uint64 // 哈希种子 23 | maxVal T // 最大计数值 24 | } 25 | 26 | // 创建一个计数器 27 | // size:数据流大小 28 | // errorRange:计数值误差范围(会超过真实计数值) 29 | // errorRate:错误率 30 | func New[T constraints.Unsigned](size uint64, errorRange T, errorRate float64) *Counter[T] { 31 | // 计数器长度 32 | counterCnt := uint64(math.Ceil(math.E * float64(size) / float64(errorRange))) 33 | // 哈希个数 34 | seedCnt := int(math.Ceil(math.Log(1 / errorRate))) 35 | seeds := make([]uint64, seedCnt) 36 | counters := make([][]T, seedCnt) 37 | source := rand.New(rand.NewSource(time.Now().UnixNano())) 38 | for i := 0; i < seedCnt; i++ { 39 | seeds[i] = source.Uint64() 40 | counters[i] = make([]T, counterCnt) 41 | } 42 | return &Counter[T]{ 43 | counters: counters, 44 | counterCnt: counterCnt, 45 | seeds: seeds, 46 | maxVal: T(0) - 1, 47 | } 48 | } 49 | 50 | // 创建一个计数器 51 | // size:数据流大小 52 | // elements:不同元素数量 53 | // errorRate:错误率 54 | func NewWithElements[T constraints.Unsigned](size, elements uint64, errorRate float64) *Counter[T] { 55 | if elements > size { 56 | panic("too much elements") 57 | } 58 | errorRange := T(0) - 1 59 | if size/elements < uint64(errorRange) { 60 | errorRange = T(size / elements) 61 | } 62 | return New(size, errorRange, errorRate) 63 | } 64 | 65 | // 增加元素的计数 66 | // 一般h是一个哈希值 67 | func (c *Counter[T]) Add(h uint64, val T) { 68 | for i, seed := range c.seeds { 69 | index := (h ^ seed) % c.counterCnt 70 | if c.counters[i][index]+val <= c.counters[i][index] { 71 | c.counters[i][index] = c.maxVal 72 | } else { 73 | c.counters[i][index] += val 74 | } 75 | } 76 | } 77 | 78 | // 增加元素的计数 79 | func (c *Counter[T]) AddBytes(b []byte, val T) { 80 | c.Add(c.hash(b), val) 81 | } 82 | 83 | // 增加元素的计数 84 | // 字符串类型 85 | func (c *Counter[T]) AddString(s string, val T) { 86 | c.AddBytes([]byte(s), val) 87 | } 88 | 89 | // 估算元素的计数 90 | func (c *Counter[T]) Estimate(h uint64) T { 91 | minCount := c.maxVal 92 | for i, seed := range c.seeds { 93 | index := (h ^ seed) % c.counterCnt 94 | count := c.counters[i][index] 95 | if count == 0 { 96 | return 0 97 | } 98 | minCount = mmath.Min(minCount, count) 99 | } 100 | return minCount 101 | } 102 | 103 | // 估算元素的计数 104 | func (c *Counter[T]) EstimateBytes(b []byte) T { 105 | return c.Estimate(c.hash(b)) 106 | } 107 | 108 | // 估算元素的计数 109 | // 字符串类型 110 | func (c *Counter[T]) EstimateString(s string) T { 111 | return c.EstimateBytes([]byte(s)) 112 | } 113 | 114 | // 计数衰减 115 | // 如果factor为0则直接清空 116 | func (c *Counter[T]) Attenuation(factor T) { 117 | for _, counter := range c.counters { 118 | if factor == 0 { 119 | mem.Memset(counter, 0) 120 | } else { 121 | for j := uint64(0); j < c.counterCnt; j++ { 122 | counter[j] /= factor 123 | } 124 | } 125 | } 126 | } 127 | 128 | // 计数器数量 129 | func (c *Counter[T]) Counters() uint64 { 130 | return c.counterCnt 131 | } 132 | 133 | // 哈希函数数量 134 | func (c *Counter[T]) Hashs() uint64 { 135 | return uint64(len(c.seeds)) 136 | } 137 | 138 | // 计算哈希值 139 | func (c *Counter[T]) hash(b []byte) uint64 { 140 | f := fnv.New64() 141 | f.Write(b) 142 | return f.Sum64() 143 | } 144 | -------------------------------------------------------------------------------- /timer/delayqueue/delayqueue.go: -------------------------------------------------------------------------------- 1 | package delayqueue 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/jiaxwu/gommon/container/heap" 10 | ) 11 | 12 | type entry[T any] struct { 13 | value T 14 | expiration time.Time // 到期时间 15 | } 16 | 17 | // 延迟队列 18 | // 参考https://github.com/RussellLuo/timingwheel/blob/master/delayqueue/delayqueue.go 19 | type DelayQueue[T any] struct { 20 | h *heap.Heap[*entry[T]] 21 | // // 保证并发安全 22 | mutex sync.Mutex 23 | // 表示Take()是否正在等待队列不为空或更早到期的元素 24 | // 0表示Take()没在等待,1表示Take()在等待 25 | sleeping int32 26 | // 唤醒通道 27 | wakeup chan struct{} 28 | } 29 | 30 | // 创建延迟队列 31 | func New[T any]() *DelayQueue[T] { 32 | return &DelayQueue[T]{ 33 | h: heap.New(nil, func(e1, e2 *entry[T]) bool { 34 | return e1.expiration.Before(e2.expiration) 35 | }), 36 | wakeup: make(chan struct{}), 37 | } 38 | } 39 | 40 | // 添加延迟元素到队列 41 | func (q *DelayQueue[T]) Push(value T, delay time.Duration) { 42 | q.mutex.Lock() 43 | defer q.mutex.Unlock() 44 | entry := &entry[T]{ 45 | value: value, 46 | expiration: time.Now().Add(delay), 47 | } 48 | q.h.Push(entry) 49 | // 唤醒等待的Take() 50 | // 这里表示新添加的元素到期时间是最早的,或者原来队列为空 51 | // 因此必须唤醒等待的Take(),因为可以拿到更早到期的元素 52 | if q.h.Peek() == entry { 53 | // 把sleeping从1修改成0,也就是唤醒等待的Take() 54 | if atomic.CompareAndSwapInt32(&q.sleeping, 1, 0) { 55 | q.wakeup <- struct{}{} 56 | } 57 | } 58 | } 59 | 60 | // 等待直到有元素到期 61 | // 或者ctx被关闭 62 | func (q *DelayQueue[T]) Take(ctx context.Context) (T, bool) { 63 | for { 64 | var timer *time.Timer 65 | q.mutex.Lock() 66 | // 有元素 67 | if !q.h.Empty() { 68 | // 获取元素 69 | entry := q.h.Peek() 70 | now := time.Now() 71 | if now.After(entry.expiration) { 72 | q.h.Pop() 73 | q.mutex.Unlock() 74 | return entry.value, true 75 | } 76 | // 到期时间,使用time.NewTimer()才能够调用Stop(),从而释放定时器 77 | timer = time.NewTimer(entry.expiration.Sub(now)) 78 | } 79 | // 走到这里表示需要等待了,设置为1告诉Push()在有新元素时要通知 80 | atomic.StoreInt32(&q.sleeping, 1) 81 | q.mutex.Unlock() 82 | 83 | // 不为空,需要同时等待元素到期,并且除非timer到期,否则都需要关闭timer避免泄露 84 | if timer != nil { 85 | select { 86 | case <-q.wakeup: // 新的更快到期元素 87 | timer.Stop() 88 | case <-timer.C: // 首元素到期 89 | // 设置为0,如果原来也为0表示有Push()正在q.wakeup被阻塞 90 | if atomic.SwapInt32(&q.sleeping, 0) == 0 { 91 | // 避免Push()的协程被阻塞 92 | <-q.wakeup 93 | } 94 | case <-ctx.Done(): // 被关闭 95 | timer.Stop() 96 | var t T 97 | return t, false 98 | } 99 | } else { 100 | select { 101 | case <-q.wakeup: // 新的更快到期元素 102 | case <-ctx.Done(): // 被关闭 103 | var t T 104 | return t, false 105 | } 106 | } 107 | } 108 | } 109 | 110 | // 返回一个通道,输出到期元素 111 | // size是通道缓存大小 112 | func (q *DelayQueue[T]) Channel(ctx context.Context, size int) <-chan T { 113 | out := make(chan T, size) 114 | go func() { 115 | for { 116 | entry, ok := q.Take(ctx) 117 | if !ok { 118 | close(out) 119 | return 120 | } 121 | out <- entry 122 | } 123 | }() 124 | return out 125 | } 126 | 127 | // 获取队头元素 128 | func (q *DelayQueue[T]) Peek() (T, bool) { 129 | q.mutex.Lock() 130 | defer q.mutex.Unlock() 131 | if q.h.Empty() { 132 | var t T 133 | return t, false 134 | } 135 | return q.h.Peek().value, true 136 | } 137 | 138 | // 获取到期元素 139 | func (q *DelayQueue[T]) Pop() (T, bool) { 140 | q.mutex.Lock() 141 | defer q.mutex.Unlock() 142 | // 没元素 143 | if q.h.Empty() { 144 | var t T 145 | return t, false 146 | } 147 | entry := q.h.Peek() 148 | // 还没元素到期 149 | if time.Now().Before(entry.expiration) { 150 | var t T 151 | return t, false 152 | } 153 | // 移除元素 154 | q.h.Pop() 155 | return entry.value, true 156 | } 157 | 158 | // 是否队列为空 159 | func (q *DelayQueue[T]) Empty() bool { 160 | q.mutex.Lock() 161 | defer q.mutex.Unlock() 162 | return q.h.Empty() 163 | } 164 | -------------------------------------------------------------------------------- /cache/fifo/fifo.go: -------------------------------------------------------------------------------- 1 | package fifo 2 | 3 | import ( 4 | "github.com/jiaxwu/gommon/cache" 5 | "github.com/jiaxwu/gommon/container/list" 6 | ) 7 | 8 | // 先进先出 9 | // 优点:公平 10 | // 非线程安全,请根据业务加锁 11 | type Cache[K comparable, V any] struct { 12 | entries map[K]*list.Element[*cache.Entry[K, V]] 13 | evictList *list.List[*cache.Entry[K, V]] 14 | capacity int 15 | onEvict cache.OnEvict[K, V] 16 | } 17 | 18 | func New[K comparable, V any](capacity int) *Cache[K, V] { 19 | if capacity < 1 { 20 | panic("too small capacity") 21 | } 22 | return &Cache[K, V]{ 23 | entries: make(map[K]*list.Element[*cache.Entry[K, V]]), 24 | evictList: list.New[*cache.Entry[K, V]](), 25 | capacity: capacity, 26 | } 27 | } 28 | 29 | // 设置 OnEvict 30 | func (c *Cache[K, V]) SetOnEvict(onEvict cache.OnEvict[K, V]) { 31 | c.onEvict = onEvict 32 | } 33 | 34 | // 添加或更新元素 35 | // 返回被淘汰的元素 36 | func (c *Cache[K, V]) Put(key K, value V) *cache.Entry[K, V] { 37 | // 如果 key 已经存在,直接把它移到最前面,然后设置新值 38 | if elem, ok := c.entries[key]; ok { 39 | c.evictList.MoveToFront(elem) 40 | elem.Value.Value = value 41 | return nil 42 | } 43 | 44 | // 如果已经到达最大尺寸,先剔除一个元素 45 | var evicted *cache.Entry[K, V] 46 | if c.Full() { 47 | evicted = c.Evict() 48 | } 49 | 50 | // 添加元素 51 | elem := c.evictList.PushFront(&cache.Entry[K, V]{ 52 | Key: key, 53 | Value: value, 54 | }) 55 | c.entries[key] = elem 56 | return evicted 57 | } 58 | 59 | // 获取元素 60 | func (c *Cache[K, V]) Get(key K) (V, bool) { 61 | return c.Peek(key) 62 | } 63 | 64 | // 获取元素 65 | func (c *Cache[K, V]) Peek(key K) (V, bool) { 66 | // 如果存在直接返回 67 | if elem, ok := c.entries[key]; ok { 68 | return elem.Value.Value, true 69 | } 70 | 71 | // 不存在返回空值和false 72 | var value V 73 | return value, false 74 | } 75 | 76 | // 是否包含元素,不更新状态 77 | func (c *Cache[K, V]) Contains(key K) bool { 78 | _, ok := c.entries[key] 79 | return ok 80 | } 81 | 82 | // 获取缓存的Keys 83 | func (c *Cache[K, V]) Keys() []K { 84 | keys := make([]K, c.Len()) 85 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 86 | keys[i] = elem.Value.Key 87 | } 88 | return keys 89 | } 90 | 91 | // 获取缓存的Values 92 | func (c *Cache[K, V]) Values() []V { 93 | values := make([]V, c.Len()) 94 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 95 | values[i] = elem.Value.Value 96 | } 97 | return values 98 | } 99 | 100 | // 获取缓存的Entries 101 | func (c *Cache[K, V]) Entries() []*cache.Entry[K, V] { 102 | entries := make([]*cache.Entry[K, V], c.Len()) 103 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 104 | entries[i] = elem.Value 105 | } 106 | return entries 107 | } 108 | 109 | // 移除元素 110 | func (c *Cache[K, V]) Remove(key K) bool { 111 | if elem, ok := c.entries[key]; ok { 112 | c.removeElement(elem) 113 | return true 114 | } 115 | return false 116 | } 117 | 118 | // 淘汰元素 119 | func (c *Cache[K, V]) Evict() *cache.Entry[K, V] { 120 | elem := c.evictList.Back() 121 | if elem == nil { 122 | return nil 123 | } 124 | c.removeElement(elem) 125 | // 回调 126 | if c.onEvict != nil { 127 | c.onEvict(elem.Value) 128 | } 129 | return elem.Value 130 | } 131 | 132 | // 清空缓存 133 | func (c *Cache[K, V]) Clear(needOnEvict bool) { 134 | // 触发回调 135 | if needOnEvict && c.onEvict != nil { 136 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 137 | c.onEvict(elem.Value) 138 | } 139 | } 140 | 141 | // 清空 142 | c.entries = make(map[K]*list.Element[*cache.Entry[K, V]]) 143 | c.evictList.Clear() 144 | } 145 | 146 | // 改变容量 147 | func (c *Cache[K, V]) Resize(capacity int, needOnEvict bool) { 148 | diff := c.Len() - capacity 149 | if diff < 0 { 150 | diff = 0 151 | } 152 | for i := 0; i < diff; i++ { 153 | c.Evict() 154 | } 155 | c.capacity = capacity 156 | } 157 | 158 | // 元素个数 159 | func (c *Cache[K, V]) Len() int { 160 | return len(c.entries) 161 | } 162 | 163 | // 容量 164 | func (c *Cache[K, V]) Cap() int { 165 | return c.capacity 166 | } 167 | 168 | // 缓存满了 169 | func (c *Cache[K, V]) Full() bool { 170 | return c.Len() == c.Cap() 171 | } 172 | 173 | // 移除给定节点 174 | func (c *Cache[K, V]) removeElement(elem *list.Element[*cache.Entry[K, V]]) { 175 | c.evictList.Remove(elem) 176 | entry := elem.Value 177 | delete(c.entries, entry.Key) 178 | } 179 | -------------------------------------------------------------------------------- /cache/arc/arc_test.go: -------------------------------------------------------------------------------- 1 | package arc 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/jiaxwu/gommon/cache" 9 | ) 10 | 11 | func TestCache_Put(t *testing.T) { 12 | c := New[string, int](3) 13 | c.Put("11", 5) 14 | c.Put("22", 6) 15 | c.Put("33", 7) 16 | c.Get("11") 17 | c.Put("44", 8) 18 | 19 | value, ok := c.Get("22") 20 | if value != 0 || ok { 21 | t.Errorf("Put() = %v, want %v", ok, false) 22 | } 23 | } 24 | 25 | func TestCache_OnEvict(t *testing.T) { 26 | c := New[string, int](3) 27 | c.SetOnEvict(func(entry *cache.Entry[string, int]) { 28 | if entry.Key != "22" || entry.Value != 6 { 29 | t.Errorf("OnEvict() = %v, want %v", entry.Key, "22") 30 | } 31 | }) 32 | c.Put("11", 5) 33 | c.Put("22", 6) 34 | c.Put("33", 7) 35 | c.Get("11") 36 | c.Put("44", 8) 37 | 38 | value, ok := c.Get("22") 39 | if value != 0 || ok { 40 | t.Errorf("Put() = %v, want %v", ok, false) 41 | } 42 | } 43 | 44 | func TestCache_Clear(t *testing.T) { 45 | c := New[string, int](3) 46 | c.Put("11", 5) 47 | c.Put("22", 6) 48 | c.Put("33", 7) 49 | c.Get("11") 50 | c.Put("44", 8) 51 | 52 | value, ok := c.Get("22") 53 | if value != 0 || ok { 54 | t.Errorf("Put() = %v, want %v", ok, false) 55 | } 56 | 57 | value, ok = c.Get("11") 58 | if value != 5 || !ok { 59 | t.Errorf("Put() = %v, want %v", ok, true) 60 | } 61 | 62 | c.Clear(false) 63 | value, ok = c.Get("11") 64 | if value != 0 || ok { 65 | t.Errorf("Put() = %v, want %v", ok, false) 66 | } 67 | } 68 | 69 | func TestCache_Peek(t *testing.T) { 70 | c := New[string, int](3) 71 | c.Put("11", 5) 72 | c.Put("22", 6) 73 | c.Put("33", 7) 74 | c.Peek("11") 75 | c.Put("44", 8) 76 | 77 | value, ok := c.Get("11") 78 | if value != 0 || ok { 79 | t.Errorf("Put() = %v, want %v", ok, false) 80 | } 81 | } 82 | 83 | func TestCache_Remove(t *testing.T) { 84 | c := New[string, int](3) 85 | c.Put("11", 5) 86 | c.Put("22", 6) 87 | c.Put("33", 7) 88 | c.Remove("22") 89 | c.Put("44", 8) 90 | 91 | value, ok := c.Get("22") 92 | if value != 0 || ok { 93 | t.Errorf("Get() = %v, want %v", value, 0) 94 | } 95 | } 96 | 97 | func TestCache_Entries(t *testing.T) { 98 | c := New[string, int](3) 99 | c.Put("11", 5) 100 | c.Put("22", 6) 101 | c.Put("33", 7) 102 | c.Get("11") 103 | c.Put("44", 8) 104 | 105 | entries := c.Entries() 106 | keys := []string{"33", "44", "11"} 107 | for i, entry := range entries { 108 | if entry.Key != keys[i] { 109 | t.Errorf("Get() = %v, want %v", entry.Key, keys[i]) 110 | } 111 | } 112 | } 113 | 114 | // arc_test.go:154: cachePercentage=0.1%, count=206048, hitCount=30244, hitRate=14.68% 115 | // arc_test.go:154: cachePercentage=0.3%, count=206048, hitCount=68373, hitRate=33.18% 116 | // arc_test.go:154: cachePercentage=0.5%, count=206048, hitCount=103926, hitRate=50.44% 117 | // arc_test.go:154: cachePercentage=0.7%, count=206048, hitCount=135787, hitRate=65.90% 118 | // arc_test.go:154: cachePercentage=1.0%, count=206048, hitCount=170632, hitRate=82.81% 119 | // arc_test.go:154: cachePercentage=2.0%, count=206048, hitCount=189194, hitRate=91.82% 120 | // arc_test.go:154: cachePercentage=3.0%, count=206048, hitCount=191151, hitRate=92.77% 121 | // arc_test.go:154: cachePercentage=5.0%, count=206048, hitCount=192620, hitRate=93.48% 122 | // arc_test.go:154: cachePercentage=10.0%, count=206048, hitCount=192842, hitRate=93.59% 123 | func TestHitRate(t *testing.T) { 124 | dataset, err := os.ReadFile("../dataset") 125 | if err != nil { 126 | t.Errorf("read dataset error %v", err) 127 | } 128 | reqs := strings.Split(string(dataset), ",") 129 | testHitRate(t, reqs, 0.001) 130 | testHitRate(t, reqs, 0.003) 131 | testHitRate(t, reqs, 0.005) 132 | testHitRate(t, reqs, 0.007) 133 | testHitRate(t, reqs, 0.01) 134 | testHitRate(t, reqs, 0.02) 135 | testHitRate(t, reqs, 0.03) 136 | testHitRate(t, reqs, 0.05) 137 | testHitRate(t, reqs, 0.1) 138 | } 139 | 140 | func testHitRate(t *testing.T, reqs []string, cachePercentage float64) { 141 | count := len(reqs) 142 | n := int(float64(count) * cachePercentage) 143 | c := New[string, int](n) 144 | hitCount := 0 145 | for _, req := range reqs { 146 | _, exists := c.Get(req) 147 | if exists { 148 | hitCount++ 149 | } else { 150 | c.Put(req, 0) 151 | } 152 | } 153 | hitRate := float64(hitCount) / float64(count) 154 | t.Logf("cachePercentage=%.1f%%, count=%v, hitCount=%v, hitRate=%.2f%%", cachePercentage*100, count, hitCount, hitRate*100) 155 | } 156 | -------------------------------------------------------------------------------- /timer/timingwheel/timingwheel.go: -------------------------------------------------------------------------------- 1 | package timingwheel 2 | 3 | import ( 4 | "context" 5 | "sync/atomic" 6 | "time" 7 | "unsafe" 8 | ) 9 | 10 | const delayQueueBufferSize = 10 // 延迟队列缓冲区大小 11 | 12 | // 时间轮 13 | // 单位都是毫秒 14 | // 基于https://github.com/RussellLuo/timingwheel的实现 15 | // 性能不如标准的time.AfterFunc(),只是作为学习时间轮 16 | type TimingWheel struct { 17 | tick int64 // 每一跳的时间 18 | wheelSize int64 // 时间轮 19 | interval int64 // 一圈的时间 20 | currentTime int64 // 当前时间 21 | buckets []*bucket // 时间轮的每个桶 22 | queue *delayQueue // 桶延迟队列 23 | overflowWheel unsafe.Pointer // 上一个时间轮 24 | } 25 | 26 | // tick的单位是毫秒 27 | func New(tick, wheelSize int64) *TimingWheel { 28 | return newTimingWheel(tick, wheelSize, time.Now().UnixMilli(), newDelayQueue()) 29 | } 30 | 31 | func newTimingWheel(tick, wheelSize, currentTime int64, queue *delayQueue) *TimingWheel { 32 | tw := &TimingWheel{ 33 | tick: tick, 34 | wheelSize: wheelSize, 35 | interval: tick * wheelSize, 36 | currentTime: truncate(currentTime, tick), 37 | buckets: make([]*bucket, wheelSize), 38 | queue: queue, 39 | } 40 | for i := 0; i < int(wheelSize); i++ { 41 | tw.buckets[i] = newBucket() 42 | } 43 | return tw 44 | } 45 | 46 | // 运行时间轮 47 | func (tw *TimingWheel) Run(ctx context.Context) { 48 | bucketChan := tw.queue.channel(ctx, delayQueueBufferSize, func() int64 { 49 | return time.Now().UnixMilli() 50 | }) 51 | for { 52 | select { 53 | case b := <-bucketChan: // 桶到期 54 | // 前进当前时间 55 | tw.advance(b.expiration) 56 | // 处理桶 57 | b.flush(tw.addOrRun) 58 | case <-ctx.Done(): // 被关闭 59 | return 60 | } 61 | } 62 | } 63 | 64 | // 添加定时器 65 | func (tw *TimingWheel) AfterFunc(delay time.Duration, f func()) *Timer { 66 | t := &Timer{ 67 | expiration: time.Now().Add(delay).UnixMilli(), 68 | task: f, 69 | } 70 | tw.add(t) 71 | return t 72 | } 73 | 74 | type Scheduler interface { 75 | // 表示下一个执行任务的时间 76 | // 如果time.IsZero()==true则不再进行 77 | Next(time.Time) time.Time 78 | } 79 | 80 | func (tw *TimingWheel) ScheduleFunc(s Scheduler, f func()) (t *Timer) { 81 | expiration := s.Next(time.Now()) 82 | if expiration.IsZero() { 83 | return 84 | } 85 | 86 | t = &Timer{ 87 | expiration: expiration.UnixMilli(), 88 | task: func() { 89 | // 添加下一次执行任务 90 | expiration := s.Next(time.UnixMilli(t.expiration)) 91 | if !expiration.IsZero() { 92 | t.expiration = expiration.UnixMilli() 93 | tw.addOrRun(t) 94 | } 95 | // 执行任务 96 | f() 97 | }, 98 | } 99 | tw.addOrRun(t) 100 | return 101 | } 102 | 103 | // 添加定时器 104 | func (tw *TimingWheel) add(t *Timer) bool { 105 | currentTime := atomic.LoadInt64(&tw.currentTime) 106 | if t.expiration < currentTime+tw.tick { // 已经过期了 107 | return false 108 | } else if t.expiration < currentTime+tw.interval { // 在当前时间轮里 109 | // 多少跳了 110 | ticks := t.expiration / tw.tick 111 | // 应该在时间轮的哪个桶里 112 | b := tw.buckets[ticks%tw.wheelSize] 113 | b.add(t) 114 | 115 | // 如果设置桶过期时间成功 116 | // 表示这个桶第一次加入定时器,因此应该把它放到延迟队列里面去等待到期 117 | if b.setExpiration(ticks * tw.tick) { 118 | tw.queue.push(b) 119 | } 120 | return true 121 | } else { // 在其他时间轮里 122 | overflowWheel := atomic.LoadPointer(&tw.overflowWheel) 123 | if overflowWheel == nil { 124 | tw.setOverflowWheel(currentTime) 125 | overflowWheel = atomic.LoadPointer(&tw.overflowWheel) 126 | } 127 | return (*TimingWheel)(overflowWheel).add(t) 128 | } 129 | } 130 | 131 | // 添加任务或运行 132 | func (tw *TimingWheel) addOrRun(t *Timer) { 133 | if !tw.add(t) { 134 | go t.task() 135 | } 136 | } 137 | 138 | // 前进时间 139 | func (tw *TimingWheel) advance(expiration int64) { 140 | currentTime := atomic.LoadInt64(&tw.currentTime) 141 | if expiration >= currentTime+tw.tick { 142 | currentTime := truncate(expiration, tw.tick) 143 | atomic.StoreInt64(&tw.currentTime, currentTime) 144 | 145 | overflowWheel := atomic.LoadPointer(&tw.overflowWheel) 146 | if overflowWheel != nil { 147 | (*TimingWheel)(overflowWheel).advance(currentTime) 148 | } 149 | } 150 | } 151 | 152 | func (tw *TimingWheel) setOverflowWheel(currentTime int64) { 153 | overflowWheel := newTimingWheel(tw.interval, tw.wheelSize, currentTime, tw.queue) 154 | atomic.CompareAndSwapPointer(&tw.overflowWheel, nil, unsafe.Pointer(overflowWheel)) 155 | } 156 | 157 | // 去除不满一整跳的时间 158 | func truncate(time, tick int64) int64 { 159 | return time - time%tick 160 | } 161 | -------------------------------------------------------------------------------- /cache/lru/lru.go: -------------------------------------------------------------------------------- 1 | package lru 2 | 3 | import ( 4 | "github.com/jiaxwu/gommon/cache" 5 | "github.com/jiaxwu/gommon/container/list" 6 | ) 7 | 8 | // 最近最少使用 9 | // 优点:稳定淘汰 10 | // 非线程安全,请根据业务加锁 11 | type Cache[K comparable, V any] struct { 12 | entries map[K]*list.Element[*cache.Entry[K, V]] 13 | evictList *list.List[*cache.Entry[K, V]] 14 | capacity int 15 | onEvict cache.OnEvict[K, V] 16 | } 17 | 18 | func New[K comparable, V any](capacity int) *Cache[K, V] { 19 | if capacity < 1 { 20 | panic("too small capacity") 21 | } 22 | return &Cache[K, V]{ 23 | entries: make(map[K]*list.Element[*cache.Entry[K, V]]), 24 | evictList: list.New[*cache.Entry[K, V]](), 25 | capacity: capacity, 26 | } 27 | } 28 | 29 | // 设置 OnEvict 30 | func (c *Cache[K, V]) SetOnEvict(onEvict cache.OnEvict[K, V]) { 31 | c.onEvict = onEvict 32 | } 33 | 34 | // 添加或更新元素 35 | // 返回被淘汰的元素 36 | func (c *Cache[K, V]) Put(key K, value V) *cache.Entry[K, V] { 37 | // 如果 key 已经存在,直接把它移到最前面,然后设置新值 38 | if elem, ok := c.entries[key]; ok { 39 | c.evictList.MoveToFront(elem) 40 | elem.Value.Value = value 41 | return nil 42 | } 43 | 44 | // 如果已经到达最大尺寸,先剔除一个元素 45 | var evicted *cache.Entry[K, V] 46 | if c.Full() { 47 | evicted = c.Evict() 48 | } 49 | 50 | // 添加元素 51 | elem := c.evictList.PushFront(&cache.Entry[K, V]{ 52 | Key: key, 53 | Value: value, 54 | }) 55 | c.entries[key] = elem 56 | return evicted 57 | } 58 | 59 | // 获取元素 60 | func (c *Cache[K, V]) Get(key K) (V, bool) { 61 | // 如果存在移动到头部,然后返回 62 | if elem, ok := c.entries[key]; ok { 63 | c.evictList.MoveToFront(elem) 64 | return elem.Value.Value, true 65 | } 66 | 67 | // 不存在返回空值和false 68 | var value V 69 | return value, false 70 | } 71 | 72 | // 获取元素,不更新状态 73 | func (c *Cache[K, V]) Peek(key K) (V, bool) { 74 | // 如果存在 75 | if elem, ok := c.entries[key]; ok { 76 | return elem.Value.Value, true 77 | } 78 | 79 | // 不存在返回空值和false 80 | var value V 81 | return value, false 82 | } 83 | 84 | // 是否包含元素,不更新状态 85 | func (c *Cache[K, V]) Contains(key K) bool { 86 | _, ok := c.entries[key] 87 | return ok 88 | } 89 | 90 | // 获取缓存的Keys 91 | func (c *Cache[K, V]) Keys() []K { 92 | keys := make([]K, c.Len()) 93 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 94 | keys[i] = elem.Value.Key 95 | } 96 | return keys 97 | } 98 | 99 | // 获取缓存的Values 100 | func (c *Cache[K, V]) Values() []V { 101 | values := make([]V, c.Len()) 102 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 103 | values[i] = elem.Value.Value 104 | } 105 | return values 106 | } 107 | 108 | // 获取缓存的Entries 109 | func (c *Cache[K, V]) Entries() []*cache.Entry[K, V] { 110 | entries := make([]*cache.Entry[K, V], c.Len()) 111 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 112 | entries[i] = elem.Value 113 | } 114 | return entries 115 | } 116 | 117 | // 移除元素 118 | func (c *Cache[K, V]) Remove(key K) bool { 119 | if elem, ok := c.entries[key]; ok { 120 | c.removeElement(elem) 121 | return true 122 | } 123 | return false 124 | } 125 | 126 | // 淘汰元素 127 | func (c *Cache[K, V]) Evict() *cache.Entry[K, V] { 128 | elem := c.evictList.Back() 129 | if elem == nil { 130 | return nil 131 | } 132 | c.removeElement(elem) 133 | // 回调 134 | if c.onEvict != nil { 135 | c.onEvict(elem.Value) 136 | } 137 | return elem.Value 138 | } 139 | 140 | // 获取可能被淘汰的元素 141 | func (c *Cache[K, V]) Victim() *cache.Entry[K, V] { 142 | elem := c.evictList.Back() 143 | if elem == nil { 144 | return nil 145 | } 146 | return elem.Value 147 | } 148 | 149 | // 清空缓存 150 | func (c *Cache[K, V]) Clear(needOnEvict bool) { 151 | // 触发回调 152 | if needOnEvict && c.onEvict != nil { 153 | for elem, i := c.evictList.Back(), 0; elem != nil; elem, i = elem.Prev(), i+1 { 154 | c.onEvict(elem.Value) 155 | } 156 | } 157 | 158 | // 清空 159 | c.entries = make(map[K]*list.Element[*cache.Entry[K, V]]) 160 | c.evictList.Clear() 161 | } 162 | 163 | // 改变容量 164 | func (c *Cache[K, V]) Resize(capacity int, needOnEvict bool) { 165 | diff := c.Len() - capacity 166 | if diff < 0 { 167 | diff = 0 168 | } 169 | for i := 0; i < diff; i++ { 170 | c.Evict() 171 | } 172 | c.capacity = capacity 173 | } 174 | 175 | // 元素个数 176 | func (c *Cache[K, V]) Len() int { 177 | return len(c.entries) 178 | } 179 | 180 | // 容量 181 | func (c *Cache[K, V]) Cap() int { 182 | return c.capacity 183 | } 184 | 185 | // 缓存满了 186 | func (c *Cache[K, V]) Full() bool { 187 | return c.Len() == c.Cap() 188 | } 189 | 190 | // 移除给定节点 191 | func (c *Cache[K, V]) removeElement(elem *list.Element[*cache.Entry[K, V]]) { 192 | c.evictList.Remove(elem) 193 | entry := elem.Value 194 | delete(c.entries, entry.Key) 195 | } 196 | -------------------------------------------------------------------------------- /cache/lru/lru_test.go: -------------------------------------------------------------------------------- 1 | package lru 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/jiaxwu/gommon/cache" 9 | ) 10 | 11 | func TestCache_Put(t *testing.T) { 12 | c := New[string, int](3) 13 | c.Put("11", 5) 14 | c.Put("22", 6) 15 | c.Put("33", 7) 16 | c.Get("11") 17 | c.Put("44", 8) 18 | 19 | value, ok := c.Get("22") 20 | if value != 0 || ok { 21 | t.Errorf("Put() = %v, want %v", ok, false) 22 | } 23 | } 24 | 25 | func TestCache_OnEvict(t *testing.T) { 26 | c := New[string, int](3) 27 | c.SetOnEvict(func(entry *cache.Entry[string, int]) { 28 | if entry.Key != "22" || entry.Value != 6 { 29 | t.Errorf("OnEvict() = %v, want %v", entry.Key, "22") 30 | } 31 | }) 32 | c.Put("11", 5) 33 | c.Put("22", 6) 34 | c.Put("33", 7) 35 | c.Get("11") 36 | c.Put("44", 8) 37 | 38 | value, ok := c.Get("22") 39 | if value != 0 || ok { 40 | t.Errorf("Put() = %v, want %v", ok, false) 41 | } 42 | } 43 | 44 | func TestCache_Clear(t *testing.T) { 45 | c := New[string, int](3) 46 | c.Put("11", 5) 47 | c.Put("22", 6) 48 | c.Put("33", 7) 49 | c.Get("11") 50 | c.Put("44", 8) 51 | 52 | value, ok := c.Get("22") 53 | if value != 0 || ok { 54 | t.Errorf("Put() = %v, want %v", ok, false) 55 | } 56 | 57 | value, ok = c.Get("11") 58 | if value != 5 || !ok { 59 | t.Errorf("Put() = %v, want %v", ok, true) 60 | } 61 | 62 | c.Clear(false) 63 | value, ok = c.Get("11") 64 | if value != 0 || ok { 65 | t.Errorf("Put() = %v, want %v", ok, false) 66 | } 67 | } 68 | 69 | func TestCache_Peek(t *testing.T) { 70 | c := New[string, int](3) 71 | c.Put("11", 5) 72 | c.Put("22", 6) 73 | c.Put("33", 7) 74 | c.Peek("11") 75 | c.Put("44", 8) 76 | 77 | value, ok := c.Get("11") 78 | if value != 0 || ok { 79 | t.Errorf("Put() = %v, want %v", ok, false) 80 | } 81 | } 82 | 83 | func TestCache_Remove(t *testing.T) { 84 | c := New[string, int](3) 85 | c.Put("11", 5) 86 | c.Put("22", 6) 87 | c.Put("33", 7) 88 | c.Remove("22") 89 | c.Put("44", 8) 90 | 91 | value, ok := c.Get("22") 92 | if value != 0 || ok { 93 | t.Errorf("Get() = %v, want %v", value, 0) 94 | } 95 | } 96 | 97 | func TestCache_Evict(t *testing.T) { 98 | c := New[string, int](3) 99 | c.Put("11", 5) 100 | c.Put("22", 6) 101 | c.Put("33", 7) 102 | c.Evict() 103 | c.Put("44", 8) 104 | 105 | value, ok := c.Get("11") 106 | if value != 0 || ok { 107 | t.Errorf("Get() = %v, want %v", value, 0) 108 | } 109 | } 110 | 111 | func TestCache_Entries(t *testing.T) { 112 | c := New[string, int](3) 113 | c.Put("11", 5) 114 | c.Put("22", 6) 115 | c.Put("33", 7) 116 | c.Get("11") 117 | c.Put("44", 8) 118 | 119 | entries := c.Entries() 120 | keys := []string{"33", "11", "44"} 121 | for i, entry := range entries { 122 | if entry.Key != keys[i] { 123 | t.Errorf("Get() = %v, want %v", entry.Key, keys[i]) 124 | } 125 | } 126 | } 127 | 128 | // lru_test.go:168: cachePercentage=0.1%, count=206048, hitCount=26717, hitRate=12.97% 129 | // lru_test.go:168: cachePercentage=0.3%, count=206048, hitCount=58169, hitRate=28.23% 130 | // lru_test.go:168: cachePercentage=0.5%, count=206048, hitCount=87446, hitRate=42.44% 131 | // lru_test.go:168: cachePercentage=0.7%, count=206048, hitCount=114358, hitRate=55.50% 132 | // lru_test.go:168: cachePercentage=1.0%, count=206048, hitCount=148556, hitRate=72.10% 133 | // lru_test.go:168: cachePercentage=2.0%, count=206048, hitCount=187286, hitRate=90.89% 134 | // lru_test.go:168: cachePercentage=3.0%, count=206048, hitCount=190649, hitRate=92.53% 135 | // lru_test.go:168: cachePercentage=5.0%, count=206048, hitCount=192606, hitRate=93.48% 136 | // lru_test.go:168: cachePercentage=10.0%, count=206048, hitCount=192842, hitRate=93.59% 137 | func TestHitRate(t *testing.T) { 138 | dataset, err := os.ReadFile("../dataset") 139 | if err != nil { 140 | t.Errorf("read dataset error %v", err) 141 | } 142 | reqs := strings.Split(string(dataset), ",") 143 | testHitRate(t, reqs, 0.001) 144 | testHitRate(t, reqs, 0.003) 145 | testHitRate(t, reqs, 0.005) 146 | testHitRate(t, reqs, 0.007) 147 | testHitRate(t, reqs, 0.01) 148 | testHitRate(t, reqs, 0.02) 149 | testHitRate(t, reqs, 0.03) 150 | testHitRate(t, reqs, 0.05) 151 | testHitRate(t, reqs, 0.1) 152 | } 153 | 154 | func testHitRate(t *testing.T, reqs []string, cachePercentage float64) { 155 | count := len(reqs) 156 | n := int(float64(count) * cachePercentage) 157 | c := New[string, int](n) 158 | hitCount := 0 159 | for _, req := range reqs { 160 | _, exists := c.Get(req) 161 | if exists { 162 | hitCount++ 163 | } else { 164 | c.Put(req, 0) 165 | } 166 | } 167 | hitRate := float64(hitCount) / float64(count) 168 | t.Logf("cachePercentage=%.1f%%, count=%v, hitCount=%v, hitRate=%.2f%%", cachePercentage*100, count, hitCount, hitRate*100) 169 | } 170 | -------------------------------------------------------------------------------- /cache/fifo/fifo_test.go: -------------------------------------------------------------------------------- 1 | package fifo 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/jiaxwu/gommon/cache" 9 | ) 10 | 11 | func TestCache_Put(t *testing.T) { 12 | c := New[string, int](3) 13 | c.Put("11", 5) 14 | c.Put("22", 6) 15 | c.Put("33", 7) 16 | c.Get("11") 17 | c.Put("44", 8) 18 | 19 | value, ok := c.Get("11") 20 | if value != 0 || ok { 21 | t.Errorf("Put() = %v, want %v", ok, false) 22 | } 23 | } 24 | 25 | func TestCache_OnEvict(t *testing.T) { 26 | c := New[string, int](3) 27 | c.SetOnEvict(func(entry *cache.Entry[string, int]) { 28 | if entry.Key != "11" || entry.Value != 5 { 29 | t.Errorf("OnEvict() = %v, want %v", entry.Key, "11") 30 | } 31 | }) 32 | c.Put("11", 5) 33 | c.Put("22", 6) 34 | c.Put("33", 7) 35 | c.Get("11") 36 | c.Put("44", 8) 37 | 38 | value, ok := c.Get("11") 39 | if value != 0 || ok { 40 | t.Errorf("Put() = %v, want %v", ok, false) 41 | } 42 | } 43 | 44 | func TestCache_Clear(t *testing.T) { 45 | c := New[string, int](3) 46 | c.Put("11", 5) 47 | c.Put("22", 6) 48 | c.Put("33", 7) 49 | c.Get("11") 50 | c.Put("44", 8) 51 | 52 | value, ok := c.Get("11") 53 | if value != 0 || ok { 54 | t.Errorf("Put() = %v, want %v", ok, false) 55 | } 56 | 57 | value, ok = c.Get("22") 58 | if value != 6 || !ok { 59 | t.Errorf("Put() = %v, want %v", ok, true) 60 | } 61 | 62 | c.Clear(false) 63 | value, ok = c.Get("11") 64 | if value != 0 || ok { 65 | t.Errorf("Put() = %v, want %v", ok, false) 66 | } 67 | } 68 | 69 | func TestCache_Peek(t *testing.T) { 70 | c := New[string, int](3) 71 | c.Put("11", 5) 72 | c.Put("22", 6) 73 | c.Put("33", 7) 74 | c.Peek("11") 75 | c.Put("44", 8) 76 | 77 | value, ok := c.Get("11") 78 | if value != 0 || ok { 79 | t.Errorf("Put() = %v, want %v", ok, false) 80 | } 81 | } 82 | 83 | func TestCache_Remove(t *testing.T) { 84 | c := New[string, int](3) 85 | c.Put("11", 5) 86 | c.Put("22", 6) 87 | c.Put("33", 7) 88 | c.Remove("22") 89 | c.Put("44", 8) 90 | 91 | value, ok := c.Get("22") 92 | if value != 0 || ok { 93 | t.Errorf("Get() = %v, want %v", value, 0) 94 | } 95 | } 96 | 97 | func TestCache_Evict(t *testing.T) { 98 | c := New[string, int](3) 99 | c.Put("11", 5) 100 | c.Put("22", 6) 101 | c.Put("33", 7) 102 | c.Evict() 103 | c.Put("44", 8) 104 | 105 | value, ok := c.Get("11") 106 | if value != 0 || ok { 107 | t.Errorf("Get() = %v, want %v", value, 0) 108 | } 109 | } 110 | 111 | func TestCache_Entries(t *testing.T) { 112 | c := New[string, int](3) 113 | c.Put("11", 5) 114 | c.Put("22", 6) 115 | c.Put("33", 7) 116 | c.Get("11") 117 | c.Put("44", 8) 118 | 119 | entries := c.Entries() 120 | keys := []string{"22", "33", "44"} 121 | for i, entry := range entries { 122 | if entry.Key != keys[i] { 123 | t.Errorf("Get() = %v, want %v", entry.Key, keys[i]) 124 | } 125 | } 126 | } 127 | 128 | // fifo_test.go:168: cachePercentage=0.1%, count=206048, hitCount=26556, hitRate=12.89% 129 | // fifo_test.go:168: cachePercentage=0.3%, count=206048, hitCount=56624, hitRate=27.48% 130 | // fifo_test.go:168: cachePercentage=0.5%, count=206048, hitCount=83375, hitRate=40.46% 131 | // fifo_test.go:168: cachePercentage=0.7%, count=206048, hitCount=106314, hitRate=51.60% 132 | // fifo_test.go:168: cachePercentage=1.0%, count=206048, hitCount=133571, hitRate=64.83% 133 | // fifo_test.go:168: cachePercentage=2.0%, count=206048, hitCount=173169, hitRate=84.04% 134 | // fifo_test.go:168: cachePercentage=3.0%, count=206048, hitCount=183426, hitRate=89.02% 135 | // fifo_test.go:168: cachePercentage=5.0%, count=206048, hitCount=189734, hitRate=92.08% 136 | // fifo_test.go:168: cachePercentage=10.0%, count=206048, hitCount=192842, hitRate=93.59% 137 | func TestHitRate(t *testing.T) { 138 | dataset, err := os.ReadFile("../dataset") 139 | if err != nil { 140 | t.Errorf("read dataset error %v", err) 141 | } 142 | reqs := strings.Split(string(dataset), ",") 143 | testHitRate(t, reqs, 0.001) 144 | testHitRate(t, reqs, 0.003) 145 | testHitRate(t, reqs, 0.005) 146 | testHitRate(t, reqs, 0.007) 147 | testHitRate(t, reqs, 0.01) 148 | testHitRate(t, reqs, 0.02) 149 | testHitRate(t, reqs, 0.03) 150 | testHitRate(t, reqs, 0.05) 151 | testHitRate(t, reqs, 0.1) 152 | } 153 | 154 | func testHitRate(t *testing.T, reqs []string, cachePercentage float64) { 155 | count := len(reqs) 156 | n := int(float64(count) * cachePercentage) 157 | c := New[string, int](n) 158 | hitCount := 0 159 | for _, req := range reqs { 160 | _, exists := c.Get(req) 161 | if exists { 162 | hitCount++ 163 | } else { 164 | c.Put(req, 0) 165 | } 166 | } 167 | hitRate := float64(hitCount) / float64(count) 168 | t.Logf("cachePercentage=%.1f%%, count=%v, hitCount=%v, hitRate=%.2f%%", cachePercentage*100, count, hitCount, hitRate*100) 169 | } 170 | -------------------------------------------------------------------------------- /counter/cm/cm_test.go: -------------------------------------------------------------------------------- 1 | package cm 2 | 3 | import ( 4 | "hash/fnv" 5 | "math" 6 | "strconv" 7 | "testing" 8 | ) 9 | 10 | func TestCount(t *testing.T) { 11 | cm := New[uint8](1000, 10, 0.001) 12 | cm.AddString("10", 1) 13 | cm.AddString("51151", 1) 14 | cm.AddString("321", 1) 15 | cm.AddString("10", 1) 16 | cm.AddString("10", 1) 17 | cm.AddString("321", 1) 18 | if cm.EstimateString("10") != 3 { 19 | t.Errorf("want %v, but %d", 3, cm.EstimateString("10")) 20 | } 21 | if cm.EstimateString("321") != 2 { 22 | t.Errorf("want %v, but %d", 2, cm.EstimateString("321")) 23 | } 24 | if cm.EstimateString("51151") != 1 { 25 | t.Errorf("want %v, but %d", 1, cm.EstimateString("1")) 26 | } 27 | 28 | cm.AddString("10", 100) 29 | if cm.EstimateString("10") != 103 { 30 | t.Errorf("want %v, but %d", 103, cm.EstimateString("10")) 31 | } 32 | cm.AddString("10", 254) 33 | if cm.EstimateString("10") != 255 { 34 | t.Errorf("want %v, but %d", 255, cm.EstimateString("10")) 35 | } 36 | cm.AddString("5", 100) 37 | if cm.EstimateString("5") != 100 { 38 | t.Errorf("want %v, but %d", 100, cm.EstimateString("5")) 39 | } 40 | cm.AddString("1", 100) 41 | if cm.EstimateString("1") != 100 { 42 | t.Errorf("want %v, but %d", 100, cm.EstimateString("1")) 43 | } 44 | 45 | cm.Attenuation(2) 46 | if cm.EstimateString("10") != 127 { 47 | t.Errorf("want %v, but %d", 127, cm.EstimateString("10")) 48 | } 49 | if cm.EstimateString("5") != 50 { 50 | t.Errorf("want %v, but %d", 50, cm.EstimateString("5")) 51 | } 52 | if cm.EstimateString("1") != 50 { 53 | t.Errorf("want %v, but %d", 50, cm.EstimateString("1")) 54 | } 55 | } 56 | 57 | func TestExpectedErrorAndErrorRate(t *testing.T) { 58 | capacity := uint64(1000000) 59 | errorRange := uint32(1) 60 | errorRate := 0.001 61 | cm := New(capacity, errorRange, errorRate) 62 | // 添加计数值 63 | for i := uint64(0); i < capacity; i++ { 64 | cm.Add(i, 1) 65 | } 66 | // 评估 67 | errorCount := 0 68 | errorSum := 0 69 | for i := uint64(0); i < capacity; i++ { 70 | val := cm.Estimate(i) 71 | if val > 1+errorRange { 72 | errorCount++ 73 | errorSum += int(val) - 1 74 | } 75 | } 76 | estimateErrorRate := float64(errorCount) / float64(capacity) 77 | estimateError := float64(errorSum) / math.Max(1, float64(errorCount)) 78 | if estimateErrorRate > errorRate { 79 | t.Errorf("errorRate not accuracy %v", estimateErrorRate) 80 | } 81 | if estimateError > float64(errorRange) { 82 | t.Errorf("errorRange not accuracy %v", estimateError) 83 | } 84 | } 85 | 86 | func BenchmarkAddAndEstimateBytes(b *testing.B) { 87 | buf := make([]byte, 8192) 88 | for length := 1; length <= cap(buf); length *= 2 { 89 | b.Run(strconv.Itoa(length), func(b *testing.B) { 90 | f := New[uint8](uint64(b.N), 10, 0.0001) 91 | buf = buf[:length] 92 | b.SetBytes(int64(length)) 93 | b.ReportAllocs() 94 | b.ResetTimer() 95 | for i := 0; i < b.N; i++ { 96 | f.AddBytes(buf, 1) 97 | f.EstimateBytes(buf) 98 | } 99 | }) 100 | } 101 | } 102 | 103 | func BenchmarkAddAndEstimate(b *testing.B) { 104 | for length := 1; length <= 8192; length *= 2 { 105 | b.Run(strconv.Itoa(length), func(b *testing.B) { 106 | f := New[uint8](uint64(b.N), 10, 0.0001) 107 | b.ReportAllocs() 108 | b.ResetTimer() 109 | for i := 0; i < b.N; i++ { 110 | f.Add(uint64(i), 1) 111 | f.Estimate(uint64(i)) 112 | } 113 | }) 114 | } 115 | } 116 | 117 | func FuzzAddAndEstimate(f *testing.F) { 118 | seeds := []string{"abc", "bbb", "0", "1", ""} 119 | for _, seed := range seeds { 120 | f.Add(seed) 121 | } 122 | n := uint64(1000000) 123 | errorRange := uint64(10) 124 | errorRate := 0.001 125 | cm := New(n, errorRange, errorRate) 126 | m := map[uint64]uint64{} 127 | count := uint64(0) 128 | f.Fuzz(func(t *testing.T, a string) { 129 | h := fnv.New64() 130 | h.Write([]byte(a)) 131 | hashValue := h.Sum64() 132 | cm.Add(hashValue, 1) 133 | m[hashValue]++ 134 | count++ 135 | if count%n == 0 { 136 | errorCount := uint64(0) 137 | errorSum := uint64(0) 138 | for k, count := range m { 139 | estimateCount := cm.Estimate(k) 140 | if count+errorRange < estimateCount { 141 | errorCount++ 142 | errorSum += (estimateCount - count) 143 | } 144 | } 145 | if errorCount != 0 { 146 | errorRateResult := float64(errorCount) / float64(n) 147 | errorRangeResult := errorSum / errorCount 148 | if errorRateResult > errorRate { 149 | t.Errorf("count=%v, errorCount=%v, errorSum=%v, errorRate=%f, errorRange=%v", count, errorCount, errorSum, errorRateResult, errorRangeResult) 150 | } 151 | } 152 | } 153 | }) 154 | } 155 | -------------------------------------------------------------------------------- /cache/slru/slru_test.go: -------------------------------------------------------------------------------- 1 | package slru 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/jiaxwu/gommon/cache" 9 | ) 10 | 11 | func TestCache_Put(t *testing.T) { 12 | c := New[string, int](3) 13 | c.Put("11", 5) 14 | c.Put("22", 6) 15 | c.Put("33", 7) 16 | c.Get("11") 17 | c.Put("44", 8) 18 | 19 | value, ok := c.Get("22") 20 | if value != 0 || ok { 21 | t.Errorf("Put() = %v, want %v", ok, false) 22 | } 23 | } 24 | 25 | func TestCache_OnEvict(t *testing.T) { 26 | c := New[string, int](3) 27 | c.SetOnEvict(func(entry *cache.Entry[string, int]) { 28 | if entry.Key != "22" || entry.Value != 6 { 29 | t.Errorf("OnEvict() = %v, want %v", entry.Key, "22") 30 | } 31 | }) 32 | c.Put("11", 5) 33 | c.Put("22", 6) 34 | c.Put("33", 7) 35 | c.Get("11") 36 | c.Put("44", 8) 37 | 38 | value, ok := c.Get("22") 39 | if value != 0 || ok { 40 | t.Errorf("Put() = %v, want %v", ok, false) 41 | } 42 | } 43 | 44 | func TestCache_Clear(t *testing.T) { 45 | c := New[string, int](3) 46 | c.Put("11", 5) 47 | c.Put("22", 6) 48 | c.Put("33", 7) 49 | c.Get("11") 50 | c.Put("44", 8) 51 | 52 | value, ok := c.Get("22") 53 | if value != 0 || ok { 54 | t.Errorf("Put() = %v, want %v", ok, false) 55 | } 56 | 57 | value, ok = c.Get("11") 58 | if value != 5 || !ok { 59 | t.Errorf("Put() = %v, want %v", ok, true) 60 | } 61 | 62 | c.Clear(false) 63 | value, ok = c.Get("11") 64 | if value != 0 || ok { 65 | t.Errorf("Put() = %v, want %v", ok, false) 66 | } 67 | } 68 | 69 | func TestCache_Peek(t *testing.T) { 70 | c := New[string, int](3) 71 | c.Put("11", 5) 72 | c.Put("22", 6) 73 | c.Put("33", 7) 74 | c.Peek("11") 75 | c.Put("44", 8) 76 | 77 | value, ok := c.Get("11") 78 | if value != 0 || ok { 79 | t.Errorf("Put() = %v, want %v", ok, false) 80 | } 81 | } 82 | 83 | func TestCache_Remove(t *testing.T) { 84 | c := New[string, int](3) 85 | c.Put("11", 5) 86 | c.Put("22", 6) 87 | c.Put("33", 7) 88 | c.Remove("22") 89 | c.Put("44", 8) 90 | 91 | value, ok := c.Get("22") 92 | if value != 0 || ok { 93 | t.Errorf("Get() = %v, want %v", value, 0) 94 | } 95 | } 96 | 97 | func TestCache_Evict(t *testing.T) { 98 | c := New[string, int](3) 99 | c.Put("11", 5) 100 | c.Put("22", 6) 101 | c.Put("33", 7) 102 | c.Evict() 103 | c.Put("44", 8) 104 | 105 | value, ok := c.Get("11") 106 | if value != 0 || ok { 107 | t.Errorf("Get() = %v, want %v", value, 0) 108 | } 109 | } 110 | 111 | func TestCache_Entries(t *testing.T) { 112 | c := New[string, int](3) 113 | c.Put("11", 5) 114 | c.Put("22", 6) 115 | c.Put("33", 7) 116 | c.Get("11") 117 | c.Put("44", 8) 118 | 119 | entries := c.Entries() 120 | keys := []string{"33", "44", "11"} 121 | for i, entry := range entries { 122 | if entry.Key != keys[i] { 123 | t.Errorf("Get() = %v, want %v", entry.Key, keys[i]) 124 | } 125 | } 126 | } 127 | 128 | // slru_test.go:159: cachePercentage=0.1%, count=206048, hitCount=30093, hitRate=14.60% 129 | // slru_test.go:159: cachePercentage=0.3%, count=206048, hitCount=67481, hitRate=32.75% 130 | // slru_test.go:159: cachePercentage=0.5%, count=206048, hitCount=101590, hitRate=49.30% 131 | // slru_test.go:159: cachePercentage=0.7%, count=206048, hitCount=131360, hitRate=63.75% 132 | // slru_test.go:159: cachePercentage=1.0%, count=206048, hitCount=164162, hitRate=79.67% 133 | // slru_test.go:159: cachePercentage=2.0%, count=206048, hitCount=189151, hitRate=91.80% 134 | // slru_test.go:159: cachePercentage=3.0%, count=206048, hitCount=191151, hitRate=92.77% 135 | // slru_test.go:159: cachePercentage=5.0%, count=206048, hitCount=192620, hitRate=93.48% 136 | // slru_test.go:159: cachePercentage=10.0%, count=206048, hitCount=192842, hitRate=93.59% 137 | func TestHitRate(t *testing.T) { 138 | dataset, err := os.ReadFile("../dataset") 139 | if err != nil { 140 | t.Errorf("read dataset error %v", err) 141 | } 142 | reqs := strings.Split(string(dataset), ",") 143 | testHitRate(t, reqs, 0.001) 144 | testHitRate(t, reqs, 0.003) 145 | testHitRate(t, reqs, 0.005) 146 | testHitRate(t, reqs, 0.007) 147 | testHitRate(t, reqs, 0.01) 148 | testHitRate(t, reqs, 0.02) 149 | testHitRate(t, reqs, 0.03) 150 | testHitRate(t, reqs, 0.05) 151 | testHitRate(t, reqs, 0.1) 152 | } 153 | 154 | func testHitRate(t *testing.T, reqs []string, cachePercentage float64) { 155 | count := len(reqs) 156 | n := int(float64(count) * cachePercentage) 157 | c := New[string, int](n) 158 | hitCount := 0 159 | for _, req := range reqs { 160 | _, exists := c.Get(req) 161 | if exists { 162 | hitCount++ 163 | } else { 164 | c.Put(req, 0) 165 | } 166 | } 167 | hitRate := float64(hitCount) / float64(count) 168 | t.Logf("cachePercentage=%.1f%%, count=%v, hitCount=%v, hitRate=%.2f%%", cachePercentage*100, count, hitCount, hitRate*100) 169 | } 170 | -------------------------------------------------------------------------------- /counter/cm/cm4.go: -------------------------------------------------------------------------------- 1 | package cm 2 | 3 | import ( 4 | "hash/fnv" 5 | "math" 6 | "math/rand" 7 | "time" 8 | 9 | mmath "github.com/jiaxwu/gommon/math" 10 | "github.com/jiaxwu/gommon/mem" 11 | ) 12 | 13 | const ( 14 | // 计数器位数 15 | counter4Bits = 4 16 | // 最大计数值 17 | counter4MaxVal = 1< counter4MaxVal { 33 | panic("too large errorRange") 34 | } 35 | // 计数器长度 36 | counterCnt := uint64(math.Ceil(math.E / (float64(errorRange) / float64(size)) / (64 / counter4Bits))) 37 | // 哈希个数 38 | seedCnt := int(math.Ceil(math.Log(1 / errorRate))) 39 | seeds := make([]uint64, seedCnt) 40 | counters := make([][]uint64, seedCnt) 41 | source := rand.New(rand.NewSource(time.Now().UnixNano())) 42 | for i := 0; i < seedCnt; i++ { 43 | seeds[i] = source.Uint64() 44 | counters[i] = make([]uint64, counterCnt) 45 | } 46 | return &Counter4{ 47 | counters: counters, 48 | counterCnt: counterCnt, 49 | seeds: seeds, 50 | } 51 | } 52 | 53 | // 创建一个计数器 54 | // size:数据流大小 55 | // elements:不同元素数量 56 | // errorRate:错误率 57 | func New4WithElements(size, elements uint64, errorRate float64) *Counter4 { 58 | if elements > size { 59 | panic("too much elements") 60 | } 61 | errorRange := uint8(counter4MaxVal) 62 | if size/elements < uint64(errorRange) { 63 | errorRange = uint8(size / elements) 64 | } 65 | return New4(size, errorRange, errorRate) 66 | } 67 | 68 | // 增加元素的计数 69 | func (c *Counter4) Add(h uint64, val uint8) { 70 | for i, seed := range c.seeds { 71 | index, offset := c.pos(h, seed) 72 | count := c.getCount(c.counters[i], index, offset) 73 | count += uint64(val) 74 | if count > counter4MaxVal { 75 | count = counter4MaxVal 76 | } 77 | c.setCount(c.counters[i], index, offset, count) 78 | } 79 | } 80 | 81 | // 增加元素的计数 82 | func (c *Counter4) AddBytes(b []byte, val uint8) { 83 | c.Add(c.hash(b), val) 84 | } 85 | 86 | // 增加元素的计数 87 | // 字符串类型 88 | func (c *Counter4) AddString(s string, val uint8) { 89 | c.AddBytes([]byte(s), val) 90 | } 91 | 92 | // 估算元素的计数 93 | func (c *Counter4) Estimate(h uint64) uint8 { 94 | minCount := uint8(counter4MaxVal) 95 | for i, seed := range c.seeds { 96 | index, offset := c.pos(h, seed) 97 | count := c.getCount(c.counters[i], index, offset) 98 | if count == 0 { 99 | return 0 100 | } 101 | minCount = mmath.Min(minCount, uint8(count)) 102 | } 103 | return minCount 104 | } 105 | 106 | // 估算元素的计数 107 | func (c *Counter4) EstimateBytes(b []byte) uint8 { 108 | return c.Estimate(c.hash(b)) 109 | } 110 | 111 | // 估算元素的计数 112 | // 字符串类型 113 | func (c *Counter4) EstimateString(s string) uint8 { 114 | return c.EstimateBytes([]byte(s)) 115 | } 116 | 117 | // 计数衰减 118 | // 如果factor为0则直接清空 119 | func (c *Counter4) Attenuation(factor uint8) { 120 | for _, counter := range c.counters { 121 | if factor == 0 || factor > counter4MaxVal { 122 | mem.Memset(counter, 0) 123 | } else { 124 | for index := uint64(0); index < c.counterCnt; index++ { 125 | for offset := uint64(0); offset < 64; offset += counter4Bits { 126 | count := c.getCount(counter, index, offset) / uint64(factor) 127 | c.setCount(counter, index, offset, count) 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | // 计数器数量 135 | func (c *Counter4) Counters() uint64 { 136 | return c.counterCnt * (64 / counter4Bits) 137 | } 138 | 139 | // 哈希函数数量 140 | func (c *Counter4) Hashs() uint64 { 141 | return uint64(len(c.seeds)) 142 | } 143 | 144 | // 返回位置 145 | // 也就是index和offset 146 | func (c *Counter4) pos(h, seed uint64) (uint64, uint64) { 147 | // 哈希值 148 | hashValue := seed ^ h 149 | // 计数器下标 150 | index := hashValue % c.counterCnt 151 | // 计数器在64位里面的偏移 152 | offset := (hashValue & counter4MaxVal) * counter4Bits 153 | return index, offset 154 | } 155 | 156 | // 获取计数值 157 | func (c *Counter4) getCount(counter []uint64, index, offset uint64) uint64 { 158 | return (counter[index] >> offset) & uint64(counter4MaxVal) 159 | } 160 | 161 | // 设置计数值 162 | func (c *Counter4) setCount(counter []uint64, index, offset, count uint64) { 163 | counter[index] = (counter[index] &^ (counter4MaxVal << offset)) | (count << offset) 164 | } 165 | 166 | // 计算哈希值 167 | func (c *Counter4) hash(b []byte) uint64 { 168 | f := fnv.New64() 169 | f.Write(b) 170 | return f.Sum64() 171 | } 172 | --------------------------------------------------------------------------------