Compare commits
6 Commits
mapset/v0.
...
synchelper
| Author | SHA1 | Date | |
|---|---|---|---|
| e484aaa1f9 | |||
| ff69d51e50 | |||
| 521fee9817 | |||
| d98d6dffef | |||
| 2582171d99 | |||
| 3671b9f80a |
5
concurrentsafemapset/go.mod
Normal file
5
concurrentsafemapset/go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module git.blauwelle.com/go/crate/concurrentsafemapset
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require git.blauwelle.com/go/crate/mapset v0.1.0
|
||||||
2
concurrentsafemapset/go.sum
Normal file
2
concurrentsafemapset/go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
git.blauwelle.com/go/crate/mapset v0.1.0 h1:ZkiToAFhsRUyKW56BVYibkho1p2oYxZApia9Ct4AY9c=
|
||||||
|
git.blauwelle.com/go/crate/mapset v0.1.0/go.mod h1:3dV0xm7uslz6n6a2MuExeytiImgb7LwS8vQhlS/j22M=
|
||||||
243
concurrentsafemapset/mapset.go
Normal file
243
concurrentsafemapset/mapset.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package concurrentsafemapset
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.blauwelle.com/go/crate/mapset"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New 返回 [MapSet]
|
||||||
|
func New[T comparable](keys ...T) *MapSet[T] {
|
||||||
|
return &MapSet[T]{
|
||||||
|
s: mapset.New(keys...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapSet 并发安全的 [mapset.MapSet]
|
||||||
|
type MapSet[T comparable] struct {
|
||||||
|
s mapset.MapSet[T]
|
||||||
|
lock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cardinality 返回集合的元素个数
|
||||||
|
func (cs *MapSet[T]) Cardinality() int {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.Cardinality()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOne 添加元素到集合
|
||||||
|
func (cs *MapSet[T]) AddOne(key T) {
|
||||||
|
cs.lock.Lock()
|
||||||
|
cs.s.AddOne(key)
|
||||||
|
cs.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddOneOk 添加元素到集合并返回是否添加成功
|
||||||
|
func (cs *MapSet[T]) AddOneOk(key T) bool {
|
||||||
|
cs.lock.Lock()
|
||||||
|
defer cs.lock.Unlock()
|
||||||
|
return cs.s.AddOneOk(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMany 添加多个元素到集合
|
||||||
|
func (cs *MapSet[T]) AddMany(keys ...T) {
|
||||||
|
cs.lock.Lock()
|
||||||
|
cs.s.AddMany(keys...)
|
||||||
|
cs.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddManyOk 添加多个元素到集合并返回添加成功的个数
|
||||||
|
func (cs *MapSet[T]) AddManyOk(keys ...T) int {
|
||||||
|
cs.lock.Lock()
|
||||||
|
defer cs.lock.Unlock()
|
||||||
|
return cs.s.AddManyOk(keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOne 从集合中删除元素
|
||||||
|
func (cs *MapSet[T]) DeleteOne(key T) {
|
||||||
|
cs.lock.Lock()
|
||||||
|
cs.s.DeleteOne(key)
|
||||||
|
cs.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteOneOk 从集合中删除元素并返回是否删除成功
|
||||||
|
func (cs *MapSet[T]) DeleteOneOk(key T) bool {
|
||||||
|
cs.lock.Lock()
|
||||||
|
defer cs.lock.Unlock()
|
||||||
|
return cs.s.DeleteOneOk(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMany 从集合删除多个元素
|
||||||
|
func (cs *MapSet[T]) DeleteMany(keys ...T) {
|
||||||
|
cs.lock.Lock()
|
||||||
|
cs.s.DeleteMany(keys...)
|
||||||
|
cs.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteManyOk 从集合删除多个元素并返回成功删除的个数
|
||||||
|
func (cs *MapSet[T]) DeleteManyOk(keys ...T) int {
|
||||||
|
cs.lock.Lock()
|
||||||
|
defer cs.lock.Unlock()
|
||||||
|
return cs.s.DeleteManyOk(keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopOne 从集合中删除1个元素, 并把被删除的元素返回
|
||||||
|
func (cs *MapSet[T]) PopOne() (T, bool) {
|
||||||
|
cs.lock.Lock()
|
||||||
|
defer cs.lock.Unlock()
|
||||||
|
return cs.s.PopOne()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear 清空集合
|
||||||
|
func (cs *MapSet[T]) Clear() {
|
||||||
|
cs.lock.Lock()
|
||||||
|
cs.s.Clear()
|
||||||
|
cs.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone 返回集合的浅拷贝
|
||||||
|
func (cs *MapSet[T]) Clone() *MapSet[T] {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return &MapSet[T]{s: cs.s.Clone()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty 判断集合是否为空
|
||||||
|
func (cs *MapSet[T]) IsEmpty() bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.IsEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal 判断集合是否和另1个集合相等
|
||||||
|
func (cs *MapSet[T]) Equal(co *MapSet[T]) bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return cs.s.Equal(co.s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsOne 判断集合是否包含1个元素
|
||||||
|
func (cs *MapSet[T]) ContainsOne(key T) bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.ContainsOne(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAll 判断集合是否包含全部元素
|
||||||
|
// 参数为空时返回 true
|
||||||
|
func (cs *MapSet[T]) ContainsAll(keys ...T) bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.ContainsAll(keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAny 判断集合是否包含任意元素
|
||||||
|
// 参数为空时返回 false
|
||||||
|
func (cs *MapSet[T]) ContainsAny(keys ...T) bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.ContainsAny(keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSubsetOf 判断集合是否是另1个集合的子集
|
||||||
|
func (cs *MapSet[T]) IsSubsetOf(co *MapSet[T]) bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return cs.s.IsSubsetOf(co.s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsProperSubsetOf 判断集合是否是另1个集合的真子集
|
||||||
|
func (cs *MapSet[T]) IsProperSubsetOf(co *MapSet[T]) bool {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return cs.s.IsProperSubsetOf(co.s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union 返回集合和另1个集合的并集
|
||||||
|
func (cs *MapSet[T]) Union(co *MapSet[T]) *MapSet[T] {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return &MapSet[T]{s: cs.s.Union(co.s)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intersection 返回集合和另1个集合的交集
|
||||||
|
func (cs *MapSet[T]) Intersection(co *MapSet[T]) *MapSet[T] {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return &MapSet[T]{s: cs.s.Intersection(co.s)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Difference 返回集合和另1个集合的差集
|
||||||
|
func (cs *MapSet[T]) Difference(co *MapSet[T]) *MapSet[T] {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return &MapSet[T]{s: cs.s.Difference(co.s)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SymmetricDifference 返回集合和另1个集合相互差集的并集
|
||||||
|
func (cs *MapSet[T]) SymmetricDifference(co *MapSet[T]) *MapSet[T] {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
co.lock.RLock()
|
||||||
|
defer co.lock.RUnlock()
|
||||||
|
return &MapSet[T]{s: cs.s.SymmetricDifference(co.s)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSlice 把集合转换成切片
|
||||||
|
func (cs *MapSet[T]) ToSlice() []T {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.ToSlice()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToAnySlice 把集合转换成 []any
|
||||||
|
func (cs *MapSet[T]) ToAnySlice() []any {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.ToAnySlice()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON 实现 [encoding/json.Marshaler]
|
||||||
|
func (cs *MapSet[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.MarshalJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON 实现 [encoding/json.Unmarshaler]
|
||||||
|
func (cs *MapSet[T]) UnmarshalJSON(b []byte) error {
|
||||||
|
cs.lock.Lock()
|
||||||
|
defer cs.lock.Unlock()
|
||||||
|
return cs.s.UnmarshalJSON(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *MapSet[T]) String() string {
|
||||||
|
cs.lock.RLock()
|
||||||
|
defer cs.lock.RUnlock()
|
||||||
|
return cs.s.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *MapSet[T]) WithLock(fn func(s mapset.MapSet[T])) {
|
||||||
|
cs.lock.Lock()
|
||||||
|
fn(cs.s)
|
||||||
|
cs.lock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *MapSet[T]) WithRLock(fn func(s mapset.MapSet[T])) {
|
||||||
|
cs.lock.RLock()
|
||||||
|
fn(cs.s)
|
||||||
|
cs.lock.RUnlock()
|
||||||
|
}
|
||||||
3
contexthelper/go.mod
Normal file
3
contexthelper/go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module git.blauwelle.com/go/crate/contexthelper
|
||||||
|
|
||||||
|
go 1.20
|
||||||
17
contexthelper/nocancel.go
Normal file
17
contexthelper/nocancel.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package contexthelper
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// WithNoCancel 阻断上级 ctx 的中断信号
|
||||||
|
// 想要使用上级 ctx 中的值但不想受上级 ctx 控制退出时可以使用
|
||||||
|
func WithNoCancel(ctx context.Context) context.Context {
|
||||||
|
return &noCancelCtx{Context: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
type noCancelCtx struct {
|
||||||
|
context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *noCancelCtx) Done() <-chan struct{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -131,12 +131,12 @@ func (g *Group) wait(c chan error, cancel context.CancelFunc) (err error) {
|
|||||||
ctx, cancel = context.WithTimeout(context.Background(), g.cfg.stopTimeout)
|
ctx, cancel = context.WithTimeout(context.Background(), g.cfg.stopTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
}
|
}
|
||||||
for _, m := range g.actors {
|
for i := len(g.actors) - 1; i >= 0; i-- {
|
||||||
if m.stopFunc != nil {
|
if g.actors[i].stopFunc != nil {
|
||||||
if g.cfg.concurrentStop {
|
if g.cfg.concurrentStop {
|
||||||
go m.stopFunc(ctx)
|
go g.actors[i].stopFunc(ctx)
|
||||||
} else {
|
} else {
|
||||||
m.stopFunc(ctx)
|
g.actors[i].stopFunc(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
runtimehelper/go.mod
Normal file
3
runtimehelper/go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module git.blauwelle.com/go/crate/runtimehelper
|
||||||
|
|
||||||
|
go 1.20
|
||||||
162
runtimehelper/helper.go
Normal file
162
runtimehelper/helper.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package runtimehelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Frame 调用相关信息
|
||||||
|
type Frame struct {
|
||||||
|
Function string `json:"func"`
|
||||||
|
File string `json:"file"`
|
||||||
|
Line int `json:"line"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid 表示是否有效
|
||||||
|
func (frame Frame) IsValid() bool {
|
||||||
|
return frame.Line > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFilename 返回文件名
|
||||||
|
func (frame Frame) GetFilename() string {
|
||||||
|
_, file := filepath.Split(frame.File)
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitFilePath 返回 File 对应的路径和文件名
|
||||||
|
func (frame Frame) SplitFilePath() (string, string) {
|
||||||
|
return filepath.Split(frame.File)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitFunction 返回 Function 对应的包路径和函数名
|
||||||
|
func (frame Frame) SplitFunction() (string, string) {
|
||||||
|
return PackagePathQualifiedFunctionName(frame.Function).Split()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Caller 返回当前调用对应的 Frame,
|
||||||
|
// skip=0 表示调用 Caller 处.
|
||||||
|
func Caller(skip int) Frame {
|
||||||
|
pc := make([]uintptr, 1)
|
||||||
|
n := runtime.Callers(skip+2, pc)
|
||||||
|
frame, _ := runtime.CallersFrames(pc[:n]).Next()
|
||||||
|
if frame.PC == 0 {
|
||||||
|
return Frame{}
|
||||||
|
}
|
||||||
|
return Frame{
|
||||||
|
Function: frame.Function,
|
||||||
|
File: frame.File,
|
||||||
|
Line: frame.Line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stack 返回调用栈,
|
||||||
|
// skip=0 表示调用 Stack 处.
|
||||||
|
func Stack(skip, maximumFrames int) []Frame {
|
||||||
|
pc := make([]uintptr, maximumFrames)
|
||||||
|
n := runtime.Callers(skip+2, pc)
|
||||||
|
stack := make([]Frame, 0, n)
|
||||||
|
frames := runtime.CallersFrames(pc[:n])
|
||||||
|
for {
|
||||||
|
frame, more := frames.Next()
|
||||||
|
if frame.PC != 0 {
|
||||||
|
stack = append(stack, Frame{
|
||||||
|
Function: frame.Function,
|
||||||
|
File: frame.File,
|
||||||
|
Line: frame.Line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallerFrame 函数调用对应的 [runtime.Frame],
|
||||||
|
// 从 [runtime.Frame] 中可以获取调用路径上的函数的名字、文件、行号等信息;
|
||||||
|
// skip 值对应的 [runtime.Frame]:
|
||||||
|
// - -1: CallerFrame
|
||||||
|
// - 0: 调用 CallerFrame 处
|
||||||
|
//
|
||||||
|
// 可以使用 [runtime.Frame.PC] != 0 判断 runtime.Frame 有效
|
||||||
|
func CallerFrame(skip int) runtime.Frame {
|
||||||
|
pc := make([]uintptr, 1)
|
||||||
|
n := runtime.Callers(skip+2, pc)
|
||||||
|
frame, _ := runtime.CallersFrames(pc[:n]).Next()
|
||||||
|
return frame
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallersFrames 返回函数调用对应的 [runtime.Frames],
|
||||||
|
// 从 Frames 中可以获取调用路径上的函数的名字、文件、行号等信息;
|
||||||
|
// skip 值对应的第1个 [runtime.Frame]:
|
||||||
|
// - -2: [runtime.Callers]
|
||||||
|
// - -1: CallersFrames
|
||||||
|
// - 0: 调用 CallersFrames 处
|
||||||
|
func CallersFrames(skip, maximumFrames int) *runtime.Frames {
|
||||||
|
pc := make([]uintptr, maximumFrames)
|
||||||
|
n := runtime.Callers(skip+2, pc)
|
||||||
|
return runtime.CallersFrames(pc[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintCallersFrames 打印函数调用栈,
|
||||||
|
// 从调用 PrintCallersFrames 的地方开始打印
|
||||||
|
func PrintCallersFrames() {
|
||||||
|
frames := CallersFrames(3, 32)
|
||||||
|
for {
|
||||||
|
frame, more := frames.Next()
|
||||||
|
if frame.PC != 0 {
|
||||||
|
fmt.Printf("%s:%d in %s\n", frame.File, frame.Line, frame.Function)
|
||||||
|
}
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackagePathQualifiedFunctionName 是 [runtime.Frame.Function] (package path-qualified function name)
|
||||||
|
type PackagePathQualifiedFunctionName string
|
||||||
|
|
||||||
|
// Split 把完整的函数路径切割成包路径和函数名
|
||||||
|
func (f PackagePathQualifiedFunctionName) Split() (string, string) {
|
||||||
|
var period int
|
||||||
|
loop:
|
||||||
|
for i := len(f) - 1; i >= 0; i-- {
|
||||||
|
switch f[i] {
|
||||||
|
case '/':
|
||||||
|
break loop
|
||||||
|
case '.':
|
||||||
|
period = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if period == 0 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
return string(f[:period]), string(f[period+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageName 把完整的函数路径切割成包路径和函数名
|
||||||
|
func (f PackagePathQualifiedFunctionName) PackageName() string {
|
||||||
|
var period int
|
||||||
|
for i := len(f) - 1; i >= 0; i-- {
|
||||||
|
switch f[i] {
|
||||||
|
case '/':
|
||||||
|
return string(f[:period])
|
||||||
|
case '.':
|
||||||
|
period = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(f[:period])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitPackageFunctionName 把完整的函数路径切割成包路径和函数名
|
||||||
|
// 参数 f 是 [runtime.Frame.Function] (package path-qualified function name),
|
||||||
|
func SplitPackageFunctionName(f string) (string, string) {
|
||||||
|
return PackagePathQualifiedFunctionName(f).Split()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPackageName 把完整的函数路径切割成包路径和函数名
|
||||||
|
// 参数 f 是 [runtime.Frame.Function] (package path-qualified function name),
|
||||||
|
func GetPackageName(f string) string {
|
||||||
|
return PackagePathQualifiedFunctionName(f).PackageName()
|
||||||
|
}
|
||||||
18
synchelper/bytes_buffer_pool.go
Normal file
18
synchelper/bytes_buffer_pool.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package synchelper
|
||||||
|
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
// NewBytesBufferPool 返回新的 BytesBufferPool
|
||||||
|
func NewBytesBufferPool(initialSize, maximumSize int) BytesBufferPool {
|
||||||
|
return NewPool(
|
||||||
|
func() any {
|
||||||
|
return bytes.NewBuffer(make([]byte, 0, initialSize))
|
||||||
|
},
|
||||||
|
func(v *bytes.Buffer) bool {
|
||||||
|
return v.Cap() <= maximumSize
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesBufferPool 是 [*bytes.Buffer] 的资源池
|
||||||
|
type BytesBufferPool = Pool[*bytes.Buffer]
|
||||||
3
synchelper/go.mod
Normal file
3
synchelper/go.mod
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module git.blauwelle.com/go/crate/synchelper
|
||||||
|
|
||||||
|
go 1.20
|
||||||
25
synchelper/io_writer.go
Normal file
25
synchelper/io_writer.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package synchelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSyncWriter 返回写互斥的 writer
|
||||||
|
func NewSyncWriter(writer io.Writer) io.Writer {
|
||||||
|
return &syncWriter{
|
||||||
|
writer: writer,
|
||||||
|
lock: sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type syncWriter struct {
|
||||||
|
writer io.Writer
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *syncWriter) Write(p []byte) (n int, err error) {
|
||||||
|
w.lock.Lock()
|
||||||
|
defer w.lock.Unlock()
|
||||||
|
return w.writer.Write(p)
|
||||||
|
}
|
||||||
34
synchelper/pool.go
Normal file
34
synchelper/pool.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package synchelper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPool 初始化 Pool,
|
||||||
|
// newFn 是资源的构造函数,
|
||||||
|
// putCond 返回 true 时表示资源可以被放回 Pool 中.
|
||||||
|
func NewPool[T any](newFn func() any, putCond func(v T) bool) Pool[T] {
|
||||||
|
return Pool[T]{
|
||||||
|
pool: &sync.Pool{New: newFn},
|
||||||
|
putCond: putCond,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pool 是通用的资源池
|
||||||
|
type Pool[T any] struct {
|
||||||
|
pool *sync.Pool
|
||||||
|
putCond func(v T) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取资源
|
||||||
|
func (pool Pool[T]) Get() T {
|
||||||
|
return pool.pool.Get().(T)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put 放回资源
|
||||||
|
func (pool Pool[T]) Put(v T) {
|
||||||
|
if !pool.putCond(v) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pool.pool.Put(v)
|
||||||
|
}
|
||||||
29
wireexample/cmd/example_01/main.go
Normal file
29
wireexample/cmd/example_01/main.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// 展示 wire 的两种 mock 实现
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
approachA()
|
||||||
|
approachB()
|
||||||
|
}
|
||||||
|
|
||||||
|
// approach A:
|
||||||
|
// - 需要 mock 的对象定义成接口, 真正的实现和 mock 分别实现接口;
|
||||||
|
// - injector 函数使用 接口类型参数, 测试时传入 mock 对象;
|
||||||
|
func approachA() {
|
||||||
|
rander := &MockRander{}
|
||||||
|
guess := InjectGuessWithoutMock(rander)
|
||||||
|
rander.Value = 1
|
||||||
|
fmt.Println("approachA:", guess.Guess(10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// approach B:
|
||||||
|
// - mock 和 真正的实现分别定义到两组 injector 中, injector 提供1个对象包含要测试的对象和 mock;
|
||||||
|
// - 测试时调用 mock injector 返回对象(包含要测试的对象和 mock);
|
||||||
|
func approachB() {
|
||||||
|
guessWithMock := InjectMockGuess()
|
||||||
|
guessWithMock.Mock.Value = 1
|
||||||
|
fmt.Println("approachB:", guessWithMock.Guess.Guess(10))
|
||||||
|
}
|
||||||
64
wireexample/cmd/example_01/provider.go
Normal file
64
wireexample/cmd/example_01/provider.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Rander interface {
|
||||||
|
Rand() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultRander struct{}
|
||||||
|
|
||||||
|
func (r *DefaultRander) Rand() int {
|
||||||
|
return rand.Int()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRander() *DefaultRander {
|
||||||
|
return &DefaultRander{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockRander struct {
|
||||||
|
Value int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *MockRander) Rand() int {
|
||||||
|
return r.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type Guess struct {
|
||||||
|
rander Rander
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Guess) Guess(guess int) string {
|
||||||
|
return fmt.Sprintf("you guess %d, real number is %d", guess, g.rander.Rand())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGuess(rander Rander) *Guess {
|
||||||
|
return &Guess{
|
||||||
|
rander: rander,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GuessWithMock struct {
|
||||||
|
Guess *Guess
|
||||||
|
Mock *MockRander
|
||||||
|
}
|
||||||
|
|
||||||
|
var GuessProviderSetWithoutMock = wire.NewSet(NewGuess)
|
||||||
|
|
||||||
|
var GuessProviderSet = wire.NewSet(
|
||||||
|
NewRander,
|
||||||
|
NewGuess,
|
||||||
|
wire.Bind(new(Rander), new(*DefaultRander)),
|
||||||
|
)
|
||||||
|
|
||||||
|
var MockGuessProviderSet = wire.NewSet(
|
||||||
|
wire.Struct(new(MockRander)),
|
||||||
|
NewGuess,
|
||||||
|
wire.Bind(new(Rander), new(*MockRander)),
|
||||||
|
wire.Struct(new(GuessWithMock), "*"),
|
||||||
|
)
|
||||||
20
wireexample/cmd/example_01/wire.go
Normal file
20
wireexample/cmd/example_01/wire.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/google/wire"
|
||||||
|
|
||||||
|
// Approach A
|
||||||
|
func InjectGuessWithoutMock(rander Rander) *Guess {
|
||||||
|
panic(wire.Build(GuessProviderSetWithoutMock))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approach B
|
||||||
|
func InjectRealGuess() *Guess {
|
||||||
|
panic(wire.Build(GuessProviderSet))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approach B
|
||||||
|
func InjectMockGuess() *GuessWithMock {
|
||||||
|
panic(wire.Build(MockGuessProviderSet))
|
||||||
|
}
|
||||||
33
wireexample/cmd/example_01/wire_gen.go
Normal file
33
wireexample/cmd/example_01/wire_gen.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// Code generated by Wire. DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:generate go run github.com/google/wire/cmd/wire
|
||||||
|
//go:build !wireinject
|
||||||
|
// +build !wireinject
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Injectors from wire.go:
|
||||||
|
|
||||||
|
// Approach A
|
||||||
|
func InjectGuessWithoutMock(rander Rander) *Guess {
|
||||||
|
guess := NewGuess(rander)
|
||||||
|
return guess
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approach B
|
||||||
|
func InjectRealGuess() *Guess {
|
||||||
|
defaultRander := NewRander()
|
||||||
|
guess := NewGuess(defaultRander)
|
||||||
|
return guess
|
||||||
|
}
|
||||||
|
|
||||||
|
// Approach B
|
||||||
|
func InjectMockGuess() *GuessWithMock {
|
||||||
|
mockRander := &MockRander{}
|
||||||
|
guess := NewGuess(mockRander)
|
||||||
|
guessWithMock := &GuessWithMock{
|
||||||
|
Guess: guess,
|
||||||
|
Mock: mockRander,
|
||||||
|
}
|
||||||
|
return guessWithMock
|
||||||
|
}
|
||||||
5
wireexample/go.mod
Normal file
5
wireexample/go.mod
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module git.blauwelle.com/go/crate/wireexample
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require github.com/google/wire v0.5.0
|
||||||
10
wireexample/go.sum
Normal file
10
wireexample/go.sum
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
|
github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8=
|
||||||
|
github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
Reference in New Issue
Block a user