diff options
Diffstat (limited to 'vendor/codeberg.org/gruf')
| -rw-r--r-- | vendor/codeberg.org/gruf/go-cache/v3/result/cache.go | 198 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-cache/v3/result/key.go | 23 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/README.md | 9 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_caller.go | 26 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go | 18 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go | 20 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/build_trace.go | 27 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/callers.go | 98 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/error.go | 35 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go | 33 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/errors.go | 231 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/once.go | 36 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/runtime.go | 97 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/standard.go | 221 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-errors/v2/value.go | 52 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-runners/pool.go | 36 | 
16 files changed, 685 insertions, 475 deletions
diff --git a/vendor/codeberg.org/gruf/go-cache/v3/result/cache.go b/vendor/codeberg.org/gruf/go-cache/v3/result/cache.go index 5756c0cf5..eded6e7f4 100644 --- a/vendor/codeberg.org/gruf/go-cache/v3/result/cache.go +++ b/vendor/codeberg.org/gruf/go-cache/v3/result/cache.go @@ -2,8 +2,6 @@ package result  import (  	"context" -	"fmt" -	"os"  	"reflect"  	_ "unsafe" @@ -11,8 +9,6 @@ import (  	"codeberg.org/gruf/go-errors/v2"  ) -var ErrUnsupportedZero = errors.New("") -  // Lookup represents a struct object lookup method in the cache.  type Lookup struct {  	// Name is a period ('.') separated string @@ -58,7 +54,8 @@ func New[T any](lookups []Lookup, copy func(T) T, cap int) *Cache[T] {  	}  	// Allocate new cache object -	c := &Cache[T]{copy: copy} +	c := new(Cache[T]) +	c.copy = copy // use copy fn.  	c.lookups = make([]structKey, len(lookups))  	for i, lookup := range lookups { @@ -96,7 +93,7 @@ func (c *Cache[T]) SetEvictionCallback(hook func(T)) {  		}  		// Free result and call hook. -		v := getResultValue[T](res) +		v := res.Value.(T)  		putResult(res)  		hook(v)  	}) @@ -125,7 +122,7 @@ func (c *Cache[T]) SetInvalidateCallback(hook func(T)) {  		}  		// Free result and call hook. -		v := getResultValue[T](res) +		v := res.Value.(T)  		putResult(res)  		hook(v)  	}) @@ -135,11 +132,8 @@ func (c *Cache[T]) SetInvalidateCallback(hook func(T)) {  func (c *Cache[T]) IgnoreErrors(ignore func(error) bool) {  	if ignore == nil {  		ignore = func(err error) bool { -			return errors.Comparable( -				err, -				context.Canceled, -				context.DeadlineExceeded, -			) +			return errors.Is(err, context.Canceled) || +				errors.Is(err, context.DeadlineExceeded)  		}  	}  	c.cache.Lock() @@ -149,23 +143,83 @@ func (c *Cache[T]) IgnoreErrors(ignore func(error) bool) {  // Load will attempt to load an existing result from the cacche for the given lookup and key parts, else calling the provided load function and caching the result.  func (c *Cache[T]) Load(lookup string, load func() (T, error), keyParts ...any) (T, error) { -	var zero T -	var res *result +	info := c.lookups.get(lookup) +	key := info.genKey(keyParts) +	return c.load(info, key, load) +} + +// Has checks the cache for a positive result under the given lookup and key parts. +func (c *Cache[T]) Has(lookup string, keyParts ...any) bool { +	info := c.lookups.get(lookup) +	key := info.genKey(keyParts) +	return c.has(info, key) +} -	// Get lookup key info by name. -	keyInfo := c.lookups.get(lookup) -	if !keyInfo.unique { -		panic("non-unique lookup does not support load: " + lookup) +// Store will call the given store function, and on success store the value in the cache as a positive result. +func (c *Cache[T]) Store(value T, store func() error) error { +	// Attempt to store this value. +	if err := store(); err != nil { +		return err  	} -	// Generate cache key string. -	ckey := keyInfo.genKey(keyParts) +	// Prepare cached result. +	result := getResult() +	result.Keys = c.lookups.generate(value) +	result.Value = c.copy(value) +	result.Error = nil + +	var evict func() + +	// Lock cache. +	c.cache.Lock() + +	defer func() { +		// Unlock cache. +		c.cache.Unlock() + +		if evict != nil { +			// Call evict. +			evict() +		} + +		// Call invalidate. +		c.invalid(value) +	}() + +	// Store result in cache. +	evict = c.store(result) + +	return nil +} + +// Invalidate will invalidate any result from the cache found under given lookup and key parts. +func (c *Cache[T]) Invalidate(lookup string, keyParts ...any) { +	info := c.lookups.get(lookup) +	key := info.genKey(keyParts) +	c.invalidate(info, key) +} + +// Clear empties the cache, calling the invalidate callback where necessary. +func (c *Cache[T]) Clear() { c.Trim(100) } + +// Trim ensures the cache stays within percentage of total capacity, truncating where necessary. +func (c *Cache[T]) Trim(perc float64) { c.cache.Trim(perc) } + +func (c *Cache[T]) load(lookup *structKey, key string, load func() (T, error)) (T, error) { +	if !lookup.unique { // ensure this lookup only returns 1 result +		panic("non-unique lookup does not support load: " + lookup.name) +	} + +	var ( +		zero T +		res  *result +	)  	// Acquire cache lock  	c.cache.Lock()  	// Look for primary key for cache key (only accept len=1) -	if pkeys := keyInfo.pkeys[ckey]; len(pkeys) == 1 { +	if pkeys := lookup.pkeys[key]; len(pkeys) == 1 {  		// Fetch the result for primary key  		entry, ok := c.cache.Cache.Get(pkeys[0]) @@ -200,8 +254,8 @@ func (c *Cache[T]) Load(lookup string, load func() (T, error), keyParts ...any)  			// This load returned an error, only  			// store this item under provided key.  			res.Keys = []cacheKey{{ -				info: keyInfo, -				key:  ckey, +				info: lookup, +				key:  key,  			}}  		} else {  			// Alloc result. @@ -239,66 +293,19 @@ func (c *Cache[T]) Load(lookup string, load func() (T, error), keyParts ...any)  	}  	// Copy value from cached result. -	v := c.copy(getResultValue[T](res)) +	v := c.copy(res.Value.(T))  	return v, nil  } -// Store will call the given store function, and on success store the value in the cache as a positive result. -func (c *Cache[T]) Store(value T, store func() error) error { -	// Attempt to store this value. -	if err := store(); err != nil { -		return err -	} - -	// Prepare cached result. -	result := getResult() -	result.Keys = c.lookups.generate(value) -	result.Value = c.copy(value) -	result.Error = nil - -	var evict func() - -	// Lock cache. -	c.cache.Lock() - -	defer func() { -		// Unlock cache. -		c.cache.Unlock() - -		if evict != nil { -			// Call evict. -			evict() -		} - -		// Call invalidate. -		c.invalid(value) -	}() - -	// Store result in cache. -	evict = c.store(result) - -	return nil -} - -// Has checks the cache for a positive result under the given lookup and key parts. -func (c *Cache[T]) Has(lookup string, keyParts ...any) bool { +func (c *Cache[T]) has(lookup *structKey, key string) bool {  	var res *result -	// Get lookup key info by name. -	keyInfo := c.lookups.get(lookup) -	if !keyInfo.unique { -		panic("non-unique lookup does not support has: " + lookup) -	} - -	// Generate cache key string. -	ckey := keyInfo.genKey(keyParts) -  	// Acquire cache lock  	c.cache.Lock()  	// Look for primary key for cache key (only accept len=1) -	if pkeys := keyInfo.pkeys[ckey]; len(pkeys) == 1 { +	if pkeys := lookup.pkeys[key]; len(pkeys) == 1 {  		// Fetch the result for primary key  		entry, ok := c.cache.Cache.Get(pkeys[0]) @@ -320,31 +327,6 @@ func (c *Cache[T]) Has(lookup string, keyParts ...any) bool {  	return ok  } -// Invalidate will invalidate any result from the cache found under given lookup and key parts. -func (c *Cache[T]) Invalidate(lookup string, keyParts ...any) { -	// Get lookup key info by name. -	keyInfo := c.lookups.get(lookup) - -	// Generate cache key string. -	ckey := keyInfo.genKey(keyParts) - -	// Look for primary key for cache key -	c.cache.Lock() -	pkeys := keyInfo.pkeys[ckey] -	delete(keyInfo.pkeys, ckey) -	c.cache.Unlock() - -	// Invalidate all primary keys. -	c.cache.InvalidateAll(pkeys...) -} - -// Clear empties the cache, calling the invalidate callback where necessary. -func (c *Cache[T]) Clear() { c.Trim(100) } - -// Trim ensures the cache stays within percentage of total capacity, truncating where necessary. -func (c *Cache[T]) Trim(perc float64) { c.cache.Trim(perc) } - -// store will cache this result under all of its required cache keys.  func (c *Cache[T]) store(res *result) (evict func()) {  	var toEvict []*result @@ -425,6 +407,17 @@ func (c *Cache[T]) store(res *result) (evict func()) {  	}  } +func (c *Cache[T]) invalidate(lookup *structKey, key string) { +	// Look for primary key for cache key +	c.cache.Lock() +	pkeys := lookup.pkeys[key] +	delete(lookup.pkeys, key) +	c.cache.Unlock() + +	// Invalidate all primary keys. +	c.cache.InvalidateAll(pkeys...) +} +  type result struct {  	// Result primary key  	PKey int64 @@ -438,12 +431,3 @@ type result struct {  	// cached error  	Error error  } - -// getResultValue is a safe way of casting and fetching result value. -func getResultValue[T any](res *result) T { -	v, ok := res.Value.(T) -	if !ok { -		fmt.Fprintf(os.Stderr, "!! BUG: unexpected value type in result: %T\n", res.Value) -	} -	return v -} diff --git a/vendor/codeberg.org/gruf/go-cache/v3/result/key.go b/vendor/codeberg.org/gruf/go-cache/v3/result/key.go index 5e10e6fa1..5508936ba 100644 --- a/vendor/codeberg.org/gruf/go-cache/v3/result/key.go +++ b/vendor/codeberg.org/gruf/go-cache/v3/result/key.go @@ -43,9 +43,8 @@ func (sk structKeys) generate(a any) []cacheKey {  		v = v.Elem()  	} -	// Acquire byte buffer +	// Acquire buffer  	buf := getBuf() -	defer putBuf(buf)  outer:  	for i := range sk { @@ -80,6 +79,9 @@ outer:  		})  	} +	// Release buf +	putBuf(buf) +  	return keys  } @@ -124,11 +126,12 @@ type structKey struct {  	unique bool  	// fields is a slice of runtime struct field -	// indices, of the fields encompassed by this key. +	// indices, of fields encompassed by this key.  	fields []structField  	// pkeys is a lookup of stored struct key values -	// to the primary cache lookup key (int64). +	// to the primary cache lookup key (int64). this +	// is protected by the main cache mutex.  	pkeys map[string][]int64  } @@ -192,9 +195,8 @@ func (sk *structKey) genKey(parts []any) string {  		panic(fmt.Sprintf("incorrect no. key parts provided: want=%d received=%d", len(parts), len(sk.fields)))  	} -	// Acquire byte buffer +	// Acquire buffer  	buf := getBuf() -	defer putBuf(buf)  	buf.Reset()  	for i, part := range parts { @@ -210,8 +212,13 @@ func (sk *structKey) genKey(parts []any) string {  	// Drop last '.'  	buf.Truncate(1) -	// Return string copy -	return string(buf.B) +	// Create str copy +	str := string(buf.B) + +	// Release buf +	putBuf(buf) + +	return str  }  type structField struct { diff --git a/vendor/codeberg.org/gruf/go-errors/v2/README.md b/vendor/codeberg.org/gruf/go-errors/v2/README.md index 90049abec..21f7cb767 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/README.md +++ b/vendor/codeberg.org/gruf/go-errors/v2/README.md @@ -1,5 +1,8 @@  # go-errors -simple but powerful errors library that allows easy wrapping and stacktracing of errors. - -to disable stacktraces set the `notrace` build tag.
\ No newline at end of file +powerful errors library with a simple API that allows: +- accessing all the standard library errors functions +- wrapping of errors +- adding values to errors, in a similar manner to contexts +- including calling function prefix when tag=errcaller is set +- including stacktrace with error when tag=errtrace is set diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_caller.go b/vendor/codeberg.org/gruf/go-errors/v2/build_caller.go new file mode 100644 index 000000000..ad5067574 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_caller.go @@ -0,0 +1,26 @@ +//go:build errcaller +// +build errcaller + +package errors + +import ( +	_ "unsafe" +) + +// IncludesCaller is a compile-time flag used to indicate whether +// to include calling function prefix on error wrap / creation. +const IncludesCaller = true + +type caller string + +// set will set the actual caller value +// only when correct build flag is set. +func (c *caller) set(v string) { +	*c = caller(v) +} + +// value returns the actual caller value +// only when correct build flag is set +func (c caller) value() string { +	return string(c) +} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go b/vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go new file mode 100644 index 000000000..935283bf5 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_nocaller.go @@ -0,0 +1,18 @@ +//go:build !errcaller +// +build !errcaller + +package errors + +// IncludesCaller is a compile-time flag used to indicate whether +// to include calling function prefix on error wrap / creation. +const IncludesCaller = false + +type caller struct{} + +// set will set the actual caller value +// only when correct build flag is set. +func (caller) set(string) {} + +// value returns the actual caller value +// only when correct build flag is set. +func (caller) value() string { return "" } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go b/vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go new file mode 100644 index 000000000..acee2e698 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_notrace.go @@ -0,0 +1,20 @@ +//go:build !errtrace +// +build !errtrace + +package errors + +import "runtime" + +// IncludesStacktrace is a compile-time flag used to indicate +// whether to include stacktraces on error wrap / creation. +const IncludesStacktrace = false + +type trace struct{} + +// set will set the actual trace value +// only when correct build flag is set. +func (trace) set([]runtime.Frame) {} + +// value returns the actual trace value +// only when correct build flag is set. +func (trace) value() Callers { return nil } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/build_trace.go b/vendor/codeberg.org/gruf/go-errors/v2/build_trace.go new file mode 100644 index 000000000..e7b652699 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/build_trace.go @@ -0,0 +1,27 @@ +//go:build errtrace +// +build errtrace + +package errors + +import ( +	"runtime" +	_ "unsafe" +) + +// IncludesStacktrace is a compile-time flag used to indicate +// whether to include stacktraces on error wrap / creation. +const IncludesStacktrace = true + +type trace []runtime.Frame + +// set will set the actual trace value +// only when correct build flag is set. +func (t *trace) set(v []runtime.Frame) { +	*t = trace(v) +} + +// value returns the actual trace value +// only when correct build flag is set. +func (t trace) value() Callers { +	return Callers(t) +} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/callers.go b/vendor/codeberg.org/gruf/go-errors/v2/callers.go deleted file mode 100644 index 3fe84b0c5..000000000 --- a/vendor/codeberg.org/gruf/go-errors/v2/callers.go +++ /dev/null @@ -1,98 +0,0 @@ -package errors - -import ( -	"encoding/json" -	"runtime" -	"strconv" -	"strings" -	"unsafe" -) - -// Callers is a stacktrace of caller PCs. -type Callers []uintptr - -// GetCallers returns a Callers slice of PCs, of at most 'depth'. -func GetCallers(skip int, depth int) Callers { -	rpc := make([]uintptr, depth) -	n := runtime.Callers(skip+1, rpc) -	return Callers(rpc[0:n]) -} - -// Frames fetches runtime frames for a slice of caller PCs. -func (f Callers) Frames() []runtime.Frame { -	// Allocate expected frames slice -	frames := make([]runtime.Frame, 0, len(f)) - -	// Get frames iterator for PCs -	iter := runtime.CallersFrames(f) - -	for { -		// Get next frame in iter -		frame, ok := iter.Next() -		if !ok { -			break -		} - -		// Append to frames slice -		frames = append(frames, frame) -	} - -	return frames -} - -// MarshalJSON implements json.Marshaler to provide an easy, simple default. -func (f Callers) MarshalJSON() ([]byte, error) { -	// JSON-able frame type -	type jsonFrame struct { -		Func string `json:"func"` -		File string `json:"file"` -		Line int    `json:"line"` -	} - -	// Convert to frames -	frames := f.Frames() - -	// Allocate expected size jsonFrame slice -	jsonFrames := make([]jsonFrame, 0, len(f)) - -	for i := 0; i < len(frames); i++ { -		frame := frames[i] - -		// Convert each to jsonFrame object -		jsonFrames = append(jsonFrames, jsonFrame{ -			Func: funcname(frame.Function), -			File: frame.File, -			Line: frame.Line, -		}) -	} - -	// marshal converted frames -	return json.Marshal(frames) -} - -// String will return a simple string representation of receiving Callers slice. -func (f Callers) String() string { -	// Guess-timate to reduce allocs -	buf := make([]byte, 0, 64*len(f)) - -	// Convert to frames -	frames := f.Frames() - -	for i := 0; i < len(frames); i++ { -		frame := frames[i] - -		// Append formatted caller info -		fn := funcname(frame.Function) -		buf = append(buf, fn+"()\n\t"+frame.File+":"...) -		buf = strconv.AppendInt(buf, int64(frame.Line), 10) -		buf = append(buf, '\n') -	} - -	return *(*string)(unsafe.Pointer(&buf)) -} - -// funcname splits a function name with pkg from its path prefix. -func funcname(name string) string { -	i := strings.LastIndex(name, "/") -	return name[i+1:] -} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/error.go b/vendor/codeberg.org/gruf/go-errors/v2/error.go deleted file mode 100644 index ed1217a29..000000000 --- a/vendor/codeberg.org/gruf/go-errors/v2/error.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build !notrace -// +build !notrace - -package errors - -type errormsg struct { -	msg   string -	wrap  error -	stack Callers -} - -func create(msg string, wrap error) *errormsg { -	return &errormsg{ -		msg:   msg, -		wrap:  wrap, -		stack: GetCallers(2, 10), -	} -} - -func (err *errormsg) Error() string { -	return err.msg -} - -func (err *errormsg) Is(target error) bool { -	other, ok := target.(*errormsg) -	return ok && (err.msg == other.msg) -} - -func (err *errormsg) Unwrap() error { -	return err.wrap -} - -func (err *errormsg) Stacktrace() Callers { -	return err.stack -} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go b/vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go deleted file mode 100644 index e5faf80a2..000000000 --- a/vendor/codeberg.org/gruf/go-errors/v2/error_notrace.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build notrace -// +build notrace - -package errors - -type errormsg struct { -	msg  string -	wrap error -} - -func create(msg string, wrap error) *errormsg { -	return &errormsg{ -		msg:  msg, -		wrap: wrap, -	} -} - -func (err *errormsg) Error() string { -	return err.msg -} - -func (err *errormsg) Is(target error) bool { -	other, ok := target.(*errormsg) -	return ok && (err.msg == other.msg) -} - -func (err *errormsg) Unwrap() error { -	return err.wrap -} - -func (err *errormsg) Stacktrace() Callers { -	return nil -} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/errors.go b/vendor/codeberg.org/gruf/go-errors/v2/errors.go index 2c4689151..d5f1a7ab1 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/errors.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/errors.go @@ -1,37 +1,248 @@  package errors  import ( -	"errors"  	"fmt" +	"runtime"  )  // New returns a new error created from message. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline  func New(msg string) error { -	return create(msg, nil) +	var c caller +	var t trace +	if IncludesCaller { +		pcs := make([]uintptr, 1) +		_ = runtime.Callers(2, pcs) +		fn := runtime.FuncForPC(pcs[0]) +		c.set(funcName(fn)) +	} +	if IncludesStacktrace { +		pcs := make([]uintptr, 10) +		n := runtime.Callers(2, pcs) +		iter := runtime.CallersFrames(pcs[:n]) +		t.set(gatherFrames(iter, n)) +	} +	return &_errormsg{ +		cfn: c, +		msg: msg, +		trc: t, +	}  }  // Newf returns a new error created from message format and args. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline  func Newf(msgf string, args ...interface{}) error { -	return create(fmt.Sprintf(msgf, args...), nil) +	var c caller +	var t trace +	if IncludesCaller { +		pcs := make([]uintptr, 1) +		_ = runtime.Callers(2, pcs) +		fn := runtime.FuncForPC(pcs[0]) +		c.set(funcName(fn)) +	} +	if IncludesStacktrace { +		pcs := make([]uintptr, 10) +		n := runtime.Callers(2, pcs) +		iter := runtime.CallersFrames(pcs[:n]) +		t.set(gatherFrames(iter, n)) +	} +	return &_errormsg{ +		cfn: c, +		msg: fmt.Sprintf(msgf, args...), +		trc: t, +	} +} + +// NewAt returns a new error created, skipping 'skip' +// frames for trace / caller information, from message. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline +func NewAt(skip int, msg string) error { +	var c caller +	var t trace +	if IncludesCaller { +		pcs := make([]uintptr, 1) +		_ = runtime.Callers(skip+1, pcs) +		fn := runtime.FuncForPC(pcs[0]) +		c.set(funcName(fn)) +	} +	if IncludesStacktrace { +		pcs := make([]uintptr, 10) +		n := runtime.Callers(skip+1, pcs) +		iter := runtime.CallersFrames(pcs[:n]) +		t.set(gatherFrames(iter, n)) +	} +	return &_errormsg{ +		cfn: c, +		msg: msg, +		trc: t, +	}  }  // Wrap will wrap supplied error within a new error created from message. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline  func Wrap(err error, msg string) error { -	return create(msg, err) +	if err == nil { +		panic("cannot wrap nil error") +	} +	var c caller +	var t trace +	if IncludesCaller { +		pcs := make([]uintptr, 1) +		_ = runtime.Callers(2, pcs) +		fn := runtime.FuncForPC(pcs[0]) +		c.set(funcName(fn)) +	} +	if IncludesStacktrace { +		pcs := make([]uintptr, 10) +		n := runtime.Callers(2, pcs) +		iter := runtime.CallersFrames(pcs[:n]) +		t.set(gatherFrames(iter, n)) +	} +	return &_errorwrap{ +		cfn: c, +		msg: msg, +		err: err, +		trc: t, +	}  }  // Wrapf will wrap supplied error within a new error created from message format and args. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline  func Wrapf(err error, msgf string, args ...interface{}) error { -	return create(fmt.Sprintf(msgf, args...), err) +	if err == nil { +		panic("cannot wrap nil error") +	} +	var c caller +	var t trace +	if IncludesCaller { +		pcs := make([]uintptr, 1) +		_ = runtime.Callers(2, pcs) +		fn := runtime.FuncForPC(pcs[0]) +		c.set(funcName(fn)) +	} +	if IncludesStacktrace { +		pcs := make([]uintptr, 10) +		n := runtime.Callers(2, pcs) +		iter := runtime.CallersFrames(pcs[:n]) +		t.set(gatherFrames(iter, n)) +	} +	return &_errorwrap{ +		cfn: c, +		msg: fmt.Sprintf(msgf, args...), +		err: err, +		trc: t, +	} +} + +// WrapAt wraps error within new error created from message, +// skipping 'skip' frames for trace / caller information. +// +// Note this function cannot be inlined, to ensure expected +// and consistent behaviour in setting trace / caller info. +// +//go:noinline +func WrapAt(skip int, err error, msg string) error { +	if err == nil { +		panic("cannot wrap nil error") +	} +	var c caller +	var t trace +	if IncludesCaller { +		pcs := make([]uintptr, 1) +		_ = runtime.Callers(skip+1, pcs) +		fn := runtime.FuncForPC(pcs[0]) +		c.set(funcName(fn)) +	} +	if IncludesStacktrace { +		pcs := make([]uintptr, 10) +		n := runtime.Callers(skip+1, pcs) +		iter := runtime.CallersFrames(pcs[:n]) +		t.set(gatherFrames(iter, n)) +	} +	return &_errorwrap{ +		cfn: c, +		msg: msg, +		err: err, +		trc: t, +	}  }  // Stacktrace fetches first stored stacktrace of callers from error chain.  func Stacktrace(err error) Callers { -	var e interface { -		Stacktrace() Callers -	} -	if !errors.As(err, &e) { +	if !IncludesStacktrace { +		// compile-time check  		return nil  	} -	return e.Stacktrace() +	if e := AsV2[*_errormsg](err); err != nil { +		return e.trc.value() +	} +	if e := AsV2[*_errorwrap](err); err != nil { +		return e.trc.value() +	} +	return nil +} + +type _errormsg struct { +	cfn caller +	msg string +	trc trace +} + +func (err *_errormsg) Error() string { +	if IncludesCaller { +		fn := err.cfn.value() +		return fn + " " + err.msg +	} else { +		return err.msg +	} +} + +func (err *_errormsg) Is(other error) bool { +	oerr, ok := other.(*_errormsg) +	return ok && oerr.msg == err.msg +} + +type _errorwrap struct { +	cfn caller +	msg string +	err error // wrapped +	trc trace +} + +func (err *_errorwrap) Error() string { +	if IncludesCaller { +		fn := err.cfn.value() +		return fn + " " + err.msg + ": " + err.err.Error() +	} else { +		return err.msg + ": " + err.err.Error() +	} +} + +func (err *_errorwrap) Is(other error) bool { +	oerr, ok := other.(*_errorwrap) +	return ok && oerr.msg == err.msg +} + +func (err *_errorwrap) Unwrap() error { +	return err.err  } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/once.go b/vendor/codeberg.org/gruf/go-errors/v2/once.go index 83a45a61f..467fe726d 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/once.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/once.go @@ -2,46 +2,30 @@ package errors  import (  	"sync/atomic" -	"unsafe"  )  // OnceError is an error structure that supports safe multi  // threaded usage and setting only once (until reset). -type OnceError struct{ err unsafe.Pointer } - -// NewOnce returns a new OnceError instance. -func NewOnce() OnceError { -	return OnceError{ -		err: nil, -	} -} +type OnceError struct{ ptr atomic.Pointer[error] }  // Store will safely set the OnceError to value, no-op if nil. -func (e *OnceError) Store(err error) { -	// Nothing to do +func (e *OnceError) Store(err error) bool {  	if err == nil { -		return +		return false  	} - -	// Only set if not already -	atomic.CompareAndSwapPointer( -		&e.err, -		nil, -		unsafe.Pointer(&err), -	) +	return e.ptr.CompareAndSwap(nil, &err)  }  // Load will load the currently stored error.  func (e *OnceError) Load() error { -	return *(*error)(atomic.LoadPointer(&e.err)) +	if ptr := e.ptr.Load(); ptr != nil { +		return *ptr +	} +	return nil  }  // IsSet returns whether OnceError has been set. -func (e *OnceError) IsSet() bool { -	return (atomic.LoadPointer(&e.err) != nil) -} +func (e *OnceError) IsSet() bool { return (e.ptr.Load() != nil) }  // Reset will reset the OnceError value. -func (e *OnceError) Reset() { -	atomic.StorePointer(&e.err, nil) -} +func (e *OnceError) Reset() { e.ptr.Store(nil) } diff --git a/vendor/codeberg.org/gruf/go-errors/v2/runtime.go b/vendor/codeberg.org/gruf/go-errors/v2/runtime.go new file mode 100644 index 000000000..0c8cf11cd --- /dev/null +++ b/vendor/codeberg.org/gruf/go-errors/v2/runtime.go @@ -0,0 +1,97 @@ +package errors + +import ( +	"encoding/json" +	"runtime" +	"strconv" +	"strings" +	"unsafe" +) + +// Callers ... +type Callers []runtime.Frame + +// MarshalJSON implements json.Marshaler to provide an easy, simple default. +func (c Callers) MarshalJSON() ([]byte, error) { +	// JSON-able frame type +	type jsonFrame struct { +		Func string `json:"func"` +		File string `json:"file"` +		Line int    `json:"line"` +	} + +	// Allocate expected size jsonFrame slice +	jsonFrames := make([]jsonFrame, len(c)) + +	// Convert each to jsonFrame object +	for i := 0; i < len(c); i++ { +		frame := c[i] +		jsonFrames[i] = jsonFrame{ +			Func: funcName(frame.Func), +			File: frame.File, +			Line: frame.Line, +		} +	} + +	// marshal converted frames +	return json.Marshal(jsonFrames) +} + +// String will return a simple string representation of receiving Callers slice. +func (c Callers) String() string { +	// Guess-timate to reduce allocs +	buf := make([]byte, 0, 64*len(c)) + +	for i := 0; i < len(c); i++ { +		frame := c[i] + +		// Append formatted caller info +		fn := funcName(frame.Func) +		buf = append(buf, fn+"()\n\t"+frame.File+":"...) +		buf = strconv.AppendInt(buf, int64(frame.Line), 10) +		buf = append(buf, '\n') +	} + +	return *(*string)(unsafe.Pointer(&buf)) +} + +// funcName formats a function name to a quickly-readable string. +func funcName(fn *runtime.Func) string { +	if fn == nil { +		return "" +	} + +	// Get func name +	// for formatting. +	name := fn.Name() + +	// Drop all but the package name and function name, no mod path +	if idx := strings.LastIndex(name, "/"); idx >= 0 { +		name = name[idx+1:] +	} + +	const params = `[...]` + +	// Drop any generic type parameter markers +	if idx := strings.Index(name, params); idx >= 0 { +		name = name[:idx] + name[idx+len(params):] +	} + +	return name +} + +// gatherFrames collates runtime frames from a frame iterator. +func gatherFrames(iter *runtime.Frames, n int) Callers { +	if iter == nil { +		return nil +	} +	frames := make([]runtime.Frame, 0, n) +	for { +		f, ok := iter.Next() +		if !ok { +			break +		} +		frames = append(frames, f) +	} +	return frames +} diff --git a/vendor/codeberg.org/gruf/go-errors/v2/standard.go b/vendor/codeberg.org/gruf/go-errors/v2/standard.go index 1d2c71c5f..3739416dc 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/standard.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/standard.go @@ -1,133 +1,37 @@  package errors  import ( -	"errors" -	"reflect"  	_ "unsafe" - -	"codeberg.org/gruf/go-bitutil"  ) -// errtype is a ptr to the error interface type. -var errtype = reflect.TypeOf((*error)(nil)).Elem() - -// Comparable is functionally equivalent to calling errors.Is() on multiple errors (up to a max of 64). -func Comparable(err error, targets ...error) bool { -	var flags bitutil.Flags64 - -	// Flags only has 64 bit-slots -	if len(targets) > 64 { -		panic("too many targets") -	} - -	for i := 0; i < len(targets); { -		if targets[i] == nil { -			if err == nil { -				return true -			} - -			// Drop nil targets from slice. -			copy(targets[i:], targets[i+1:]) -			targets = targets[:len(targets)-1] -			continue -		} - -		// Check if this error is directly comparable -		if reflect.TypeOf(targets[i]).Comparable() { -			flags = flags.Set(uint8(i)) -		} - -		i++ -	} - -	for err != nil { -		// Check if this layer supports .Is interface -		is, ok := err.(interface{ Is(error) bool }) - -		if !ok { -			// Error does not support interface -			// -			// Only try perform direct compare -			for i := 0; i < len(targets); i++ { -				// Try directly compare errors -				if flags.Get(uint8(i)) && -					err == targets[i] { -					return true -				} -			} -		} else { -			// Error supports the .Is interface -			// -			// Perform direct compare AND .Is() -			for i := 0; i < len(targets); i++ { -				if (flags.Get(uint8(i)) && -					err == targets[i]) || -					is.Is(targets[i]) { -					return true -				} -			} -		} - -		// Unwrap to next layer -		err = errors.Unwrap(err) -	} - -	return false -} - -// Assignable is functionally equivalent to calling errors.As() on multiple errors, -// except that it only checks assignability as opposed to setting the target. -func Assignable(err error, targets ...error) bool { -	if err == nil { -		// Fastest case. -		return false -	} - -	for i := 0; i < len(targets); { -		if targets[i] == nil { -			// Drop nil targets from slice. -			copy(targets[i:], targets[i+1:]) -			targets = targets[:len(targets)-1] -			continue -		} -		i++ -	} - -	for err != nil { -		// Check if this layer supports .As interface -		as, ok := err.(interface{ As(any) bool }) - -		// Get reflected err type. -		te := reflect.TypeOf(err) - -		if !ok { -			// Error does not support interface. -			// -			// Check assignability using reflection. -			for i := 0; i < len(targets); i++ { -				tt := reflect.TypeOf(targets[i]) -				if te.AssignableTo(tt) { -					return true -				} -			} -		} else { -			// Error supports the .As interface. -			// -			// Check using .As() and reflection. -			for i := 0; i < len(targets); i++ { -				if as.As(targets[i]) { -					return true -				} else if tt := reflect.TypeOf(targets[i]); // nocollapse -				te.AssignableTo(tt) { -					return true -				} -			} +// Is reports whether any error in err's tree matches target. +// +// The tree consists of err itself, followed by the errors obtained by repeatedly +// calling Unwrap. When err wraps multiple errors, Is examines err followed by a +// depth-first traversal of its children. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +// +// An error type might provide an Is method so it can be treated as equivalent +// to an existing error. For example, if MyError defines +// +//	func (m MyError) Is(target error) bool { return target == fs.ErrExist } +// +// then Is(MyError{}, fs.ErrExist) returns true. See [syscall.Errno.Is] for +// an example in the standard library. An Is method should only shallowly +// compare err and the target and not call Unwrap on either. +// +//go:linkname Is errors.Is +func Is(err error, target error) bool + +// IsV2 calls Is(err, target) for each target within targets. +func IsV2(err error, targets ...error) bool { +	for _, target := range targets { +		if Is(err, target) { +			return true  		} - -		// Unwrap to next layer. -		err = errors.Unwrap(err)  	} -  	return false  } @@ -152,8 +56,79 @@ func Assignable(err error, targets ...error) bool {  //go:linkname As errors.As  func As(err error, target any) bool +// AsV2 is functionally similar to As(), instead +// leveraging generics to handle allocation and +// returning of a concrete generic parameter type. +func AsV2[Type any](err error) Type { +	var t Type +	var ok bool +	errs := []error{err} +	for len(errs) > 0 { +		// Pop next error to check. +		err := errs[len(errs)-1] +		errs = errs[:len(errs)-1] + +		// Check direct type. +		t, ok = err.(Type) +		if ok { +			return t +		} + +		// Look for .As() support. +		as, ok := err.(interface { +			As(target any) bool +		}) + +		if ok { +			// Attempt .As(). +			if as.As(&t) { +				return t +			} +		} + +		// Try unwrap errors. +		switch u := err.(type) { +		case interface{ Unwrap() error }: +			errs = append(errs, u.Unwrap()) +		case interface{ Unwrap() []error }: +			errs = append(errs, u.Unwrap()...) +		} +	} +	return t +} +  // Unwrap returns the result of calling the Unwrap method on err, if err's -// type contains an Unwrap method returning error. Otherwise, Unwrap returns nil. +// type contains an Unwrap method returning error. +// Otherwise, Unwrap returns nil. +// +// Unwrap only calls a method of the form "Unwrap() error". +// In particular Unwrap does not unwrap errors returned by [Join].  //  //go:linkname Unwrap errors.Unwrap  func Unwrap(err error) error + +// UnwrapV2 is functionally similar to Unwrap(), except that +// it also handles the case of interface{ Unwrap() []error }. +func UnwrapV2(err error) []error { +	switch u := err.(type) { +	case interface{ Unwrap() error }: +		if e := u.Unwrap(); err != nil { +			return []error{e} +		} +	case interface{ Unwrap() []error }: +		return u.Unwrap() +	} +	return nil +} + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +// +//go:linkname Join errors.Join +func Join(errs ...error) error diff --git a/vendor/codeberg.org/gruf/go-errors/v2/value.go b/vendor/codeberg.org/gruf/go-errors/v2/value.go index 6d7ec3a25..876e5f80e 100644 --- a/vendor/codeberg.org/gruf/go-errors/v2/value.go +++ b/vendor/codeberg.org/gruf/go-errors/v2/value.go @@ -1,56 +1,50 @@  package errors -import "errors" -  // WithValue wraps err to store given key-value pair, accessible via Value() function.  func WithValue(err error, key any, value any) error {  	if err == nil {  		panic("nil error")  	} -	return &errWithValue{ +	var kvs []kv +	if e := AsV2[*errWithValues](err); e != nil { +		kvs = e.kvs +	} +	return &errWithValues{  		err: err, -		key: key, -		val: value, +		kvs: append(kvs, kv{key, value}),  	}  }  // Value searches for value stored under given key in error chain.  func Value(err error, key any) any { -	var e *errWithValue - -	if !errors.As(err, &e) { -		return nil +	if e := AsV2[*errWithValues](err); e != nil { +		return e.Value(key)  	} - -	return e.Value(key) +	return nil  } -type errWithValue struct { +// simple key-value type. +type kv struct{ k, v any } + +// errWithValues wraps an error to provide key-value storage. +type errWithValues struct {  	err error -	key any -	val any +	kvs []kv  } -func (e *errWithValue) Error() string { +func (e *errWithValues) Error() string {  	return e.err.Error()  } -func (e *errWithValue) Is(target error) bool { -	return e.err == target +func (e *errWithValues) Unwrap() error { +	return e.err  } -func (e *errWithValue) Unwrap() error { -	return Unwrap(e.err) -} - -func (e *errWithValue) Value(key any) any { -	for { -		if key == e.key { -			return e.val -		} - -		if !errors.As(e.err, &e) { -			return nil +func (e *errWithValues) Value(key any) any { +	for i := range e.kvs { +		if e.kvs[i].k == key { +			return e.kvs[i].v  		}  	} +	return nil  } diff --git a/vendor/codeberg.org/gruf/go-runners/pool.go b/vendor/codeberg.org/gruf/go-runners/pool.go index 3d9105986..644cde0b9 100644 --- a/vendor/codeberg.org/gruf/go-runners/pool.go +++ b/vendor/codeberg.org/gruf/go-runners/pool.go @@ -221,8 +221,15 @@ func worker_run(ctx context.Context, fns <-chan WorkerFunc) bool {  	defer func() {  		// Recover and drop any panic  		if r := recover(); r != nil { + +			// Gather calling func frames. +			pcs := make([]uintptr, 10) +			n := runtime.Callers(3, pcs) +			i := runtime.CallersFrames(pcs[:n]) +			c := gatherFrames(i, n) +  			const msg = "worker_run: recovered panic: %v\n\n%s\n" -			fmt.Fprintf(os.Stderr, msg, r, errors.GetCallers(2, 10)) +			fmt.Fprintf(os.Stderr, msg, r, c.String())  		}  	}() @@ -243,8 +250,15 @@ func drain_queue(fns <-chan WorkerFunc) bool {  	defer func() {  		// Recover and drop any panic  		if r := recover(); r != nil { -			const msg = "drain_queue: recovered panic: %v\n\n%s\n" -			fmt.Fprintf(os.Stderr, msg, r, errors.GetCallers(2, 10)) + +			// Gather calling func frames. +			pcs := make([]uintptr, 10) +			n := runtime.Callers(3, pcs) +			i := runtime.CallersFrames(pcs[:n]) +			c := gatherFrames(i, n) + +			const msg = "worker_run: recovered panic: %v\n\n%s\n" +			fmt.Fprintf(os.Stderr, msg, r, c.String())  		}  	}() @@ -260,3 +274,19 @@ func drain_queue(fns <-chan WorkerFunc) bool {  		}  	}  } + +// gatherFrames collates runtime frames from a frame iterator. +func gatherFrames(iter *runtime.Frames, n int) errors.Callers { +	if iter == nil { +		return nil +	} +	frames := make([]runtime.Frame, 0, n) +	for { +		f, ok := iter.Next() +		if !ok { +			break +		} +		frames = append(frames, f) +	} +	return frames +}  | 
