diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-kv/format')
-rw-r--r-- | vendor/codeberg.org/gruf/go-kv/format/README.md | 5 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-kv/format/benchmark.png | bin | 0 -> 36505 bytes | |||
-rw-r--r-- | vendor/codeberg.org/gruf/go-kv/format/format.go | 922 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-kv/format/formatter.go | 349 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-kv/format/util.go | 56 |
5 files changed, 1332 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-kv/format/README.md b/vendor/codeberg.org/gruf/go-kv/format/README.md new file mode 100644 index 000000000..fe7b2338d --- /dev/null +++ b/vendor/codeberg.org/gruf/go-kv/format/README.md @@ -0,0 +1,5 @@ +String formatting package using Rust-style formatting directives, geared specifcally towards formatting arguments for key-value log output. Provides ONLY default, key, value and verbose formatting directives. For most formatting cases you will be best served by `"fmt"`. + +Generally more visually friendly than `"fmt"` and performance is much improved. + +
\ No newline at end of file diff --git a/vendor/codeberg.org/gruf/go-kv/format/benchmark.png b/vendor/codeberg.org/gruf/go-kv/format/benchmark.png Binary files differnew file mode 100644 index 000000000..7151fbc55 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-kv/format/benchmark.png diff --git a/vendor/codeberg.org/gruf/go-kv/format/format.go b/vendor/codeberg.org/gruf/go-kv/format/format.go new file mode 100644 index 000000000..df5a94b7c --- /dev/null +++ b/vendor/codeberg.org/gruf/go-kv/format/format.go @@ -0,0 +1,922 @@ +package format + +import ( + "reflect" + "strconv" + "unicode/utf8" + + "codeberg.org/gruf/go-byteutil" +) + +const ( + // Flag bit constants, note they are prioritised in this order. + IsKeyBit = uint8(1) << 0 // set to indicate key formatting + VboseBit = uint8(1) << 1 // set to indicate verbose formatting + IsValBit = uint8(1) << 2 // set to indicate value formatting + PanicBit = uint8(1) << 3 // set after panic to prevent recursion +) + +// format provides formatting of values into a Buffer. +type format struct { + // Flags are the currently set value flags. + Flags uint8 + + // Derefs is the current value dereference count. + Derefs uint8 + + // CurDepth is the current Format iterator depth. + CurDepth uint8 + + // VType is the current value type. + VType string + + // Config is the set Formatter config (MUST NOT be nil). + Config *Formatter + + // Buffer is the currently set output buffer. + Buffer *byteutil.Buffer +} + +// AtMaxDepth returns whether format is currently at max depth. +func (f format) AtMaxDepth() bool { + return f.CurDepth > f.Config.MaxDepth +} + +// Key returns whether the isKey flag is set. +func (f format) Key() bool { + return (f.Flags & IsKeyBit) != 0 +} + +// Value returns whether the isVal flag is set. +func (f format) Value() bool { + return (f.Flags & IsValBit) != 0 +} + +// Verbose returns whether the verbose flag is set. +func (f format) Verbose() bool { + return (f.Flags & VboseBit) != 0 +} + +// Panic returns whether the panic flag is set. +func (f format) Panic() bool { + return (f.Flags & PanicBit) != 0 +} + +// SetKey returns format instance with the IsKey bit set to true, +// note this resets the dereference count. +func (f format) SetKey() format { + flags := f.Flags | IsKeyBit + flags &= ^IsValBit + return format{ + Flags: flags, + CurDepth: f.CurDepth, + Config: f.Config, + Buffer: f.Buffer, + } +} + +// SetValue returns format instance with the IsVal bit set to true, +// note this resets the dereference count. +func (f format) SetValue() format { + flags := f.Flags | IsValBit + flags &= ^IsKeyBit + return format{ + Flags: flags, + CurDepth: f.CurDepth, + Config: f.Config, + Buffer: f.Buffer, + } +} + +// SetVerbose returns format instance with the Vbose bit set to true, +// note this resets the dereference count. +func (f format) SetVerbose() format { + return format{ + Flags: f.Flags | VboseBit, + CurDepth: f.CurDepth, + Config: f.Config, + Buffer: f.Buffer, + } +} + +// SetPanic returns format instance with the panic bit set to true, +// note this resets the dereference count and sets IsVal (unsetting IsKey) bit. +func (f format) SetPanic() format { + flags := f.Flags | PanicBit + flags |= IsValBit + flags &= ^IsKeyBit + return format{ + Flags: flags, + CurDepth: f.CurDepth, + Config: f.Config, + Buffer: f.Buffer, + } +} + +// IncrDepth returns format instance with depth incremented and derefs reset. +func (f format) IncrDepth() format { + return format{ + Flags: f.Flags, + Derefs: f.Derefs, + CurDepth: f.CurDepth + 1, + Config: f.Config, + Buffer: f.Buffer, + } +} + +// IncrDerefs returns format instance with dereference count incremented. +func (f format) IncrDerefs() format { + return format{ + Flags: f.Flags, + Derefs: f.Derefs + 1, + CurDepth: f.CurDepth, + Config: f.Config, + Buffer: f.Buffer, + } +} + +func (f format) AppendType() { + const derefs = `********************************` + + `********************************` + + `********************************` + + `********************************` + + `********************************` + + `********************************` + + `********************************` + + `********************************` + f.Buffer.B = append(f.Buffer.B, derefs[:f.Derefs]...) + f.Buffer.B = append(f.Buffer.B, f.VType...) +} + +func (f format) AppendNil() { + if !f.Verbose() { + f.Buffer.B = append(f.Buffer.B, `nil`...) + return + } + + // Append nil with type + f.Buffer.B = append(f.Buffer.B, '(') + f.AppendType() + f.Buffer.B = append(f.Buffer.B, `)(nil`...) + f.Buffer.B = append(f.Buffer.B, ')') +} + +func (f format) AppendByte(b byte) { + switch { + // Always quoted + case f.Key(): + f.Buffer.B = append(f.Buffer.B, '\'') + f.Buffer.B = append(f.Buffer.B, byte2str(b)...) + f.Buffer.B = append(f.Buffer.B, '\'') + + // Always quoted ASCII with type + case f.Verbose(): + f._AppendPrimitiveTyped(func(f format) { + f.Buffer.B = append(f.Buffer.B, '\'') + f.Buffer.B = append(f.Buffer.B, byte2str(b)...) + f.Buffer.B = append(f.Buffer.B, '\'') + }) + + // Always quoted + case f.Value(): + f.Buffer.B = append(f.Buffer.B, '\'') + f.Buffer.B = append(f.Buffer.B, byte2str(b)...) + f.Buffer.B = append(f.Buffer.B, '\'') + + // Append as raw byte + default: + f.Buffer.B = append(f.Buffer.B, b) + } +} + +func (f format) AppendBytes(b []byte) { + switch { + // Bytes CAN be nil formatted + case b == nil: + f.AppendNil() + + // Handle bytes as string key + case f.Key(): + f.AppendStringKey(b2s(b)) + + // Append as separate ASCII quoted bytes in slice + case f.Verbose(): + f._AppendArrayTyped(func(f format) { + for i := 0; i < len(b); i++ { + f.Buffer.B = append(f.Buffer.B, '\'') + f.Buffer.B = append(f.Buffer.B, byte2str(b[i])...) + f.Buffer.B = append(f.Buffer.B, `',`...) + } + if len(b) > 0 { + f.Buffer.Truncate(1) + } + }) + + // Append as quoted string + case f.Value(): + f.AppendStringQuoted(b2s(b)) + + // Append as raw bytes + default: + f.Buffer.B = append(f.Buffer.B, b...) + } +} + +func (f format) AppendRune(r rune) { + switch { + // Quoted only if spaces/requires escaping + case f.Key(): + f.AppendRuneKey(r) + + // Always quoted ASCII with type + case f.Verbose(): + f._AppendPrimitiveTyped(func(f format) { + f.Buffer.B = strconv.AppendQuoteRuneToASCII(f.Buffer.B, r) + }) + + // Always quoted value + case f.Value(): + f.Buffer.B = strconv.AppendQuoteRune(f.Buffer.B, r) + + // Append as raw rune + default: + f.Buffer.WriteRune(r) + } +} + +func (f format) AppendRuneKey(r rune) { + if utf8.RuneLen(r) > 1 && (r < ' ' && r != '\t') || r == '`' || r == '\u007F' { + // Quote and escape this rune + f.Buffer.B = strconv.AppendQuoteRuneToASCII(f.Buffer.B, r) + } else { + // Simply append rune + f.Buffer.WriteRune(r) + } +} + +func (f format) AppendRunes(r []rune) { + switch { + // Runes CAN be nil formatted + case r == nil: + f.AppendNil() + + // Handle bytes as string key + case f.Key(): + f.AppendStringKey(string(r)) + + // Append as separate ASCII quoted bytes in slice + case f.Verbose(): + f._AppendArrayTyped(func(f format) { + for i := 0; i < len(r); i++ { + f.Buffer.B = strconv.AppendQuoteRuneToASCII(f.Buffer.B, r[i]) + f.Buffer.B = append(f.Buffer.B, ',') + } + if len(r) > 0 { + f.Buffer.Truncate(1) + } + }) + + // Append as quoted string + case f.Value(): + f.AppendStringQuoted(string(r)) + + // Append as raw bytes + default: + for i := 0; i < len(r); i++ { + f.Buffer.WriteRune(r[i]) + } + } +} + +func (f format) AppendString(s string) { + switch { + // Quoted only if spaces/requires escaping + case f.Key(): + f.AppendStringKey(s) + + // Always quoted with type + case f.Verbose(): + f._AppendPrimitiveTyped(func(f format) { + f.AppendStringQuoted(s) + }) + + // Always quoted string + case f.Value(): + f.AppendStringQuoted(s) + + // All else + default: + f.Buffer.B = append(f.Buffer.B, s...) + } +} + +func (f format) AppendStringKey(s string) { + if !strconv.CanBackquote(s) { + // Requires quoting AND escaping + f.Buffer.B = strconv.AppendQuote(f.Buffer.B, s) + } else if ContainsDoubleQuote(s) { + // Contains double quotes, needs escaping + f.Buffer.B = AppendEscape(f.Buffer.B, s) + } else if len(s) < 1 || ContainsSpaceOrTab(s) { + // Contains space, needs quotes + f.Buffer.B = append(f.Buffer.B, '"') + f.Buffer.B = append(f.Buffer.B, s...) + f.Buffer.B = append(f.Buffer.B, '"') + } else { + // All else write as-is + f.Buffer.B = append(f.Buffer.B, s...) + } +} + +func (f format) AppendStringQuoted(s string) { + if !strconv.CanBackquote(s) { + // Requires quoting AND escaping + f.Buffer.B = strconv.AppendQuote(f.Buffer.B, s) + } else if ContainsDoubleQuote(s) { + // Contains double quotes, needs escaping + f.Buffer.B = append(f.Buffer.B, '"') + f.Buffer.B = AppendEscape(f.Buffer.B, s) + f.Buffer.B = append(f.Buffer.B, '"') + } else { + // Simply append with quotes + f.Buffer.B = append(f.Buffer.B, '"') + f.Buffer.B = append(f.Buffer.B, s...) + f.Buffer.B = append(f.Buffer.B, '"') + } +} + +func (f format) AppendBool(b bool) { + if f.Verbose() { + // Append as bool with type information + f._AppendPrimitiveTyped(func(f format) { + f.Buffer.B = strconv.AppendBool(f.Buffer.B, b) + }) + } else { + // Simply append as bool + f.Buffer.B = strconv.AppendBool(f.Buffer.B, b) + } +} + +func (f format) AppendInt(i int64) { + f._AppendPrimitiveType(func(f format) { + f.Buffer.B = strconv.AppendInt(f.Buffer.B, i, 10) + }) +} + +func (f format) AppendUint(u uint64) { + f._AppendPrimitiveType(func(f format) { + f.Buffer.B = strconv.AppendUint(f.Buffer.B, u, 10) + }) +} + +func (f format) AppendFloat(l float64) { + f._AppendPrimitiveType(func(f format) { + f.AppendFloatValue(l) + }) +} + +func (f format) AppendFloatValue(l float64) { + f.Buffer.B = strconv.AppendFloat(f.Buffer.B, l, 'f', -1, 64) +} + +func (f format) AppendComplex(c complex128) { + f._AppendPrimitiveType(func(f format) { + f.AppendFloatValue(real(c)) + f.Buffer.B = append(f.Buffer.B, '+') + f.AppendFloatValue(imag(c)) + f.Buffer.B = append(f.Buffer.B, 'i') + }) +} + +func (f format) AppendPtr(u uint64) { + f._AppendPtrType(func(f format) { + if u == 0 { + // Append as nil + f.Buffer.B = append(f.Buffer.B, `nil`...) + } else { + // Append as hex number + f.Buffer.B = append(f.Buffer.B, `0x`...) + f.Buffer.B = strconv.AppendUint(f.Buffer.B, u, 16) + } + }) +} + +func (f format) AppendInterfaceOrReflect(i interface{}) { + if !f.AppendInterface(i) { + // Interface append failed, used reflected value + type + f.AppendReflectValue(reflect.ValueOf(i), reflect.TypeOf(i)) + } +} + +func (f format) AppendInterfaceOrReflectNext(v reflect.Value, t reflect.Type) { + // Check we haven't hit max + if f.AtMaxDepth() { + f.Buffer.B = append(f.Buffer.B, `...`...) + return + } + + // Incr the depth + f = f.IncrDepth() + + // Make actual call + f.AppendReflectOrInterface(v, t) +} + +func (f format) AppendReflectOrInterface(v reflect.Value, t reflect.Type) { + if !v.CanInterface() || + !f.AppendInterface(v.Interface()) { + // Interface append failed, use reflect + f.AppendReflectValue(v, t) + } +} + +func (f format) AppendInterface(i interface{}) bool { + switch i := i.(type) { + // Reflect types + case reflect.Type: + f.AppendReflectType(i) + case reflect.Value: + f.Buffer.B = append(f.Buffer.B, `reflect.Value`...) + f.Buffer.B = append(f.Buffer.B, '(') + f.Buffer.B = append(f.Buffer.B, i.String()...) + f.Buffer.B = append(f.Buffer.B, ')') + + // Bytes, runes and string types + case rune: + f.VType = `int32` + f.AppendRune(i) + case []rune: + f.VType = `[]int32` + f.AppendRunes(i) + case byte: + f.VType = `uint8` + f.AppendByte(i) + case []byte: + f.VType = `[]uint8` + f.AppendBytes(i) + case string: + f.VType = `string` + f.AppendString(i) + + // Int types + case int: + f.VType = `int` + f.AppendInt(int64(i)) + case int8: + f.VType = `int8` + f.AppendInt(int64(i)) + case int16: + f.VType = `int16` + f.AppendInt(int64(i)) + case int64: + f.VType = `int64` + f.AppendInt(int64(i)) + + // Uint types + case uint: + f.VType = `uint` + f.AppendUint(uint64(i)) + case uint16: + f.VType = `uint16` + f.AppendUint(uint64(i)) + case uint32: + f.VType = `uint32` + f.AppendUint(uint64(i)) + case uint64: + f.VType = `uint64` + f.AppendUint(uint64(i)) + + // Float types + case float32: + f.VType = `float32` + f.AppendFloat(float64(i)) + case float64: + f.VType = `float64` + f.AppendFloat(float64(i)) + + // Bool type + case bool: + f.VType = `bool` + f.AppendBool(i) + + // Complex types + case complex64: + f.VType = `complex64` + f.AppendComplex(complex128(i)) + case complex128: + f.VType = `complex128` + f.AppendComplex(complex128(i)) + + // Method types + case error: + return f._AppendMethodType(func() string { + return i.Error() + }, i) + case interface{ String() string }: + return f._AppendMethodType(func() string { + return i.String() + }, i) + + // No quick handler + default: + return false + } + + return true +} + +func (f format) AppendReflectType(t reflect.Type) { + switch f.VType = `reflect.Type`; { + case isNil(t) /* safer nil check */ : + f.AppendNil() + case f.Verbose(): + f.AppendType() + f.Buffer.B = append(f.Buffer.B, '(') + f.Buffer.B = append(f.Buffer.B, t.String()...) + f.Buffer.B = append(f.Buffer.B, ')') + default: + f.Buffer.B = append(f.Buffer.B, t.String()...) + } +} + +func (f format) AppendReflectValue(v reflect.Value, t reflect.Type) { + switch v.Kind() { + // String/byte types + case reflect.String: + f.VType = t.String() + f.AppendString(v.String()) + case reflect.Uint8: + f.VType = t.String() + f.AppendByte(byte(v.Uint())) + case reflect.Int32: + f.VType = t.String() + f.AppendRune(rune(v.Int())) + + // Float tpyes + case reflect.Float32, reflect.Float64: + f.VType = t.String() + f.AppendFloat(v.Float()) + + // Int types + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int64: + f.VType = t.String() + f.AppendInt(v.Int()) + + // Uint types + case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: + f.VType = t.String() + f.AppendUint(v.Uint()) + + // Complex types + case reflect.Complex64, reflect.Complex128: + f.VType = t.String() + f.AppendComplex(v.Complex()) + + // Bool type + case reflect.Bool: + f.VType = t.String() + f.AppendBool(v.Bool()) + + // Slice and array types + case reflect.Array: + f.AppendArray(v, t) + case reflect.Slice: + f.AppendSlice(v, t) + + // Map types + case reflect.Map: + f.AppendMap(v, t) + + // Struct types + case reflect.Struct: + f.AppendStruct(v, t) + + // Interface type + case reflect.Interface: + if v.IsNil() { + // Append nil ptr type + f.VType = t.String() + f.AppendNil() + } else { + // Append interface + v = v.Elem() + t = v.Type() + f.AppendReflectOrInterface(v, t) + } + + // Deref'able ptr type + case reflect.Ptr: + if v.IsNil() { + // Append nil ptr type + f.VType = t.String() + f.AppendNil() + } else { + // Deref to next level + f = f.IncrDerefs() + v, t = v.Elem(), t.Elem() + f.AppendReflectOrInterface(v, t) + } + + // 'raw' pointer types + case reflect.UnsafePointer, reflect.Func, reflect.Chan: + f.VType = t.String() + f.AppendPtr(uint64(v.Pointer())) + case reflect.Uintptr: + f.VType = t.String() + f.AppendPtr(v.Uint()) + + // Zero reflect value + case reflect.Invalid: + f.Buffer.B = append(f.Buffer.B, `nil`...) + + // All others + default: + f.VType = t.String() + f.AppendType() + } +} + +func (f format) AppendSlice(v reflect.Value, t reflect.Type) { + // Get slice value type + f.VType = t.String() + + if t.Elem().Kind() == reflect.Uint8 { + // This is a byte slice + f.AppendBytes(v.Bytes()) + return + } + + if v.IsNil() { + // Nil slice + f.AppendNil() + return + } + + if f.Verbose() { + // Append array with type information + f._AppendArrayTyped(func(f format) { + f.AppendArrayElems(v, t) + }) + } else { + // Simply append array as elems + f._AppendArray(func(f format) { + f.AppendArrayElems(v, t) + }) + } +} + +func (f format) AppendArray(v reflect.Value, t reflect.Type) { + // Get array value type + f.VType = t.String() + + if f.Verbose() { + // Append array with type information + f._AppendArrayTyped(func(f format) { + f.AppendArrayElems(v, t) + }) + } else { + // Simply append array as elems + f._AppendArray(func(f format) { + f.AppendArrayElems(v, t) + }) + } +} + +func (f format) AppendArrayElems(v reflect.Value, t reflect.Type) { + // Get no. elems + n := v.Len() + + // Get elem type + et := t.Elem() + + // Append values + for i := 0; i < n; i++ { + f.SetValue().AppendInterfaceOrReflectNext(v.Index(i), et) + f.Buffer.B = append(f.Buffer.B, ',') + } + + // Drop last comma + if n > 0 { + f.Buffer.Truncate(1) + } +} + +func (f format) AppendMap(v reflect.Value, t reflect.Type) { + // Get value type + f.VType = t.String() + + if v.IsNil() { + // Nil map -- no fields + f.AppendNil() + return + } + + // Append field formatted map fields + f._AppendFieldType(func(f format) { + f.AppendMapFields(v, t) + }) +} + +func (f format) AppendMapFields(v reflect.Value, t reflect.Type) { + // Get a map iterator + r := v.MapRange() + n := v.Len() + + // Get key/val types + kt := t.Key() + kv := t.Elem() + + // Iterate pairs + for r.Next() { + f.SetKey().AppendInterfaceOrReflectNext(r.Key(), kt) + f.Buffer.B = append(f.Buffer.B, '=') + f.SetValue().AppendInterfaceOrReflectNext(r.Value(), kv) + f.Buffer.B = append(f.Buffer.B, ' ') + } + + // Drop last space + if n > 0 { + f.Buffer.Truncate(1) + } +} + +func (f format) AppendStruct(v reflect.Value, t reflect.Type) { + // Get value type + f.VType = t.String() + + // Append field formatted struct fields + f._AppendFieldType(func(f format) { + f.AppendStructFields(v, t) + }) +} + +func (f format) AppendStructFields(v reflect.Value, t reflect.Type) { + // Get field no. + n := v.NumField() + + // Iterate struct fields + for i := 0; i < n; i++ { + vfield := v.Field(i) + tfield := t.Field(i) + + // Append field name + f.AppendStringKey(tfield.Name) + f.Buffer.B = append(f.Buffer.B, '=') + f.SetValue().AppendInterfaceOrReflectNext(vfield, tfield.Type) + + // Append separator + f.Buffer.B = append(f.Buffer.B, ' ') + } + + // Drop last space + if n > 0 { + f.Buffer.Truncate(1) + } +} + +func (f format) _AppendMethodType(method func() string, i interface{}) (ok bool) { + // Verbose -- no methods + if f.Verbose() { + return false + } + + // Catch nil type + if isNil(i) { + f.AppendNil() + return true + } + + // Catch any panics + defer func() { + if r := recover(); r != nil { + // DON'T recurse catchPanic() + if f.Panic() { + panic(r) + } + + // Attempt to decode panic into buf + f.Buffer.B = append(f.Buffer.B, `!{PANIC=`...) + f.SetPanic().AppendInterfaceOrReflect(r) + f.Buffer.B = append(f.Buffer.B, '}') + + // Ensure no further attempts + // to format after return + ok = true + } + }() + + // Get method result + result := method() + + switch { + // Append as key formatted + case f.Key(): + f.AppendStringKey(result) + + // Append as always quoted + case f.Value(): + f.AppendStringQuoted(result) + + // Append as-is + default: + f.Buffer.B = append(f.Buffer.B, result...) + } + + return true +} + +// _AppendPrimitiveType is a helper to append prefix/suffix for primitives (numbers/bools/bytes/runes). +func (f format) _AppendPrimitiveType(appendPrimitive func(format)) { + if f.Verbose() { + // Append value with type information + f._AppendPrimitiveTyped(appendPrimitive) + } else { + // Append simply as-is + appendPrimitive(f) + } +} + +// _AppendPrimitiveTyped is a helper to append prefix/suffix for primitives (numbers/bools/bytes/runes) with their types (if deref'd). +func (f format) _AppendPrimitiveTyped(appendPrimitive func(format)) { + if f.Derefs > 0 { + // Is deref'd, append type info + f.Buffer.B = append(f.Buffer.B, '(') + f.AppendType() + f.Buffer.WriteString(`)(`) + appendPrimitive(f) + f.Buffer.B = append(f.Buffer.B, ')') + } else { + // Simply append value + appendPrimitive(f) + } +} + +// _AppendPtrType is a helper to append prefix/suffix for ptr types (with type if necessary). +func (f format) _AppendPtrType(appendPtr func(format)) { + if f.Verbose() { + // Append value with type information + f.Buffer.B = append(f.Buffer.B, '(') + f.AppendType() + f.Buffer.WriteString(`)(`) + appendPtr(f) + f.Buffer.B = append(f.Buffer.B, ')') + } else { + // Append simply as-is + appendPtr(f) + } +} + +// _AppendArray is a helper to append prefix/suffix for array-types. +func (f format) _AppendArray(appendArray func(format)) { + f.Buffer.B = append(f.Buffer.B, '[') + appendArray(f) + f.Buffer.B = append(f.Buffer.B, ']') +} + +// _AppendArrayTyped is a helper to append prefix/suffix for array-types with their types. +func (f format) _AppendArrayTyped(appendArray func(format)) { + f.AppendType() + f.Buffer.B = append(f.Buffer.B, '{') + appendArray(f) + f.Buffer.B = append(f.Buffer.B, '}') +} + +// _AppendFields is a helper to append prefix/suffix for field-types (with type if necessary). +func (f format) _AppendFieldType(appendFields func(format)) { + if f.Verbose() { + f.AppendType() + } + f.Buffer.B = append(f.Buffer.B, '{') + appendFields(f) + f.Buffer.B = append(f.Buffer.B, '}') +} + +// byte2str returns 'c' as a string, escaping if necessary. +func byte2str(c byte) string { + switch c { + case '\a': + return `\a` + case '\b': + return `\b` + case '\f': + return `\f` + case '\n': + return `\n` + case '\r': + return `\r` + case '\t': + return `\t` + case '\v': + return `\v` + case '\'': + return `\\` + default: + if c < ' ' { + const hex = "0123456789abcdef" + return `\x` + + string(hex[c>>4]) + + string(hex[c&0xF]) + } + return string(c) + } +} diff --git a/vendor/codeberg.org/gruf/go-kv/format/formatter.go b/vendor/codeberg.org/gruf/go-kv/format/formatter.go new file mode 100644 index 000000000..fd8cf98df --- /dev/null +++ b/vendor/codeberg.org/gruf/go-kv/format/formatter.go @@ -0,0 +1,349 @@ +package format + +import ( + "strconv" + "strings" + + "codeberg.org/gruf/go-byteutil" +) + +// Formatter allows configuring value and string formatting. +type Formatter struct { + // MaxDepth specifies the max depth of fields the formatter will iterate. + // Once max depth is reached, value will simply be formatted as "...". + // e.g. + // + // MaxDepth=1 + // type A struct{ + // Nested B + // } + // type B struct{ + // Nested C + // } + // type C struct{ + // Field string + // } + // + // Append(&buf, A{}) => {Nested={Nested={Field=...}}} + MaxDepth uint8 +} + +// Append will append formatted form of supplied values into 'buf'. +func (f *Formatter) Append(buf *byteutil.Buffer, v ...interface{}) { + fmt := format{Buffer: buf, Config: f} + for i := 0; i < len(v); i++ { + fmt.AppendInterfaceOrReflect(v[i]) + fmt.Buffer.B = append(fmt.Buffer.B, ' ') + } + if len(v) > 0 { + fmt.Buffer.Truncate(1) + } +} + +// Appendf will append the formatted string with supplied values into 'buf'. +// Supported format directives: +// - '{}' => format supplied arg, in place +// - '{0}' => format arg at index 0 of supplied, in place +// - '{:?}' => format supplied arg verbosely, in place +// - '{:k}' => format supplied arg as key, in place +// - '{:v}' => format supplied arg as value, in place +// +// To escape either of '{}' simply append an additional brace e.g. +// - '{{' => '{' +// - '}}' => '}' +// - '{{}}' => '{}' +// - '{{:?}}' => '{:?}' +// +// More formatting directives might be included in the future. +func (f *Formatter) Appendf(buf *byteutil.Buffer, s string, a ...interface{}) { + const ( + // ground state + modeNone = uint8(0) + + // prev reached '{' + modeOpen = uint8(1) + + // prev reached '}' + modeClose = uint8(2) + + // parsing directive index + modeIdx = uint8(3) + + // parsing directive operands + modeOp = uint8(4) + ) + + var ( + // mode is current parsing mode + mode uint8 + + // arg is the current arg index + arg int + + // carg is current directive-set arg index + carg int + + // last is the trailing cursor to see slice windows + last int + + // idx is the current index in 's' + idx int + + // fmt is the base argument formatter + fmt = format{ + Config: f, + Buffer: buf, + } + + // NOTE: these functions are defined here as function + // locals as it turned out to be better for performance + // doing it this way, than encapsulating their logic in + // some kind of parsing structure. Maybe if the parser + // was pooled along with the buffers it might work out + // better, but then it makes more internal functions i.e. + // .Append() .Appendf() less accessible outside package. + // + // Currently, passing '-gcflags "-l=4"' causes a not + // insignificant decrease in ns/op, which is likely due + // to more aggressive function inlining, which this + // function can obviously stand to benefit from :) + + // Str returns current string window slice, and updates + // the trailing cursor 'last' to current 'idx' + Str = func() string { + str := s[last:idx] + last = idx + return str + } + + // MoveUp moves the trailing cursor 'last' just past 'idx' + MoveUp = func() { + last = idx + 1 + } + + // MoveUpTo moves the trailing cursor 'last' either up to + // closest '}', or current 'idx', whichever is furthest. + // NOTE: by calling bytealg.IndexByteString() directly (and + // not the strconv pass-through, we shave-off complexity + // which allows this function to be inlined). + MoveUpTo = func() { + i := strings.IndexByte(s[idx:], '}') + if i >= 0 { + idx += i + } + MoveUp() + } + + // ParseIndex parses an integer from the current string + // window, updating 'last' to 'idx'. The string window + // is ASSUMED to contain only valid ASCII numbers. This + // only returns false if number exceeds platform int size + ParseIndex = func() bool { + var str string + + // Get current window + if str = Str(); len(str) < 1 { + return true + } + + // Index HAS to fit within platform integer size + if !(strconv.IntSize == 32 && (0 < len(s) && len(s) < 10)) || + !(strconv.IntSize == 64 && (0 < len(s) && len(s) < 19)) { + return false + } + + carg = 0 + + // Build integer from string + for i := 0; i < len(str); i++ { + carg = carg*10 + int(str[i]-'0') + } + + return true + } + + // ValidOp checks that for current ending idx, that a valid + // operand was achieved -- only 0, 1 are valid numbers. + ValidOp = func() bool { + diff := (idx - last) + last = idx + return diff < 2 + } + + // AppendArg will take either the directive-set, or + // iterated arg index, check within bounds of 'a' and + // append the that argument formatted to the buffer. + // On failure, it will append an error string + AppendArg = func() { + // Look for idx + if carg < 0 { + carg = arg + } + + // Incr idx + arg++ + + if carg < len(a) { + // Append formatted argument value + fmt.AppendInterfaceOrReflect(a[carg]) + } else { + // No argument found for index + fmt.Buffer.B = append(fmt.Buffer.B, `!{MISSING_ARG}`...) + } + } + + // Reset will reset the mode to ground, the flags + // to empty and parsed 'carg' to empty + Reset = func() { + mode = modeNone + fmt.CurDepth = 0 + fmt.Flags = 0 + fmt.VType = "" + fmt.Derefs = 0 + carg = -1 + } + ) + + for idx = 0; idx < len(s); idx++ { + // Get next char + c := s[idx] + + switch mode { + // Ground mode + case modeNone: + switch c { + case '{': + // Enter open mode + fmt.Buffer.B = append(fmt.Buffer.B, Str()...) + mode = modeOpen + MoveUp() + case '}': + // Enter close mode + fmt.Buffer.B = append(fmt.Buffer.B, Str()...) + mode = modeClose + MoveUp() + } + + // Encountered open '{' + case modeOpen: + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + // Starting index + mode = modeIdx + MoveUp() + case '{': + // Escaped bracket + fmt.Buffer.B = append(fmt.Buffer.B, '{') + mode = modeNone + MoveUp() + case '}': + // Format arg + AppendArg() + Reset() + MoveUp() + case ':': + // Starting operands + mode = modeOp + MoveUp() + default: + // Bad char, missing a close + fmt.Buffer.B = append(fmt.Buffer.B, `!{MISSING_CLOSE}`...) + mode = modeNone + MoveUpTo() + } + + // Encountered close '}' + case modeClose: + switch c { + case '}': + // Escaped close bracket + fmt.Buffer.B = append(fmt.Buffer.B, '}') + mode = modeNone + MoveUp() + default: + // Missing an open bracket + fmt.Buffer.B = append(fmt.Buffer.B, `!{MISSING_OPEN}`...) + mode = modeNone + MoveUp() + } + + // Preparing index + case modeIdx: + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + case ':': + if !ParseIndex() { + // Unable to parse an integer + fmt.Buffer.B = append(fmt.Buffer.B, `!{BAD_INDEX}`...) + mode = modeNone + MoveUpTo() + } else { + // Starting operands + mode = modeOp + MoveUp() + } + case '}': + if !ParseIndex() { + // Unable to parse an integer + fmt.Buffer.B = append(fmt.Buffer.B, `!{BAD_INDEX}`...) + } else { + // Format arg + AppendArg() + } + Reset() + MoveUp() + default: + // Not a valid index character + fmt.Buffer.B = append(fmt.Buffer.B, `!{BAD_INDEX}`...) + mode = modeNone + MoveUpTo() + } + + // Preparing operands + case modeOp: + switch c { + case 'k': + fmt.Flags |= IsKeyBit + case 'v': + fmt.Flags |= IsValBit + case '?': + fmt.Flags |= VboseBit + case '}': + if !ValidOp() { + // Bad operands parsed + fmt.Buffer.B = append(fmt.Buffer.B, `!{BAD_OPERAND}`...) + } else { + // Format arg + AppendArg() + } + Reset() + MoveUp() + default: + // Not a valid operand char + fmt.Buffer.B = append(fmt.Buffer.B, `!{BAD_OPERAND}`...) + Reset() + MoveUpTo() + } + } + } + + // Append any remaining + fmt.Buffer.B = append(fmt.Buffer.B, s[last:]...) +} + +// formatter is the default formatter instance. +var formatter = Formatter{ + MaxDepth: 10, +} + +// Append will append formatted form of supplied values into 'buf' using default formatter. +// See Formatter.Append() for more documentation. +func Append(buf *byteutil.Buffer, v ...interface{}) { + formatter.Append(buf, v...) +} + +// Appendf will append the formatted string with supplied values into 'buf' using default formatter. +// See Formatter.Appendf() for more documentation. +func Appendf(buf *byteutil.Buffer, s string, a ...interface{}) { + formatter.Appendf(buf, s, a...) +} diff --git a/vendor/codeberg.org/gruf/go-kv/format/util.go b/vendor/codeberg.org/gruf/go-kv/format/util.go new file mode 100644 index 000000000..c4c42329e --- /dev/null +++ b/vendor/codeberg.org/gruf/go-kv/format/util.go @@ -0,0 +1,56 @@ +package format + +import ( + "strings" + "unsafe" +) + +// ContainsSpaceOrTab checks if "s" contains space or tabs. +func ContainsSpaceOrTab(s string) bool { + if i := strings.IndexByte(s, ' '); i != -1 { + return true // note using indexbyte as it is ASM. + } else if i := strings.IndexByte(s, '\t'); i != -1 { + return true + } + return false +} + +// ContainsDoubleQuote checks if "s" contains a double quote. +func ContainsDoubleQuote(s string) bool { + return (strings.IndexByte(s, '"') != -1) +} + +// AppendEscape will append 's' to 'dst' and escape any double quotes. +func AppendEscape(dst []byte, str string) []byte { + var delim bool + for i := range str { + if str[i] == '\\' && !delim { + // Set delim flag + delim = true + continue + } else if str[i] == '"' && !delim { + // Append escaped double quote + dst = append(dst, `\"`...) + continue + } else if delim { + // Append skipped slash + dst = append(dst, `\`...) + delim = false + } + + // Append char as-is + dst = append(dst, str[i]) + } + return dst +} + +// isNil will safely check if 'v' is nil without dealing with weird Go interface nil bullshit. +func isNil(i interface{}) bool { + type eface struct{ _type, data unsafe.Pointer } //nolint + return (*(*eface)(unsafe.Pointer(&i))).data == nil //nolint +} + +// b2s converts a byteslice to string without allocation. +func b2s(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} |