diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-kv/v2/format')
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/abi.go | 262 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/args.go | 247 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/array.go | 231 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/format.go | 614 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/formatting.go | 161 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/map.go | 130 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/methods.go | 153 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/pointer.go | 128 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/slice.go | 158 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/struct.go | 173 | ||||
| -rw-r--r-- | vendor/codeberg.org/gruf/go-kv/v2/format/type.go | 150 |
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() +} |
