summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-kv/v2/format
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/codeberg.org/gruf/go-kv/v2/format')
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/abi.go262
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/args.go247
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/array.go231
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/format.go614
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go161
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/map.go130
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/methods.go153
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go128
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/slice.go158
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/struct.go173
-rw-r--r--vendor/codeberg.org/gruf/go-kv/v2/format/type.go150
11 files changed, 2407 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/abi.go b/vendor/codeberg.org/gruf/go-kv/v2/format/abi.go
new file mode 100644
index 000000000..815660a5e
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/abi.go
@@ -0,0 +1,262 @@
+//go:build go1.24 && !go1.25
+
+package format
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+const (
+ // see: go/src/internal/abi/type.go
+ abi_KindDirectIface uint8 = 1 << 5
+ abi_KindMask uint8 = (1 << 5) - 1
+)
+
+// abi_Type is a copy of the memory layout of abi.Type{}.
+//
+// see: go/src/internal/abi/type.go
+type abi_Type struct {
+ _ uintptr
+ PtrBytes uintptr
+ _ uint32
+ _ uint8
+ _ uint8
+ _ uint8
+ Kind_ uint8
+ _ func(unsafe.Pointer, unsafe.Pointer) bool
+ _ *byte
+ _ int32
+ _ int32
+}
+
+// abi_EmptyInterface is a copy of the memory layout of abi.EmptyInterface{},
+// which is to say also the memory layout of any method-less interface.
+//
+// see: go/src/internal/abi/iface.go
+type abi_EmptyInterface struct {
+ Type *abi_Type
+ Data unsafe.Pointer
+}
+
+// see: go/src/internal/abi/type.go Type.Kind()
+func abi_Type_Kind(t reflect.Type) uint8 {
+ iface := (*reflect_nonEmptyInterface)(unsafe.Pointer(&t))
+ atype := (*abi_Type)(unsafe.Pointer(iface.word))
+ return atype.Kind_ & abi_KindMask
+}
+
+// see: go/src/internal/abi/type.go Type.IfaceIndir()
+func abi_Type_IfaceIndir(t reflect.Type) bool {
+ iface := (*reflect_nonEmptyInterface)(unsafe.Pointer(&t))
+ atype := (*abi_Type)(unsafe.Pointer(iface.word))
+ return atype.Kind_&abi_KindDirectIface == 0
+}
+
+// pack_iface packs a new reflect.nonEmptyInterface{} using shielded itab
+// pointer and data (word) pointer, returning a pointer for caller casting.
+func pack_iface(itab uintptr, word unsafe.Pointer) unsafe.Pointer {
+ return unsafe.Pointer(&reflect_nonEmptyInterface{
+ itab: itab,
+ word: word,
+ })
+}
+
+// get_iface_ITab generates a new value of given type,
+// casts it to the generic param interface type, and
+// returns the .itab portion of the reflect.nonEmptyInterface{}.
+// this is useful for later calls to pack_iface for known type.
+func get_iface_ITab[I any](t reflect.Type) uintptr {
+ s := reflect.New(t).Elem().Interface().(I)
+ i := (*reflect_nonEmptyInterface)(unsafe.Pointer(&s))
+ return i.itab
+}
+
+// unpack_eface returns the .Data portion of an abi.EmptyInterface{}.
+func unpack_eface(a any) unsafe.Pointer {
+ return (*abi_EmptyInterface)(unsafe.Pointer((&a))).Data
+}
+
+// add returns the ptr addition of starting ptr and a delta.
+func add(ptr unsafe.Pointer, delta uintptr) unsafe.Pointer {
+ return unsafe.Pointer(uintptr(ptr) + delta)
+}
+
+// typeof is short-hand for reflect.TypeFor[T]().
+func typeof[T any]() reflect.Type {
+ return reflect.TypeFor[T]()
+}
+
+// see: go/src/reflect/value.go
+type reflect_flag uintptr
+
+const (
+ // see: go/src/reflect/value.go
+ reflect_flagKindWidth = 5 // there are 27 kinds
+ reflect_flagKindMask reflect_flag = 1<<reflect_flagKindWidth - 1
+ reflect_flagStickyRO reflect_flag = 1 << 5
+ reflect_flagEmbedRO reflect_flag = 1 << 6
+ reflect_flagIndir reflect_flag = 1 << 7
+ reflect_flagAddr reflect_flag = 1 << 8
+ reflect_flagMethod reflect_flag = 1 << 9
+ reflect_flagMethodShift = 10
+ reflect_flagRO reflect_flag = reflect_flagStickyRO | reflect_flagEmbedRO
+
+ // custom flag to indicate key types.
+ flagKeyType = 1 << 10
+)
+
+// reflect_iface_elem_flags returns the reflect_flag expected of an unboxed interface element of type.
+//
+// see: go/src/reflect/value.go unpackElem()
+func reflect_iface_elem_flags(elemType reflect.Type) reflect_flag {
+ if elemType == nil {
+ return 0
+ }
+ flags := reflect_flag(abi_Type_Kind(elemType))
+ if abi_Type_IfaceIndir(elemType) {
+ flags |= reflect_flagIndir
+ }
+ return flags
+}
+
+// reflect_pointer_elem_flags returns the reflect_flag expected of a dereferenced pointer element of type.
+//
+// see: go/src/reflect/value.go Value.Elem()
+func reflect_pointer_elem_flags(ptrFlags reflect_flag, elemType reflect.Type) reflect_flag {
+ return ptrFlags | reflect_flagIndir | reflect_flagAddr | reflect_flag(abi_Type_Kind(elemType))
+}
+
+// reflect_array_elem_flags returns the reflect_flag expected of an element of type in an array.
+//
+// see: go/src/reflect/value.go Value.Index()
+func reflect_array_elem_flags(arrayFlags reflect_flag, elemType reflect.Type) reflect_flag {
+ return arrayFlags&(reflect_flagIndir|reflect_flagAddr) | reflect_flag(abi_Type_Kind(elemType))
+}
+
+// reflect_slice_elem_flags returns the reflect_flag expected of a slice element of type.
+//
+// see: go/src/reflect/value.go Value.Index()
+func reflect_slice_elem_flags(elemType reflect.Type) reflect_flag {
+ return reflect_flagAddr | reflect_flagIndir | reflect_flag(abi_Type_Kind(elemType))
+}
+
+// reflect_struct_field_flags returns the reflect_flag expected of a struct field of type.
+//
+// see: go/src/reflect/value.go Value.Field()
+func reflect_struct_field_flags(structFlags reflect_flag, fieldType reflect.Type) reflect_flag {
+ return structFlags&(reflect_flagIndir|reflect_flagAddr) | reflect_flag(abi_Type_Kind(fieldType))
+}
+
+// reflect_map_key_flags returns the reflect_flag expected of a map key of type (with our own key type mask set).
+//
+// see: go/src/reflect/map_swiss.go MapIter.Key()
+func reflect_map_key_flags(keyType reflect.Type) reflect_flag {
+ return flagKeyType | reflect_flag(abi_Type_Kind(keyType))
+}
+
+// reflect_map_elem_flags returns the reflect_flag expected of a map element of type.
+//
+// see: go/src/reflect/map_swiss.go MapIter.Value()
+func reflect_map_elem_flags(elemType reflect.Type) reflect_flag {
+ return reflect_flag(abi_Type_Kind(elemType))
+}
+
+// reflect_nonEmptyInterface is a copy of the memory layout of reflect.nonEmptyInterface,
+// which is also to say the memory layout of any non-empty (i.e. w/ method) interface.
+//
+// see: go/src/reflect/value.go
+type reflect_nonEmptyInterface struct {
+ itab uintptr
+ word unsafe.Pointer
+}
+
+// reflect_Value is a copy of the memory layout of reflect.Value{}.
+//
+// see: go/src/reflect/value.go
+type reflect_Value struct {
+ typ_ unsafe.Pointer
+ ptr unsafe.Pointer
+ reflect_flag
+}
+
+func init() {
+ if unsafe.Sizeof(reflect_Value{}) != unsafe.Sizeof(reflect.Value{}) {
+ panic("reflect_Value{} not in sync with reflect.Value{}")
+ }
+}
+
+// reflect_type_data returns the .word from the reflect.Type{} cast
+// as the reflect.nonEmptyInterface{}, which itself will be a pointer
+// to the actual abi.Type{} that this reflect.Type{} is wrapping.
+func reflect_type_data(t reflect.Type) unsafe.Pointer {
+ return (*reflect_nonEmptyInterface)(unsafe.Pointer(&t)).word
+}
+
+// build_reflect_value manually builds a reflect.Value{} by setting the internal field members.
+func build_reflect_value(rtype reflect.Type, data unsafe.Pointer, flags reflect_flag) reflect.Value {
+ return *(*reflect.Value)(unsafe.Pointer(&reflect_Value{reflect_type_data(rtype), data, flags}))
+}
+
+// maps_Iter is a copy of the memory layout of maps.Iter{}.
+//
+// see: go/src/internal/runtime/maps/table.go
+type maps_Iter struct {
+ key unsafe.Pointer
+ elem unsafe.Pointer
+ _ uintptr
+ _ uintptr
+ _ uint64
+ _ uint64
+ _ uint64
+ _ uint8
+ _ int
+ _ uintptr
+ _ struct{ _ unsafe.Pointer }
+ _ uint64
+}
+
+// reflect_MapIter is a copy of the memory layout of reflect.MapIter{}.
+//
+// see: go/src/reflect/map_swiss.go
+type reflect_MapIter struct {
+ m reflect.Value
+ hiter maps_Iter
+}
+
+func init() {
+ if unsafe.Sizeof(reflect_MapIter{}) != unsafe.Sizeof(reflect.MapIter{}) {
+ panic("reflect_MapIter{} not in sync with reflect.MapIter{}")
+ }
+}
+
+// map_iter creates a new map iterator from value,
+// skipping the initial v.MapRange() type checking.
+func map_iter(v reflect.Value) *reflect.MapIter {
+ var i reflect_MapIter
+ i.m = v
+ return (*reflect.MapIter)(unsafe.Pointer(&i))
+}
+
+// map_key returns ptr to current map key in iter.
+func map_key(i *reflect.MapIter) unsafe.Pointer {
+ return (*reflect_MapIter)(unsafe.Pointer(i)).hiter.key
+}
+
+// map_elem returns ptr to current map element in iter.
+func map_elem(i *reflect.MapIter) unsafe.Pointer {
+ return (*reflect_MapIter)(unsafe.Pointer(i)).hiter.elem
+}
+
+// see: go/src/internal/unsafeheader/unsafeheader.go
+type unsafeheader_Slice struct {
+ Data unsafe.Pointer
+ Len int
+ Cap int
+}
+
+// see: go/src/internal/unsafeheader/unsafeheader.go
+type unsafeheader_String struct {
+ Data unsafe.Pointer
+ Len int
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/args.go b/vendor/codeberg.org/gruf/go-kv/v2/format/args.go
new file mode 100644
index 000000000..c85a04d26
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/args.go
@@ -0,0 +1,247 @@
+package format
+
+const (
+ // TypeMask when set in argument flags
+ // indicates that type information of
+ // the passed value, and all nested types,
+ // should be included in formatted output.
+ TypeMask = uint64(1) << 0
+
+ // LogfmtMask when set in argument flags
+ // indicates that strings should be escaped
+ // and quoted only where necessary. i.e. if
+ // it contains any unsafe ASCII chars or double
+ // quotes it will be quoted and escaped, if it
+ // contains any spaces it will be quoted, and
+ // all else will be printed as-is. This proves
+ // particularly well readable in key-value types.
+ LogfmtMask = uint64(1) << 1
+
+ // NumberMask when set in argument flags
+ // indicates that where possible value
+ // types should be formatted as numbers,
+ // i.e. byte or rune types.
+ NumberMask = uint64(1) << 2
+
+ // TextMask when set in argument flags
+ // indicates that where possible value
+ // types should be formatted as text,
+ // i.e. []byte or []rune types.
+ TextMask = uint64(1) << 3
+
+ // QuotedTextMask when set in argument flags
+ // indicates that text should always be quoted.
+ QuotedTextMask = uint64(1) << 4
+
+ // QuotedAsciiMask when set in argument flags
+ // indicates that text should always be quoted,
+ // and escaped as ASCII characters where needed.
+ QuotedAsciiMask = uint64(1) << 5
+
+ // NoMethodMask when set in argument flags
+ // indicates that where a type supports a
+ // known method, (e.g. Error() or String()),
+ // this should not be used for formatting
+ // instead treating as a method-less type.
+ // e.g. printing the entire struct value of
+ // a &url.URL{} without calling String().
+ NoMethodMask = uint64(1) << 6
+)
+
+var (
+ // default set of Args.
+ defaultArgs = Args{
+ Flags: LogfmtMask | TextMask,
+ Int: IntArgs{Base: 10},
+ Uint: IntArgs{Base: 10},
+ Float: FloatArgs{Fmt: 'g', Prec: -1},
+ Complex: ComplexArgs{
+ Real: FloatArgs{Fmt: 'g', Prec: -1},
+ Imag: FloatArgs{Fmt: 'g', Prec: -1},
+ },
+ }
+
+ // zeroArgs used for checking
+ // zero value Arg{} fields.
+ zeroArgs Args
+)
+
+// DefaultArgs returns default
+// set of formatter arguments.
+func DefaultArgs() Args {
+ return defaultArgs
+}
+
+// Args contains arguments
+// for a call to a FormatFunc.
+type Args struct {
+
+ // Boolean argument
+ // flags as bit-field.
+ Flags uint64
+
+ // Integer
+ // arguments.
+ // i.e. for:
+ // - int
+ // - int8
+ // - int16
+ // - int32 (treated as rune char, number with NumberMask)
+ // - int64
+ Int IntArgs
+
+ // Unsigned
+ // integer
+ // arguments.
+ // i.e. for:
+ // - uint
+ // - uint8 (treated as byte char, number with NumberMask)
+ // - uint16
+ // - uint32
+ // - uint64
+ Uint IntArgs
+
+ // Float
+ // arguments.
+ // i.e. for:
+ // - float32
+ // - float64
+ Float FloatArgs
+
+ // Complex
+ // arguments.
+ // i.e. for:
+ // - complex64
+ // - complex128
+ Complex ComplexArgs
+}
+
+// IntArgs provides a set of
+// arguments for customizing
+// integer number serialization.
+type IntArgs struct {
+ Base int
+ Pad int
+}
+
+// FloatArgs provides a set of
+// arguments for customizing
+// float number serialization.
+type FloatArgs struct {
+ Fmt byte
+ Prec int
+}
+
+// ComplexArgs provides a set of
+// arguments for customizing complex
+// number serialization, as real and
+// imaginary float number parts.
+type ComplexArgs struct {
+ Real FloatArgs
+ Imag FloatArgs
+}
+
+// WithType returns if TypeMask is set.
+func (a *Args) WithType() bool {
+ return a.Flags&TypeMask != 0
+}
+
+// Logfmt returns if LogfmtMask is set.
+func (a *Args) Logfmt() bool {
+ return a.Flags&LogfmtMask != 0
+}
+
+// AsNumber returns if NumberMask is set.
+func (a *Args) AsNumber() bool {
+ return a.Flags&NumberMask != 0
+}
+
+// AsText returns if TextMask is set.
+func (a *Args) AsText() bool {
+ return a.Flags&TextMask != 0
+}
+
+// AsQuotedText returns if QuotedTextMask is set.
+func (a *Args) AsQuotedText() bool {
+ return a.Flags&QuotedTextMask != 0
+}
+
+// AsQuotedASCII returns if QuotedAsciiMask is set.
+func (a *Args) AsQuotedASCII() bool {
+ return a.Flags&QuotedAsciiMask != 0
+}
+
+// NoMethod returns if NoMethodMask is set.
+func (a *Args) NoMethod() bool {
+ return a.Flags&NoMethodMask != 0
+}
+
+// SetWithType sets the TypeMask bit.
+func (a *Args) SetWithType() {
+ a.Flags = a.Flags | TypeMask
+}
+
+// SetLogfmt sets the LogfmtMask bit.
+func (a *Args) SetLogfmt() {
+ a.Flags = a.Flags | LogfmtMask
+}
+
+// SetAsNumber sets the NumberMask bit.
+func (a *Args) SetAsNumber() {
+ a.Flags = a.Flags | NumberMask
+}
+
+// SetAsText sets the TextMask bit.
+func (a *Args) SetAsText() {
+ a.Flags = a.Flags | TextMask
+}
+
+// SetAsQuotedText sets the QuotedTextMask bit.
+func (a *Args) SetAsQuotedText() {
+ a.Flags = a.Flags | QuotedTextMask
+}
+
+// SetAsQuotedASCII sets the QuotedAsciiMask bit.
+func (a *Args) SetAsQuotedASCII() {
+ a.Flags = a.Flags | QuotedAsciiMask
+}
+
+// SetNoMethod sets the NoMethodMask bit.
+func (a *Args) SetNoMethod() {
+ a.Flags = a.Flags | NoMethodMask
+}
+
+// UnsetWithType unsets the TypeMask bit.
+func (a *Args) UnsetWithType() {
+ a.Flags = a.Flags & ^TypeMask
+}
+
+// UnsetLogfmt unsets the LogfmtMask bit.
+func (a *Args) UnsetLogfmt() {
+ a.Flags = a.Flags & ^LogfmtMask
+}
+
+// UnsetAsNumber unsets the NumberMask bit.
+func (a *Args) UnsetAsNumber() {
+ a.Flags = a.Flags & ^NumberMask
+}
+
+// UnsetAsText unsets the TextMask bit.
+func (a *Args) UnsetAsText() {
+ a.Flags = a.Flags & ^TextMask
+}
+
+// UnsetAsQuotedText unsets the QuotedTextMask bit.
+func (a *Args) UnsetAsQuotedText() {
+ a.Flags = a.Flags & ^QuotedTextMask
+}
+
+// UnsetAsQuotedASCII unsets the QuotedAsciiMask bit.
+func (a *Args) UnsetAsQuotedASCII() {
+ a.Flags = a.Flags & ^QuotedAsciiMask
+}
+
+// UnsetNoMethod unsets the NoMethodMask bit.
+func (a *Args) UnsetNoMethod() {
+ a.Flags = a.Flags & ^NoMethodMask
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/array.go b/vendor/codeberg.org/gruf/go-kv/v2/format/array.go
new file mode 100644
index 000000000..ab3a90d39
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/array.go
@@ -0,0 +1,231 @@
+package format
+
+import "unsafe"
+
+// iterArrayType returns a FormatFunc capable of iterating
+// and formatting the given array type currently in typenode{}.
+// note this will fetch a sub-FormatFunc for the array element
+// type, and also handle special cases of [n]byte, [n]rune arrays.
+func (fmt *Formatter) iterArrayType(t typenode) FormatFunc {
+
+ // Array element type.
+ elem := t.rtype.Elem()
+
+ // Get nested elem typenode with appropriate flags.
+ flags := reflect_array_elem_flags(t.flags, elem)
+ et := t.next(elem, flags)
+
+ // Get elem format func.
+ fn := fmt.loadOrGet(et)
+ if fn == nil {
+ panic("unreachable")
+ }
+
+ // Handle possible sizes.
+ switch t.rtype.Len() {
+ case 0:
+ return emptyArrayType(t)
+ case 1:
+ return iterSingleArrayType(t, fn)
+ default:
+ return iterMultiArrayType(t, fn)
+ }
+}
+
+func emptyArrayType(t typenode) FormatFunc {
+ if !t.needs_typestr() {
+ // Simply append empty.
+ return func(s *State) {
+ s.B = append(s.B, "[]"...)
+ }
+ }
+
+ // Array type string with refs.
+ typestr := t.typestr_with_refs()
+
+ // Append empty with type.
+ return func(s *State) {
+ if s.A.WithType() {
+ s.B = append(s.B, typestr...)
+ s.B = append(s.B, "{}"...)
+ } else {
+ s.B = append(s.B, "[]"...)
+ }
+ }
+}
+
+func iterSingleArrayType(t typenode, fn FormatFunc) FormatFunc {
+ if !t.needs_typestr() {
+ return func(s *State) {
+ // Wrap 'fn' in braces.
+ s.B = append(s.B, '[')
+ fn(s)
+ s.B = append(s.B, ']')
+ }
+ }
+
+ // Array type string with refs.
+ typestr := t.typestr_with_refs()
+
+ // Wrap in type+braces.
+ return func(s *State) {
+
+ // Open / close braces.
+ var open, close uint8
+ open, close = '[', ']'
+
+ // Include type info.
+ if s.A.WithType() {
+ s.B = append(s.B, typestr...)
+ open, close = '{', '}'
+ }
+
+ // Wrap 'fn' in braces.
+ s.B = append(s.B, open)
+ fn(s)
+ s.B = append(s.B, close)
+ }
+}
+
+func iterMultiArrayType(t typenode, fn FormatFunc) FormatFunc {
+ // Array element in-memory size.
+ esz := t.rtype.Elem().Size()
+
+ // Number of elements.
+ n := t.rtype.Len()
+
+ if !t.needs_typestr() {
+ // Wrap elems in braces.
+ return func(s *State) {
+ ptr := s.P
+
+ // Prepend array brace.
+ s.B = append(s.B, '[')
+
+ for i := 0; i < n; i++ {
+ // Format at array index.
+ offset := esz * uintptr(i)
+ s.P = add(ptr, offset)
+ fn(s)
+
+ // Append separator.
+ s.B = append(s.B, ',')
+ }
+
+ // Drop final space.
+ s.B = s.B[:len(s.B)-1]
+
+ // Prepend array brace.
+ s.B = append(s.B, ']')
+ }
+ }
+
+ // Array type string with refs.
+ typestr := t.typestr_with_refs()
+
+ // Wrap in type+braces.
+ return func(s *State) {
+ ptr := s.P
+
+ // Open / close braces.
+ var open, close uint8
+ open, close = '[', ']'
+
+ // Include type info.
+ if s.A.WithType() {
+ s.B = append(s.B, typestr...)
+ open, close = '{', '}'
+ }
+
+ // Prepend array brace.
+ s.B = append(s.B, open)
+
+ for i := 0; i < n; i++ {
+ // Format at array index.
+ offset := esz * uintptr(i)
+ s.P = add(ptr, offset)
+ fn(s)
+
+ // Append separator.
+ s.B = append(s.B, ',')
+ }
+
+ // Drop final comma.
+ s.B = s.B[:len(s.B)-1]
+
+ // Prepend array brace.
+ s.B = append(s.B, close)
+ }
+}
+
+func wrapByteArray(t typenode, fn FormatFunc) FormatFunc {
+ n := t.rtype.Len()
+ if !t.needs_typestr() {
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ var v string
+ p := (*unsafeheader_String)(unsafe.Pointer(&v))
+ p.Len = n
+ p.Data = s.P
+ appendString(s, v)
+ } else {
+ fn(s)
+ }
+ }
+ }
+ typestr := t.typestr_with_ptrs()
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ var v string
+ p := (*unsafeheader_String)(unsafe.Pointer(&v))
+ p.Len = n
+ p.Data = s.P
+ if s.A.WithType() {
+ s.B = append(s.B, "("+typestr+")("...)
+ appendString(s, v)
+ s.B = append(s.B, ")"...)
+ } else {
+ appendString(s, v)
+ }
+ } else {
+ fn(s)
+ }
+ }
+}
+
+func wrapRuneArray(t typenode, fn FormatFunc) FormatFunc {
+ n := t.rtype.Len()
+ if !t.needs_typestr() {
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ var v []rune
+ p := (*unsafeheader_Slice)(unsafe.Pointer(&v))
+ p.Cap = n
+ p.Len = n
+ p.Data = s.P
+ appendString(s, string(v))
+ } else {
+ fn(s)
+ }
+ }
+ }
+ typestr := t.typestr_with_ptrs()
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ var v []rune
+ p := (*unsafeheader_Slice)(unsafe.Pointer(&v))
+ p.Cap = n
+ p.Len = n
+ p.Data = s.P
+ if s.A.WithType() {
+ s.B = append(s.B, "("+typestr+")("...)
+ appendString(s, string(v))
+ s.B = append(s.B, ")"...)
+ } else {
+ appendString(s, string(v))
+ }
+ } else {
+ fn(s)
+ }
+ }
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/format.go b/vendor/codeberg.org/gruf/go-kv/v2/format/format.go
new file mode 100644
index 000000000..2ab62eb06
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/format.go
@@ -0,0 +1,614 @@
+package format
+
+import (
+ "reflect"
+ "runtime/debug"
+ "strconv"
+ "sync"
+ "unsafe"
+)
+
+// FormatFunc defines a function capable of formatting
+// the value contained in State{}.P, based on args in
+// State{}.A, storing the result in buffer State{}.B.
+type FormatFunc func(*State)
+
+// State contains all necessary
+// arguments, buffer and value
+// data pointer required for a
+// FormatFunc operation, in a
+// reusable structure if wanted.
+type State struct {
+
+ // A contains args
+ // passed to this
+ // FormatFunc call.
+ A Args
+
+ // B is the buffer
+ // that values will
+ // be formatted into.
+ B []byte
+
+ // P contains a ptr
+ // to the value type
+ // being formatted.
+ P unsafe.Pointer
+
+ // stores pointers to the
+ // recent interface values
+ // we have visited. to prevent
+ // possible recursion of
+ // runtime defined data.
+ ifaces ptr_ring
+}
+
+// ptr_ring size.
+const ringsz = 16
+
+// ptr_ring is a ring buffer of pointers,
+// purposely stored as uintptrs as all we
+// need them for is value comparisons and
+// we don't want to hold-up the GC.
+type ptr_ring struct {
+ p [ringsz]uintptr
+ n uint8
+}
+
+func (p *ptr_ring) contains(ptr unsafe.Pointer) bool {
+ for _, eptr := range p.p {
+ if uintptr(ptr) == eptr {
+ return true
+ }
+ }
+ return false
+}
+
+func (p *ptr_ring) set(ptr unsafe.Pointer) {
+ p.p[p.n%ringsz] = uintptr(ptr)
+ p.n++
+}
+
+func (p *ptr_ring) clear() {
+ p.p = [ringsz]uintptr{}
+ p.n = 0
+}
+
+// Formatter provides access to value formatting
+// provided by this library. It encompasses a set
+// of configurable default arguments for when none
+// are set, and an internal concurrency-safe cache
+// of FormatFuncs to passed value type.
+type Formatter struct {
+
+ // Defaults defines the default
+ // set of arguments to use when
+ // none are supplied to calls to
+ // Append() and AppendState().
+ Defaults Args
+
+ // internal
+ // format func
+ // cache map.
+ fns sync.Map
+}
+
+// LoadFor returns a FormatFunc for the given value type.
+func (fmt *Formatter) LoadFor(value any) FormatFunc {
+ rtype := reflect.TypeOf(value)
+ flags := reflect_iface_elem_flags(rtype)
+ t := new_typenode(rtype, flags)
+ return fmt.loadOrStore(t)
+}
+
+// Append calls AppendState() with a newly allocated State{}, returning byte buffer.
+func (fmt *Formatter) Append(buf []byte, value any, args Args) []byte {
+ s := new(State)
+ s.A = args
+ s.B = buf
+ fmt.AppendState(s, value)
+ return s.B
+}
+
+// AppendState will format the given value into the given
+// State{}'s byte buffer, using currently-set arguments.
+func (fmt *Formatter) AppendState(s *State, value any) {
+ switch {
+ case s.A != zeroArgs:
+ break
+ case fmt.Defaults != zeroArgs:
+ // use fmt defaults.
+ s.A = fmt.Defaults
+ default:
+ // global defaults.
+ s.A = defaultArgs
+ }
+ s.P = unpack_eface(value)
+ s.ifaces.clear()
+ s.ifaces.set(s.P)
+ fmt.LoadFor(value)(s)
+}
+
+func (fmt *Formatter) loadOrGet(t typenode) FormatFunc {
+ // Look for existing stored
+ // func under this type key.
+ v, _ := fmt.fns.Load(t.key())
+ fn, _ := v.(FormatFunc)
+
+ if fn == nil {
+ // Load format func
+ // for typecontext.
+ fn = fmt.get(t)
+ if fn == nil {
+ panic("unreachable")
+ }
+ }
+
+ return fn
+}
+
+func (fmt *Formatter) loadOrStore(t typenode) FormatFunc {
+ // Get cache key.
+ key := t.key()
+
+ // Look for existing stored
+ // func under this type key.
+ v, _ := fmt.fns.Load(key)
+ fn, _ := v.(FormatFunc)
+
+ if fn == nil {
+ // Load format func
+ // for typecontext.
+ fn = fmt.get(t)
+ if fn == nil {
+ panic("unreachable")
+ }
+
+ // Store in map under type.
+ fmt.fns.Store(key, fn)
+ }
+
+ return fn
+}
+
+var (
+ // reflectTypeType is the reflected type of the reflect type,
+ // used in fmt.get() to prevent iter of internal ABI structs.
+ reflectTypeType = reflect.TypeOf(reflect.TypeOf(0))
+
+ // stringable int types.
+ byteType = typeof[byte]()
+ runeType = typeof[rune]()
+
+ // stringable slice types.
+ bytesType = typeof[[]byte]()
+ runesType = typeof[[]rune]()
+)
+
+func (fmt *Formatter) get(t typenode) (fn FormatFunc) {
+ if t.rtype == nil {
+ // catch nil type.
+ return appendNil
+ }
+
+ defer func() {
+ if r := recover(); r != nil {
+ debug.PrintStack()
+ panic(r) // keep panicking
+ } else if fn == nil {
+ panic("nil func")
+ }
+
+ // Don't allow method functions for map keys,
+ // to prevent situation of the method receiver
+ // attempting to modify stored map key itself.
+ if t.flags&flagKeyType != 0 {
+ return
+ }
+
+ // Check if type supports known method receiver.
+ if methodFn := getMethodType(t); methodFn != nil {
+
+ // Keep ptr to existing
+ // non-method format fn.
+ noMethodFn := fn
+
+ // Wrap 'fn' to switch
+ // between method / none.
+ fn = func(s *State) {
+ if s.A.NoMethod() {
+ noMethodFn(s)
+ } else {
+ methodFn(s)
+ }
+ }
+ }
+ }()
+
+ if t.rtype == reflectTypeType {
+ // DO NOT iterate down internal ABI
+ // types, some are in non-GC memory.
+ return getPointerType(t)
+ }
+
+ if !t.visit() {
+ // On type recursion simply
+ // format as raw pointer.
+ return getPointerType(t)
+ }
+
+ // Get func for type kind.
+ switch t.rtype.Kind() {
+ case reflect.Interface:
+ return fmt.getInterfaceType(t)
+ case reflect.String:
+ return getStringType(t)
+ case reflect.Bool:
+ return getBoolType(t)
+ case reflect.Int,
+ reflect.Int8,
+ reflect.Int16,
+ reflect.Int32,
+ reflect.Int64:
+ return getIntType(t)
+ case reflect.Uint,
+ reflect.Uint8,
+ reflect.Uint16,
+ reflect.Uint32,
+ reflect.Uint64:
+ return getUintType(t)
+ case reflect.Float32,
+ reflect.Float64:
+ return getFloatType(t)
+ case reflect.Complex64,
+ reflect.Complex128:
+ return getComplexType(t)
+ case reflect.Pointer:
+ return fmt.derefPointerType(t)
+ case reflect.Array:
+ elem := t.rtype.Elem()
+ switch fn := fmt.iterArrayType(t); {
+ case elem.AssignableTo(byteType):
+ return wrapByteArray(t, fn)
+ case elem.AssignableTo(runeType):
+ return wrapRuneArray(t, fn)
+ default:
+ return fn
+ }
+ case reflect.Slice:
+ switch fn := fmt.iterSliceType(t); {
+ case t.rtype.AssignableTo(bytesType):
+ return wrapByteSlice(t, fn)
+ case t.rtype.AssignableTo(runesType):
+ return wrapRuneSlice(t, fn)
+ default:
+ return fn
+ }
+ case reflect.Struct:
+ return fmt.iterStructType(t)
+ case reflect.Map:
+ return fmt.iterMapType(t)
+ default:
+ return getPointerType(t)
+ }
+}
+
+func (fmt *Formatter) getInterfaceType(t typenode) FormatFunc {
+ if t.rtype.NumMethod() == 0 {
+ return func(s *State) {
+ eface := *(*any)(s.P)
+ s.P = unpack_eface(eface)
+ rtype := reflect.TypeOf(eface)
+ if rtype == nil {
+ appendNil(s)
+ return
+ }
+ if s.ifaces.contains(s.P) {
+ getPointerType(t)(s)
+ return
+ }
+ s.ifaces.set(s.P)
+ flags := reflect_iface_elem_flags(rtype)
+ t := new_typenode(rtype, flags)
+ fmt.loadOrStore(t)(s)
+ }
+ } else {
+ return func(s *State) {
+ iface := *(*interface{ M() })(s.P)
+ s.P = unpack_eface(iface)
+ rtype := reflect.TypeOf(iface)
+ if rtype == nil {
+ appendNil(s)
+ return
+ }
+ if s.ifaces.contains(s.P) {
+ getPointerType(t)(s)
+ return
+ }
+ s.ifaces.set(s.P)
+ flags := reflect_iface_elem_flags(rtype)
+ t := new_typenode(rtype, flags)
+ fmt.loadOrStore(t)(s)
+ }
+ }
+}
+
+func getStringType(t typenode) FormatFunc {
+ return with_typestr_ptrs(t, func(s *State) {
+ appendString(s, *(*string)(s.P))
+ })
+}
+
+func getBoolType(t typenode) FormatFunc {
+ return with_typestr_ptrs(t, func(s *State) {
+ s.B = strconv.AppendBool(s.B, *(*bool)(s.P))
+ })
+}
+
+func getIntType(t typenode) FormatFunc {
+ switch t.rtype.Bits() {
+ case 8:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendInt(s, int64(*(*int8)(s.P)))
+ })
+ case 16:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendInt(s, int64(*(*int16)(s.P)))
+ })
+ case 32:
+ return with_typestr_ptrs(t, func(s *State) {
+ switch {
+ case s.A.AsNumber():
+ // fallthrough
+ case s.A.AsQuotedText():
+ s.B = strconv.AppendQuoteRune(s.B, *(*rune)(s.P))
+ return
+ case s.A.AsQuotedASCII():
+ s.B = strconv.AppendQuoteRuneToASCII(s.B, *(*rune)(s.P))
+ return
+ case s.A.AsText():
+ s.B = AppendEscapeRune(s.B, *(*rune)(s.P))
+ return
+ }
+ appendInt(s, int64(*(*int32)(s.P)))
+ })
+ case 64:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendInt(s, int64(*(*int64)(s.P)))
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+func getUintType(t typenode) FormatFunc {
+ switch t.rtype.Bits() {
+ case 8:
+ return with_typestr_ptrs(t, func(s *State) {
+ switch {
+ case s.A.AsNumber():
+ // fallthrough
+ case s.A.AsQuotedText() || s.A.AsQuotedASCII():
+ s.B = AppendQuoteByte(s.B, *(*byte)(s.P))
+ return
+ case s.A.AsText():
+ s.B = AppendEscapeByte(s.B, *(*byte)(s.P))
+ return
+ }
+ appendUint(s, uint64(*(*uint8)(s.P)))
+ })
+ case 16:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendUint(s, uint64(*(*uint16)(s.P)))
+ })
+ case 32:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendUint(s, uint64(*(*uint32)(s.P)))
+ })
+ case 64:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendUint(s, uint64(*(*uint64)(s.P)))
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+func getFloatType(t typenode) FormatFunc {
+ switch t.rtype.Bits() {
+ case 32:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendFloat(s, float64(*(*float32)(s.P)), 32)
+ })
+ case 64:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendFloat(s, float64(*(*float64)(s.P)), 64)
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+func getComplexType(t typenode) FormatFunc {
+ switch t.rtype.Bits() {
+ case 64:
+ return with_typestr_ptrs(t, func(s *State) {
+ v := *(*complex64)(s.P)
+ r, i := real(v), imag(v)
+ appendComplex(s, float64(r), float64(i), 32)
+ })
+ case 128:
+ return with_typestr_ptrs(t, func(s *State) {
+ v := *(*complex128)(s.P)
+ r, i := real(v), imag(v)
+ appendComplex(s, float64(r), float64(i), 64)
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+func getPointerType(t typenode) FormatFunc {
+ switch t.indirect() {
+ case true:
+ return with_typestr_ptrs(t, func(s *State) {
+ s.P = *(*unsafe.Pointer)(s.P)
+ appendPointer(s, s.P)
+ })
+ case false:
+ return with_typestr_ptrs(t, func(s *State) {
+ appendPointer(s, s.P)
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+func with_typestr_ptrs(t typenode, fn FormatFunc) FormatFunc {
+ if fn == nil {
+ panic("nil func")
+ }
+ if !t.needs_typestr() {
+ return fn
+ }
+ typestr := t.typestr_with_ptrs()
+ return func(s *State) {
+ if s.A.WithType() {
+ s.B = append(s.B, "("+typestr+")("...)
+ fn(s)
+ s.B = append(s.B, ")"...)
+ } else {
+ fn(s)
+ }
+ }
+}
+
+func appendString(s *State, v string) {
+ switch {
+ case s.A.Logfmt() || s.A.WithType():
+ if len(v) > SingleTermLine || !IsSafeASCII(v) {
+ // Requires quoting AND escaping
+ s.B = strconv.AppendQuote(s.B, v)
+ } else if ContainsDoubleQuote(v) {
+ // Contains double quotes, needs escaping
+ s.B = append(s.B, '"')
+ s.B = AppendEscape(s.B, v)
+ s.B = append(s.B, '"')
+ } else if s.A.WithType() ||
+ len(v) == 0 || ContainsSpaceOrTab(v) {
+ // Contains space / empty, needs quotes
+ s.B = append(s.B, '"')
+ s.B = append(s.B, v...)
+ s.B = append(s.B, '"')
+ } else {
+ // All else write as-is
+ s.B = append(s.B, v...)
+ }
+ case s.A.AsQuotedText():
+ s.B = strconv.AppendQuote(s.B, v)
+ case s.A.AsQuotedASCII():
+ s.B = strconv.AppendQuoteToASCII(s.B, v)
+ default:
+ s.B = append(s.B, v...)
+ }
+}
+
+func appendInt(s *State, v int64) {
+ args := s.A.Int
+ if args == zeroArgs.Int {
+ args = defaultArgs.Int
+ }
+ if args.Pad > 0 {
+ const zeros = `00000000000000000000`
+ if args.Pad > len(zeros) {
+ panic("cannot pad > " + zeros)
+ }
+ if v == 0 {
+ s.B = append(s.B, zeros[:args.Pad]...)
+ return
+ }
+ abs := abs64(v)
+ chars := int(v / int64(args.Base))
+ if v%int64(args.Base) != 0 {
+ chars++
+ }
+ if abs != v {
+ s.B = append(s.B, '-')
+ v = abs
+ }
+ if n := args.Pad - chars; n > 0 {
+ s.B = append(s.B, zeros[:n]...)
+ }
+ }
+ s.B = strconv.AppendInt(s.B, v, args.Base)
+}
+
+func appendUint(s *State, v uint64) {
+ args := s.A.Int
+ if args == zeroArgs.Int {
+ args = defaultArgs.Int
+ }
+ if args.Pad > 0 {
+ const zeros = `00000000000000000000`
+ if args.Pad > len(zeros) {
+ panic("cannot pad > " + zeros)
+ }
+ if v == 0 {
+ s.B = append(s.B, zeros[:args.Pad]...)
+ return
+ }
+ chars := int(v / uint64(args.Base))
+ if v%uint64(args.Base) != 0 {
+ chars++
+ }
+ if n := args.Pad - chars; n > 0 {
+ s.B = append(s.B, zeros[:n]...)
+ }
+ }
+ s.B = strconv.AppendUint(s.B, v, args.Base)
+}
+
+func appendFloat(s *State, v float64, bits int) {
+ args := s.A.Float
+ if args == zeroArgs.Float {
+ args = defaultArgs.Float
+ }
+ s.B = strconv.AppendFloat(s.B, float64(v), args.Fmt, args.Prec, bits)
+}
+
+func appendComplex(s *State, r, i float64, bits int) {
+ args := s.A.Complex
+ if args == zeroArgs.Complex {
+ args = defaultArgs.Complex
+ }
+ s.B = strconv.AppendFloat(s.B, float64(r), args.Real.Fmt, args.Real.Prec, bits)
+ s.B = append(s.B, '+')
+ s.B = strconv.AppendFloat(s.B, float64(i), args.Imag.Fmt, args.Imag.Prec, bits)
+ s.B = append(s.B, 'i')
+}
+
+func appendPointer(s *State, v unsafe.Pointer) {
+ if v != nil {
+ s.B = append(s.B, "0x"...)
+ s.B = strconv.AppendUint(s.B, uint64(uintptr(v)), 16)
+ } else {
+ appendNil(s)
+ }
+}
+
+func appendNilType(s *State, typestr string) {
+ if s.A.WithType() {
+ s.B = append(s.B, "("+typestr+")(<nil>)"...)
+ } else {
+ s.B = append(s.B, "<nil>"...)
+ }
+}
+
+func appendNil(s *State) {
+ s.B = append(s.B, "<nil>"...)
+}
+
+func abs64(i int64) int64 {
+ u := uint64(i >> 63)
+ return (i ^ int64(u)) + int64(u&1)
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go b/vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go
new file mode 100644
index 000000000..e09edbefc
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go
@@ -0,0 +1,161 @@
+package format
+
+import (
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+const (
+ // SingleTermLine: beyond a certain length of string, all of the
+ // extra checks to handle quoting/not-quoting add a significant
+ // amount of extra processing time. Quoting in this manner only really
+ // effects readability on a single line, so a max string length that
+ // encompasses the maximum number of columns on *most* terminals was
+ // selected. This was chosen using the metric that 1080p is one of the
+ // most common display resolutions, and that a relatively small font size
+ // of 7 requires 223 columns. So 256 should be >= $COLUMNS (fullscreen)
+ // in 99% of usecases (these figures all pulled out of my ass).
+ SingleTermLine = 256
+)
+
+// IsSafeASCII checks whether string is printable (i.e. non-control char) ASCII text.
+func IsSafeASCII(str string) bool {
+ for _, r := range str {
+ if (r < ' ' && r != '\t') ||
+ r >= 0x7f {
+ return false
+ }
+ }
+ return true
+}
+
+// ContainsSpaceOrTab checks if "s" contains space or tabs. EXPECTS ASCII.
+func ContainsSpaceOrTab(s string) bool {
+ if i := strings.IndexByte(s, ' '); i >= 0 {
+ return true // note using indexbyte as it is ASM.
+ } else if i := strings.IndexByte(s, '\t'); i >= 0 {
+ return true
+ }
+ return false
+}
+
+// ContainsDoubleQuote checks if "s" contains a double quote. EXPECTS ASCII.
+func ContainsDoubleQuote(s string) bool {
+ return (strings.IndexByte(s, '"') >= 0)
+}
+
+// AppendEscape will append 's' to 'buf' and escape any double quotes. EXPECTS ASCII.
+func AppendEscape(buf []byte, str string) []byte {
+ for i := range str {
+ switch str[i] {
+ case '\\':
+ // Append delimited '\'
+ buf = append(buf, '\\', '\\')
+
+ case '"':
+ // Append delimited '"'
+ buf = append(buf, '\\', '"')
+ default:
+ // Append char as-is
+ buf = append(buf, str[i])
+ }
+ }
+ return buf
+}
+
+const hex = "0123456789abcdef"
+
+// AppendEscapeByte ...
+func AppendEscapeByte(buf []byte, c byte) []byte {
+ switch c {
+ case '\a':
+ return append(buf, `\a`...)
+ case '\b':
+ return append(buf, `\b`...)
+ case '\f':
+ return append(buf, `\f`...)
+ case '\n':
+ return append(buf, `\n`...)
+ case '\r':
+ return append(buf, `\r`...)
+ case '\t':
+ return append(buf, `\t`...)
+ case '\v':
+ return append(buf, `\v`...)
+ case '\\':
+ return append(buf, `\\`...)
+ default:
+ if c < ' ' {
+ return append(buf, '\\', 'x', hex[c>>4], hex[c&0xF])
+ }
+ return append(buf, c)
+ }
+}
+
+// AppendQuoteByte ...
+func AppendQuoteByte(buf []byte, c byte) []byte {
+ if c == '\'' {
+ return append(buf, `'\''`...)
+ }
+ buf = append(buf, '\'')
+ buf = AppendEscapeByte(buf, c)
+ buf = append(buf, '\'')
+ return buf
+}
+
+// AppendEscapeRune ...
+func AppendEscapeRune(buf []byte, r rune) []byte {
+ if unicode.IsPrint(r) {
+ return utf8.AppendRune(buf, r)
+ }
+ switch r {
+ case '\a':
+ return append(buf, `\a`...)
+ case '\b':
+ return append(buf, `\b`...)
+ case '\f':
+ return append(buf, `\f`...)
+ case '\n':
+ return append(buf, `\n`...)
+ case '\r':
+ return append(buf, `\r`...)
+ case '\t':
+ return append(buf, `\t`...)
+ case '\v':
+ return append(buf, `\v`...)
+ case '\\':
+ return append(buf, `\\`...)
+ default:
+ switch {
+ case r < ' ' || r == 0x7f:
+ buf = append(buf, `\x`...)
+ buf = append(buf, hex[byte(r)>>4])
+ buf = append(buf, hex[byte(r)&0xF])
+ case !utf8.ValidRune(r):
+ r = 0xFFFD
+ fallthrough
+ case r < 0x10000:
+ buf = append(buf, `\u`...)
+ buf = append(buf,
+ hex[r>>uint(12)&0xF],
+ hex[r>>uint(8)&0xF],
+ hex[r>>uint(4)&0xF],
+ hex[r>>uint(0)&0xF],
+ )
+ default:
+ buf = append(buf, `\U`...)
+ buf = append(buf,
+ hex[r>>uint(28)&0xF],
+ hex[r>>uint(24)&0xF],
+ hex[r>>uint(20)&0xF],
+ hex[r>>uint(16)&0xF],
+ hex[r>>uint(12)&0xF],
+ hex[r>>uint(8)&0xF],
+ hex[r>>uint(4)&0xF],
+ hex[r>>uint(0)&0xF],
+ )
+ }
+ }
+ return buf
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/map.go b/vendor/codeberg.org/gruf/go-kv/v2/format/map.go
new file mode 100644
index 000000000..038fb74ac
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/map.go
@@ -0,0 +1,130 @@
+package format
+
+import "unsafe"
+
+// iterMapType returns a FormatFunc capable of iterating
+// and formatting the given map type currently in typenode{}.
+// note this will fetch sub-FormatFuncs for key / value types.
+func (fmt *Formatter) iterMapType(t typenode) FormatFunc {
+
+ // Key / value types.
+ key := t.rtype.Key()
+ elem := t.rtype.Elem()
+
+ // Get nested k / v typenodes with appropriate flags.
+ flagsKey := reflect_map_key_flags(key)
+ flagsVal := reflect_map_elem_flags(elem)
+ kt := t.next(t.rtype.Key(), flagsKey)
+ vt := t.next(t.rtype.Elem(), flagsVal)
+
+ // Get key format func.
+ kfn := fmt.loadOrGet(kt)
+ if kfn == nil {
+ panic("unreachable")
+ }
+
+ // Get value format func.
+ vfn := fmt.loadOrGet(vt)
+ if vfn == nil {
+ panic("unreachable")
+ }
+
+ // Final map type.
+ rtype := t.rtype
+ flags := t.flags
+
+ // Map type string with ptrs / refs.
+ typestrPtrs := t.typestr_with_ptrs()
+ typestrRefs := t.typestr_with_refs()
+
+ if !t.needs_typestr() {
+ return func(s *State) {
+ if s.P == nil || *(*unsafe.Pointer)(s.P) == nil {
+ // Append nil.
+ appendNil(s)
+ return
+ }
+
+ // Build reflect value, and then a map iter.
+ v := build_reflect_value(rtype, s.P, flags)
+ i := map_iter(v)
+
+ // Prepend object brace.
+ s.B = append(s.B, '{')
+
+ // Before len.
+ l := len(s.B)
+
+ for i.Next() {
+ // Pass to key fn.
+ s.P = map_key(i)
+ kfn(s)
+
+ // Add key seperator.
+ s.B = append(s.B, '=')
+
+ // Pass to elem fn.
+ s.P = map_elem(i)
+ vfn(s)
+
+ // Add comma pair seperator.
+ s.B = append(s.B, ',', ' ')
+ }
+
+ if len(s.B) != l {
+ // Drop final ", ".
+ s.B = s.B[:len(s.B)-2]
+ }
+
+ // Append object brace.
+ s.B = append(s.B, '}')
+ }
+ }
+
+ return func(s *State) {
+ if s.P == nil || *(*unsafe.Pointer)(s.P) == nil {
+ // Append nil value with type.
+ appendNilType(s, typestrPtrs)
+ return
+ }
+
+ // Build reflect value, and then a map iter.
+ v := build_reflect_value(rtype, s.P, flags)
+ i := map_iter(v)
+
+ // Include type info.
+ if s.A.WithType() {
+ s.B = append(s.B, typestrRefs...)
+ }
+
+ // Prepend object brace.
+ s.B = append(s.B, '{')
+
+ // Before len.
+ l := len(s.B)
+
+ for i.Next() {
+ // Pass to key fn.
+ s.P = map_key(i)
+ kfn(s)
+
+ // Add key seperator.
+ s.B = append(s.B, '=')
+
+ // Pass to elem fn.
+ s.P = map_elem(i)
+ vfn(s)
+
+ // Add comma pair seperator.
+ s.B = append(s.B, ',', ' ')
+ }
+
+ if len(s.B) != l {
+ // Drop final ", ".
+ s.B = s.B[:len(s.B)-2]
+ }
+
+ // Append object brace.
+ s.B = append(s.B, '}')
+ }
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go b/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go
new file mode 100644
index 000000000..7c1795771
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/methods.go
@@ -0,0 +1,153 @@
+package format
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+type Stringer interface{ String() string }
+
+var (
+ // stringer type for implement checks.
+ stringerType = typeof[Stringer]()
+
+ // error type for implement checks.
+ errorType = typeof[error]()
+)
+
+// getMethodType returns a *possible* FormatFunc to handle case
+// of a type that implements any known interface{} types, else nil.
+func getMethodType(t typenode) FormatFunc {
+ switch {
+ case t.rtype.Implements(stringerType):
+ switch t.rtype.Kind() {
+ case reflect.Interface:
+ return getInterfaceStringerType(t)
+ default:
+ return getConcreteStringerType(t)
+ }
+ case t.rtype.Implements(errorType):
+ switch t.rtype.Kind() {
+ case reflect.Interface:
+ return getInterfaceErrorType(t)
+ default:
+ return getConcreteErrorType(t)
+ }
+ default:
+ return nil
+ }
+}
+
+// getInterfaceStringerType returns a FormatFunc to handle case of an interface{}
+// type that implements Stringer{}, i.e. Stringer{} itself and any superset of.
+func getInterfaceStringerType(t typenode) FormatFunc {
+ switch t.indirect() && !t.iface_indir() {
+ case true:
+ return with_typestr_ptrs(t, func(s *State) {
+ s.P = *(*unsafe.Pointer)(s.P)
+ if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*Stringer)(s.P)
+ appendString(s, v.String())
+ })
+ case false:
+ return with_typestr_ptrs(t, func(s *State) {
+ if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*Stringer)(s.P)
+ appendString(s, v.String())
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+// getConcreteStringerType returns a FormatFunc to handle case of concrete
+// (i.e. non-interface{}) type that has a Stringer{} method receiver.
+func getConcreteStringerType(t typenode) FormatFunc {
+ itab := get_iface_ITab[Stringer](t.rtype)
+ switch t.indirect() && !t.iface_indir() {
+ case true:
+ return with_typestr_ptrs(t, func(s *State) {
+ s.P = *(*unsafe.Pointer)(s.P)
+ if s.P == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*Stringer)(pack_iface(itab, s.P))
+ appendString(s, v.String())
+ })
+ case false:
+ return with_typestr_ptrs(t, func(s *State) {
+ if s.P == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*Stringer)(pack_iface(itab, s.P))
+ appendString(s, v.String())
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+// getInterfaceErrorType returns a FormatFunc to handle case of an interface{}
+// type that implements error{}, i.e. error{} itself and any superset of.
+func getInterfaceErrorType(t typenode) FormatFunc {
+ switch t.indirect() && !t.iface_indir() {
+ case true:
+ return with_typestr_ptrs(t, func(s *State) {
+ s.P = *(*unsafe.Pointer)(s.P)
+ if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*error)(s.P)
+ appendString(s, v.Error())
+ })
+ case false:
+ return with_typestr_ptrs(t, func(s *State) {
+ if s.P == nil || (*reflect_nonEmptyInterface)(s.P).word == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*error)(s.P)
+ appendString(s, v.Error())
+ })
+ default:
+ panic("unreachable")
+ }
+}
+
+// getConcreteErrorType returns a FormatFunc to handle case of concrete
+// (i.e. non-interface{}) type that has an error{} method receiver.
+func getConcreteErrorType(t typenode) FormatFunc {
+ itab := get_iface_ITab[error](t.rtype)
+ switch t.indirect() && !t.iface_indir() {
+ case true:
+ return with_typestr_ptrs(t, func(s *State) {
+ s.P = *(*unsafe.Pointer)(s.P)
+ if s.P == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*error)(pack_iface(itab, s.P))
+ appendString(s, v.Error())
+ })
+ case false:
+ return with_typestr_ptrs(t, func(s *State) {
+ if s.P == nil {
+ appendNil(s)
+ return
+ }
+ v := *(*error)(pack_iface(itab, s.P))
+ appendString(s, v.Error())
+ })
+ default:
+ panic("unreachable")
+ }
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go b/vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go
new file mode 100644
index 000000000..1f860aba9
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go
@@ -0,0 +1,128 @@
+package format
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+// derefPointerType returns a FormatFunc capable of dereferencing
+// and formatting the given pointer type currently in typenode{}.
+// note this will fetch a sub-FormatFunc for resulting value type.
+func (fmt *Formatter) derefPointerType(t typenode) FormatFunc {
+ var n int
+ rtype := t.rtype
+ flags := t.flags
+
+ // Iteratively dereference pointer types.
+ for rtype.Kind() == reflect.Pointer {
+
+ // If this is actual indirect
+ // memory, increase dereferences.
+ if flags&reflect_flagIndir != 0 {
+ n++
+ }
+
+ // Get next elem type.
+ rtype = rtype.Elem()
+
+ // Get next set of dereferenced elem type flags.
+ flags = reflect_pointer_elem_flags(flags, rtype)
+ }
+
+ // Wrap value as typenode.
+ vt := t.next(rtype, flags)
+
+ // Get value format func.
+ fn := fmt.loadOrGet(vt)
+ if fn == nil {
+ panic("unreachable")
+ }
+
+ if !t.needs_typestr() {
+ if n <= 0 {
+ // No derefs are needed.
+ return func(s *State) {
+ if s.P == nil {
+ // Final check.
+ appendNil(s)
+ return
+ }
+
+ // Format
+ // final
+ // value.
+ fn(s)
+ }
+ }
+
+ return func(s *State) {
+ // Deref n number times.
+ for i := n; i > 0; i-- {
+
+ if s.P == nil {
+ // Nil check.
+ appendNil(s)
+ return
+ }
+
+ // Further deref pointer value.
+ s.P = *(*unsafe.Pointer)(s.P)
+ }
+
+ if s.P == nil {
+ // Final check.
+ appendNil(s)
+ return
+ }
+
+ // Format
+ // final
+ // value.
+ fn(s)
+ }
+ }
+
+ // Final type string with ptrs.
+ typestr := t.typestr_with_ptrs()
+
+ if n <= 0 {
+ // No derefs are needed.
+ return func(s *State) {
+ if s.P == nil {
+ // Final nil value check.
+ appendNilType(s, typestr)
+ return
+ }
+
+ // Format
+ // final
+ // value.
+ fn(s)
+ }
+ }
+
+ return func(s *State) {
+ // Deref n number times.
+ for i := n; i > 0; i-- {
+ if s.P == nil {
+ // Check for nil value.
+ appendNilType(s, typestr)
+ return
+ }
+
+ // Further deref pointer value.
+ s.P = *(*unsafe.Pointer)(s.P)
+ }
+
+ if s.P == nil {
+ // Final nil value check.
+ appendNilType(s, typestr)
+ return
+ }
+
+ // Format
+ // final
+ // value.
+ fn(s)
+ }
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/slice.go b/vendor/codeberg.org/gruf/go-kv/v2/format/slice.go
new file mode 100644
index 000000000..f76e85410
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/slice.go
@@ -0,0 +1,158 @@
+package format
+
+// iterSliceType returns a FormatFunc capable of iterating
+// and formatting the given slice type currently in typenode{}.
+// note this will fetch a sub-FormatFunc for the slice element
+// type, and also handle special cases of []byte, []rune slices.
+func (fmt *Formatter) iterSliceType(t typenode) FormatFunc {
+
+ // Get nested element type.
+ elem := t.rtype.Elem()
+ esz := elem.Size()
+
+ // Get nested elem typenode with flags.
+ flags := reflect_slice_elem_flags(elem)
+ et := t.next(elem, flags)
+
+ // Get elem format func.
+ fn := fmt.loadOrGet(et)
+ if fn == nil {
+ panic("unreachable")
+ }
+
+ if !t.needs_typestr() {
+ return func(s *State) {
+ ptr := s.P
+
+ // Get data as unsafe slice header.
+ hdr := (*unsafeheader_Slice)(ptr)
+ if hdr == nil || hdr.Data == nil {
+
+ // Append nil.
+ appendNil(s)
+ return
+ }
+
+ // Prepend array brace.
+ s.B = append(s.B, '[')
+
+ if hdr.Len > 0 {
+ for i := 0; i < hdr.Len; i++ {
+ // Format at array index.
+ offset := esz * uintptr(i)
+ s.P = add(hdr.Data, offset)
+ fn(s)
+
+ // Append separator.
+ s.B = append(s.B, ',')
+ }
+
+ // Drop final comma.
+ s.B = s.B[:len(s.B)-1]
+ }
+
+ // Append array brace.
+ s.B = append(s.B, ']')
+ }
+ }
+
+ // Slice type string with ptrs / refs.
+ typestrPtrs := t.typestr_with_ptrs()
+ typestrRefs := t.typestr_with_refs()
+
+ return func(s *State) {
+ ptr := s.P
+
+ // Get data as unsafe slice header.
+ hdr := (*unsafeheader_Slice)(ptr)
+ if hdr == nil || hdr.Data == nil {
+
+ // Append nil value with type.
+ appendNilType(s, typestrPtrs)
+ return
+ }
+
+ // Open / close braces.
+ var open, close uint8
+ open, close = '[', ']'
+
+ // Include type info.
+ if s.A.WithType() {
+ s.B = append(s.B, typestrRefs...)
+ open, close = '{', '}'
+ }
+
+ // Prepend array brace.
+ s.B = append(s.B, open)
+
+ if hdr.Len > 0 {
+ for i := 0; i < hdr.Len; i++ {
+ // Format at array index.
+ offset := esz * uintptr(i)
+ s.P = add(hdr.Data, offset)
+ fn(s)
+
+ // Append separator.
+ s.B = append(s.B, ',')
+ }
+
+ // Drop final comma.
+ s.B = s.B[:len(s.B)-1]
+ }
+
+ // Append array brace.
+ s.B = append(s.B, close)
+ }
+}
+
+func wrapByteSlice(t typenode, fn FormatFunc) FormatFunc {
+ if !t.needs_typestr() {
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ appendString(s, *(*string)(s.P))
+ } else {
+ fn(s)
+ }
+ }
+ }
+ typestr := t.typestr_with_ptrs()
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ if s.A.WithType() {
+ s.B = append(s.B, "("+typestr+")("...)
+ appendString(s, *(*string)(s.P))
+ s.B = append(s.B, ")"...)
+ } else {
+ appendString(s, *(*string)(s.P))
+ }
+ } else {
+ fn(s)
+ }
+ }
+}
+
+func wrapRuneSlice(t typenode, fn FormatFunc) FormatFunc {
+ if !t.needs_typestr() {
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ appendString(s, string(*(*[]rune)(s.P)))
+ } else {
+ fn(s)
+ }
+ }
+ }
+ typestr := t.typestr_with_ptrs()
+ return func(s *State) {
+ if s.A.AsText() || s.A.AsQuotedText() || s.A.AsQuotedASCII() {
+ if s.A.WithType() {
+ s.B = append(s.B, "("+typestr+")("...)
+ appendString(s, string(*(*[]rune)(s.P)))
+ s.B = append(s.B, ")"...)
+ } else {
+ appendString(s, string(*(*[]rune)(s.P)))
+ }
+ } else {
+ fn(s)
+ }
+ }
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/struct.go b/vendor/codeberg.org/gruf/go-kv/v2/format/struct.go
new file mode 100644
index 000000000..cc1c8634d
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/struct.go
@@ -0,0 +1,173 @@
+package format
+
+// field stores the minimum necessary
+// data for iterating and formatting
+// each field in a given struct.
+type field struct {
+ format FormatFunc
+ name string
+ offset uintptr
+}
+
+// iterStructType returns a FormatFunc capable of iterating
+// and formatting the given struct type currently in typenode{}.
+// note this will fetch sub-FormatFuncs for each struct field.
+func (fmt *Formatter) iterStructType(t typenode) FormatFunc {
+ // Number of struct fields.
+ n := t.rtype.NumField()
+
+ // Gather format functions.
+ fields := make([]field, n)
+ for i := 0; i < n; i++ {
+
+ // Get struct field at index.
+ sfield := t.rtype.Field(i)
+ rtype := sfield.Type
+
+ // Get nested field typenode with appropriate flags.
+ flags := reflect_struct_field_flags(t.flags, rtype)
+ ft := t.next(sfield.Type, flags)
+
+ // Get field format func.
+ fn := fmt.loadOrGet(ft)
+ if fn == nil {
+ panic("unreachable")
+ }
+
+ // Set field info.
+ fields[i] = field{
+ format: fn,
+ name: sfield.Name,
+ offset: sfield.Offset,
+ }
+ }
+
+ // Handle no. fields.
+ switch len(fields) {
+ case 0:
+ return emptyStructType(t)
+ case 1:
+ return iterSingleFieldStructType(t, fields[0])
+ default:
+ return iterMultiFieldStructType(t, fields)
+ }
+}
+
+func emptyStructType(t typenode) FormatFunc {
+ if !t.needs_typestr() {
+ return func(s *State) {
+ // Append empty object.
+ s.B = append(s.B, "{}"...)
+ }
+ }
+
+ // Struct type string with refs.
+ typestr := t.typestr_with_refs()
+
+ // Append empty object
+ // with type information.
+ return func(s *State) {
+ if s.A.WithType() {
+ s.B = append(s.B, typestr...)
+ }
+ s.B = append(s.B, "{}"...)
+ }
+}
+
+func iterSingleFieldStructType(t typenode, field field) FormatFunc {
+ if field.format == nil {
+ panic("nil func")
+ }
+
+ if !t.needs_typestr() {
+ return func(s *State) {
+ // Wrap 'fn' with braces + field name.
+ s.B = append(s.B, "{"+field.name+"="...)
+ field.format(s)
+ s.B = append(s.B, "}"...)
+ }
+ }
+
+ // Struct type string with refs.
+ typestr := t.typestr_with_refs()
+
+ return func(s *State) {
+ // Include type info.
+ if s.A.WithType() {
+ s.B = append(s.B, typestr...)
+ }
+
+ // Wrap 'fn' with braces + field name.
+ s.B = append(s.B, "{"+field.name+"="...)
+ field.format(s)
+ s.B = append(s.B, "}"...)
+ }
+}
+
+func iterMultiFieldStructType(t typenode, fields []field) FormatFunc {
+ for _, field := range fields {
+ if field.format == nil {
+ panic("nil func")
+ }
+ }
+
+ if !t.needs_typestr() {
+ return func(s *State) {
+ ptr := s.P
+
+ // Prepend object brace.
+ s.B = append(s.B, '{')
+
+ for i := 0; i < len(fields); i++ {
+ // Get struct field ptr via offset.
+ s.P = add(ptr, fields[i].offset)
+
+ // Append field name and value separator.
+ s.B = append(s.B, fields[i].name+"="...)
+
+ // Format i'th field.
+ fields[i].format(s)
+ s.B = append(s.B, ',', ' ')
+ }
+
+ // Drop final ", ".
+ s.B = s.B[:len(s.B)-2]
+
+ // Append object brace.
+ s.B = append(s.B, '}')
+ }
+ }
+
+ // Struct type string with refs.
+ typestr := t.typestr_with_refs()
+
+ return func(s *State) {
+ ptr := s.P
+
+ // Include type info.
+ if s.A.WithType() {
+ s.B = append(s.B, typestr...)
+ }
+
+ // Prepend object brace.
+ s.B = append(s.B, '{')
+
+ for i := 0; i < len(fields); i++ {
+ // Get struct field ptr via offset.
+ s.P = add(ptr, fields[i].offset)
+
+ // Append field name and value separator.
+ s.B = append(s.B, fields[i].name+"="...)
+
+ // Format i'th field.
+ fields[i].format(s)
+ s.B = append(s.B, ',', ' ')
+ }
+
+ // Drop final ", ".
+ s.B = s.B[:len(s.B)-2]
+
+ // Append object brace.
+ s.B = append(s.B, '}')
+ }
+}
diff --git a/vendor/codeberg.org/gruf/go-kv/v2/format/type.go b/vendor/codeberg.org/gruf/go-kv/v2/format/type.go
new file mode 100644
index 000000000..ec5557b05
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-kv/v2/format/type.go
@@ -0,0 +1,150 @@
+package format
+
+import (
+ "reflect"
+ "strings"
+)
+
+// typenode ...
+type typenode struct {
+ typeinfo
+ parent *typenode
+}
+
+// typeinfo ...
+type typeinfo struct {
+ rtype reflect.Type
+ flags reflect_flag
+}
+
+// new_typenode returns a new typenode{} with reflect.Type and flags.
+func new_typenode(t reflect.Type, flags reflect_flag) typenode {
+ return typenode{typeinfo: typeinfo{
+ rtype: t,
+ flags: flags,
+ }}
+}
+
+// key returns data (i.e. type value info)
+// to store a FormatFunc under in a cache.
+func (n typenode) key() typeinfo {
+ return n.typeinfo
+}
+
+// indirect returns whether reflect_flagIndir is set for given type flags.
+func (n typenode) indirect() bool {
+ return n.flags&reflect_flagIndir != 0
+}
+
+// iface_indir returns the result of abi.Type{}.IfaceIndir() for underlying type.
+func (n typenode) iface_indir() bool {
+ return abi_Type_IfaceIndir(n.rtype)
+}
+
+// next ...
+func (n typenode) next(t reflect.Type, flags reflect_flag) typenode {
+ child := new_typenode(t, flags)
+ child.parent = &n
+ return child
+}
+
+// visit ...
+func (n typenode) visit() bool {
+ t := n.rtype
+
+ // Check if type is already encountered further up tree.
+ for node := n.parent; node != nil; node = node.parent {
+ if node.rtype == t {
+ return false
+ }
+ }
+
+ return true
+}
+
+// needs_typestr returns whether the type contained in the
+// receiving typenode{} needs type string information prefixed
+// when the TypeMask argument flag bit is set. Certain types
+// don't need this as the parent type already indicates this.
+func (n typenode) needs_typestr() bool {
+ if n.parent == nil {
+ return true
+ }
+ switch p := n.parent.rtype; p.Kind() {
+ case reflect.Pointer:
+ return n.parent.needs_typestr()
+ case reflect.Slice,
+ reflect.Array,
+ reflect.Map:
+ return false
+ default:
+ return true
+ }
+}
+
+// typestr_with_ptrs returns the type string for
+// current typenode{} with asterisks for pointers.
+func (n typenode) typestr_with_ptrs() string {
+ t := n.rtype
+
+ // Check for parent.
+ if n.parent == nil {
+ return t.String()
+ }
+
+ // Get parent type.
+ p := n.parent.rtype
+
+ // If parent is not ptr, then
+ // this was not a deref'd ptr.
+ if p.Kind() != reflect.Pointer {
+ return t.String()
+ }
+
+ // Return un-deref'd
+ // ptr (parent) type.
+ return p.String()
+}
+
+// typestr_with_refs returns the type string for
+// current typenode{} with ampersands for pointers.
+func (n typenode) typestr_with_refs() string {
+ t := n.rtype
+
+ // Check for parent.
+ if n.parent == nil {
+ return t.String()
+ }
+
+ // Get parent type.
+ p := n.parent.rtype
+
+ var d int
+
+ // Count number of dereferences.
+ for p.Kind() == reflect.Pointer {
+ p = p.Elem()
+ d++
+ }
+
+ if d <= 0 {
+ // Prefer just returning our
+ // own string if possible, to
+ // reduce number of strings
+ // we need to allocate.
+ return t.String()
+ }
+
+ // Value type str.
+ str := t.String()
+
+ // Return with type ptrs
+ // symbolized by 'refs'.
+ var buf strings.Builder
+ buf.Grow(len(str) + d)
+ for i := 0; i < d; i++ {
+ buf.WriteByte('&')
+ }
+ buf.WriteString(str)
+ return buf.String()
+}