├── 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 |
--------------------------------------------------------------------------------