summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-format
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/codeberg.org/gruf/go-format')
-rw-r--r--vendor/codeberg.org/gruf/go-format/LICENSE9
-rw-r--r--vendor/codeberg.org/gruf/go-format/README.md16
-rw-r--r--vendor/codeberg.org/gruf/go-format/buffer.go81
-rw-r--r--vendor/codeberg.org/gruf/go-format/format.go565
-rw-r--r--vendor/codeberg.org/gruf/go-format/formatter.go352
-rw-r--r--vendor/codeberg.org/gruf/go-format/print.go88
-rw-r--r--vendor/codeberg.org/gruf/go-format/util.go13
7 files changed, 1124 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-format/LICENSE b/vendor/codeberg.org/gruf/go-format/LICENSE
new file mode 100644
index 000000000..b7c4417ac
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/LICENSE
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2021 gruf
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/codeberg.org/gruf/go-format/README.md b/vendor/codeberg.org/gruf/go-format/README.md
new file mode 100644
index 000000000..7126e215e
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/README.md
@@ -0,0 +1,16 @@
+# go-format
+
+String formatting package using Rust-style formatting directives.
+
+Output is generally more visually-friendly than `"fmt"`, while performance is neck-and-neck.
+
+README is WIP.
+
+## todos
+
+- improved verbose printing of number types
+
+- more test cases
+
+- improved verbose printing of string ptr types
+
diff --git a/vendor/codeberg.org/gruf/go-format/buffer.go b/vendor/codeberg.org/gruf/go-format/buffer.go
new file mode 100644
index 000000000..393f2fcd3
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/buffer.go
@@ -0,0 +1,81 @@
+package format
+
+import (
+ "io"
+ "unicode/utf8"
+ "unsafe"
+)
+
+// ensure we conform to io.Writer.
+var _ io.Writer = (*Buffer)(nil)
+
+// Buffer is a simple wrapper around a byte slice.
+type Buffer struct {
+ B []byte
+}
+
+// Write will append given byte slice to buffer, fulfilling io.Writer.
+func (buf *Buffer) Write(b []byte) (int, error) {
+ buf.B = append(buf.B, b...)
+ return len(b), nil
+}
+
+// AppendByte appends given byte to the buffer.
+func (buf *Buffer) AppendByte(b byte) {
+ buf.B = append(buf.B, b)
+}
+
+// AppendRune appends given rune to the buffer.
+func (buf *Buffer) AppendRune(r rune) {
+ if r < utf8.RuneSelf {
+ buf.B = append(buf.B, byte(r))
+ return
+ }
+
+ l := buf.Len()
+ for i := 0; i < utf8.UTFMax; i++ {
+ buf.B = append(buf.B, 0)
+ }
+ n := utf8.EncodeRune(buf.B[l:buf.Len()], r)
+ buf.B = buf.B[:l+n]
+}
+
+// Append will append given byte slice to the buffer.
+func (buf *Buffer) Append(b []byte) {
+ buf.B = append(buf.B, b...)
+}
+
+// AppendString appends given string to the buffer.
+func (buf *Buffer) AppendString(s string) {
+ buf.B = append(buf.B, s...)
+}
+
+// Len returns the length of the buffer's underlying byte slice.
+func (buf *Buffer) Len() int {
+ return len(buf.B)
+}
+
+// Cap returns the capacity of the buffer's underlying byte slice.
+func (buf *Buffer) Cap() int {
+ return cap(buf.B)
+}
+
+// Truncate will reduce the length of the buffer by 'n'.
+func (buf *Buffer) Truncate(n int) {
+ if n > len(buf.B) {
+ n = len(buf.B)
+ }
+ buf.B = buf.B[:buf.Len()-n]
+}
+
+// Reset will reset the buffer length to 0 (retains capacity).
+func (buf *Buffer) Reset() {
+ buf.B = buf.B[:0]
+}
+
+// String returns the underlying byte slice as a string. Please note
+// this value is tied directly to the underlying byte slice, if you
+// write to the buffer then returned string values will also change.
+func (buf *Buffer) String() string {
+ return *(*string)(unsafe.Pointer(&buf.B))
+}
diff --git a/vendor/codeberg.org/gruf/go-format/format.go b/vendor/codeberg.org/gruf/go-format/format.go
new file mode 100644
index 000000000..856fe890e
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/format.go
@@ -0,0 +1,565 @@
+package format
+
+import (
+ "reflect"
+ "strconv"
+ "unsafe"
+)
+
+// Formattable defines a type capable of being formatted and appended to a byte buffer.
+type Formattable interface {
+ AppendFormat([]byte) []byte
+}
+
+// format is the object passed among the append___ formatting functions.
+type format struct {
+ flags uint8 // 'isKey' and 'verbose' flags
+ drefs uint8 // current value deref count
+ curd uint8 // current depth
+ maxd uint8 // maximum depth
+ buf *Buffer // out buffer
+}
+
+const (
+ // flag bit constants.
+ isKeyBit = uint8(1) << 0
+ isValBit = uint8(1) << 1
+ vboseBit = uint8(1) << 2
+ panicBit = uint8(1) << 3
+)
+
+// AtMaxDepth returns whether format is currently at max depth.
+func (f format) AtMaxDepth() bool {
+ return f.curd > f.maxd
+}
+
+// Derefs returns no. times current value has been dereferenced.
+func (f format) Derefs() uint8 {
+ return f.drefs
+}
+
+// IsKey returns whether the isKey flag is set.
+func (f format) IsKey() bool {
+ return (f.flags & isKeyBit) != 0
+}
+
+// IsValue returns whether the isVal flag is set.
+func (f format) IsValue() 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
+}
+
+// SetIsKey returns format instance with the isKey bit set to value.
+func (f format) SetIsKey() format {
+ return format{
+ flags: f.flags & ^isValBit | isKeyBit,
+ curd: f.curd,
+ maxd: f.maxd,
+ buf: f.buf,
+ }
+}
+
+// SetIsValue returns format instance with the isVal bit set to value.
+func (f format) SetIsValue() format {
+ return format{
+ flags: f.flags & ^isKeyBit | isValBit,
+ curd: f.curd,
+ maxd: f.maxd,
+ buf: f.buf,
+ }
+}
+
+// SetPanic returns format instance with the panic bit set to value.
+func (f format) SetPanic() format {
+ return format{
+ flags: f.flags | panicBit /* handle panic as value */ | isValBit & ^isKeyBit,
+ curd: f.curd,
+ maxd: f.maxd,
+ buf: f.buf,
+ }
+}
+
+// IncrDepth returns format instance with depth incremented.
+func (f format) IncrDepth() format {
+ return format{
+ flags: f.flags,
+ curd: f.curd + 1,
+ maxd: f.maxd,
+ buf: f.buf,
+ }
+}
+
+// IncrDerefs returns format instance with dereference count incremented.
+func (f format) IncrDerefs() format {
+ return format{
+ flags: f.flags,
+ drefs: f.drefs + 1,
+ curd: f.curd,
+ maxd: f.maxd,
+ buf: f.buf,
+ }
+}
+
+// appendType appends a type using supplied type str.
+func appendType(fmt format, t string) {
+ for i := uint8(0); i < fmt.Derefs(); i++ {
+ fmt.buf.AppendByte('*')
+ }
+ fmt.buf.AppendString(t)
+}
+
+// appendNilType Appends nil to buf, type included if verbose.
+func appendNilType(fmt format, t string) {
+ if fmt.Verbose() {
+ fmt.buf.AppendByte('(')
+ appendType(fmt, t)
+ fmt.buf.AppendString(`)(nil)`)
+ } else {
+ fmt.buf.AppendString(`nil`)
+ }
+}
+
+// appendByte Appends a single byte to buf.
+func appendByte(fmt format, b byte) {
+ if fmt.IsValue() || fmt.Verbose() {
+ fmt.buf.AppendString(`'` + string(b) + `'`)
+ } else {
+ fmt.buf.AppendByte(b)
+ }
+}
+
+// appendBytes Appends a quoted byte slice to buf.
+func appendBytes(fmt format, b []byte) {
+ if b == nil {
+ // Bytes CAN be nil formatted
+ appendNilType(fmt, `[]byte`)
+ } else {
+ // Append bytes as slice
+ fmt.buf.AppendByte('[')
+ for _, b := range b {
+ fmt.buf.AppendByte(b)
+ fmt.buf.AppendByte(',')
+ }
+ if len(b) > 0 {
+ fmt.buf.Truncate(1)
+ }
+ fmt.buf.AppendByte(']')
+ }
+}
+
+// appendString Appends an escaped, double-quoted string to buf.
+func appendString(fmt format, s string) {
+ switch {
+ // Key in a key-value pair
+ case fmt.IsKey():
+ if !strconv.CanBackquote(s) {
+ // Requires quoting AND escaping
+ fmt.buf.B = strconv.AppendQuote(fmt.buf.B, s)
+ } else if containsSpaceOrTab(s) {
+ // Contains space, needs quotes
+ fmt.buf.AppendString(`"` + s + `"`)
+ } else {
+ // All else write as-is
+ fmt.buf.AppendString(s)
+ }
+
+ // Value in a key-value pair (always escape+quote)
+ case fmt.IsValue():
+ fmt.buf.B = strconv.AppendQuote(fmt.buf.B, s)
+
+ // Verbose but neither key nor value (always quote)
+ case fmt.Verbose():
+ fmt.buf.AppendString(`"` + s + `"`)
+
+ // All else
+ default:
+ fmt.buf.AppendString(s)
+ }
+}
+
+// appendBool Appends a formatted bool to buf.
+func appendBool(fmt format, b bool) {
+ fmt.buf.B = strconv.AppendBool(fmt.buf.B, b)
+}
+
+// appendInt Appends a formatted int to buf.
+func appendInt(fmt format, i int64) {
+ fmt.buf.B = strconv.AppendInt(fmt.buf.B, i, 10)
+}
+
+// appendUint Appends a formatted uint to buf.
+func appendUint(fmt format, u uint64) {
+ fmt.buf.B = strconv.AppendUint(fmt.buf.B, u, 10)
+}
+
+// appendFloat Appends a formatted float to buf.
+func appendFloat(fmt format, f float64) {
+ fmt.buf.B = strconv.AppendFloat(fmt.buf.B, f, 'G', -1, 64)
+}
+
+// appendComplex Appends a formatted complex128 to buf.
+func appendComplex(fmt format, c complex128) {
+ appendFloat(fmt, real(c))
+ fmt.buf.AppendByte('+')
+ appendFloat(fmt, imag(c))
+ fmt.buf.AppendByte('i')
+}
+
+// isNil will safely check if 'v' is nil without dealing with weird Go interface nil bullshit.
+func isNil(i interface{}) bool {
+ e := *(*struct {
+ _ unsafe.Pointer // type
+ v unsafe.Pointer // value
+ })(unsafe.Pointer(&i))
+ return (e.v == nil)
+}
+
+// appendIfaceOrReflectValue will attempt to append as interface, falling back to reflection.
+func appendIfaceOrRValue(fmt format, i interface{}) {
+ if !appendIface(fmt, i) {
+ appendRValue(fmt, reflect.ValueOf(i))
+ }
+}
+
+// appendValueNext checks for interface methods before performing appendRValue, checking + incr depth.
+func appendRValueOrIfaceNext(fmt format, v reflect.Value) {
+ // Check we haven't hit max
+ if fmt.AtMaxDepth() {
+ fmt.buf.AppendString("...")
+ return
+ }
+
+ // Incr the depth
+ fmt = fmt.IncrDepth()
+
+ // Make actual call
+ if !v.CanInterface() || !appendIface(fmt, v.Interface()) {
+ appendRValue(fmt, v)
+ }
+}
+
+// appendIface parses and Appends a formatted interface value to buf.
+func appendIface(fmt format, i interface{}) (ok bool) {
+ ok = true // default
+ catchPanic := func() {
+ if r := recover(); r != nil {
+ // DON'T recurse catchPanic()
+ if fmt.Panic() {
+ panic(r)
+ }
+
+ // Attempt to decode panic into buf
+ fmt.buf.AppendString(`!{PANIC=`)
+ appendIfaceOrRValue(fmt.SetPanic(), r)
+ fmt.buf.AppendByte('}')
+
+ // Ensure return
+ ok = true
+ }
+ }
+
+ switch i := i.(type) {
+ // Nil type
+ case nil:
+ fmt.buf.AppendString(`nil`)
+
+ // Reflect types
+ case reflect.Type:
+ if isNil(i) /* safer nil check */ {
+ appendNilType(fmt, `reflect.Type`)
+ } else {
+ appendType(fmt, `reflect.Type`)
+ fmt.buf.AppendString(`(` + i.String() + `)`)
+ }
+ case reflect.Value:
+ appendType(fmt, `reflect.Value`)
+ fmt.buf.AppendByte('(')
+ fmt.flags |= vboseBit
+ appendRValue(fmt, i)
+ fmt.buf.AppendByte(')')
+
+ // Bytes and string types
+ case byte:
+ appendByte(fmt, i)
+ case []byte:
+ appendBytes(fmt, i)
+ case string:
+ appendString(fmt, i)
+
+ // Int types
+ case int:
+ appendInt(fmt, int64(i))
+ case int8:
+ appendInt(fmt, int64(i))
+ case int16:
+ appendInt(fmt, int64(i))
+ case int32:
+ appendInt(fmt, int64(i))
+ case int64:
+ appendInt(fmt, i)
+
+ // Uint types
+ case uint:
+ appendUint(fmt, uint64(i))
+ // case uint8 :: this is 'byte'
+ case uint16:
+ appendUint(fmt, uint64(i))
+ case uint32:
+ appendUint(fmt, uint64(i))
+ case uint64:
+ appendUint(fmt, i)
+
+ // Float types
+ case float32:
+ appendFloat(fmt, float64(i))
+ case float64:
+ appendFloat(fmt, i)
+
+ // Bool type
+ case bool:
+ appendBool(fmt, i)
+
+ // Complex types
+ case complex64:
+ appendComplex(fmt, complex128(i))
+ case complex128:
+ appendComplex(fmt, i)
+
+ // Method types
+ case error:
+ switch {
+ case fmt.Verbose():
+ ok = false
+ case isNil(i) /* use safer nil check */ :
+ appendNilType(fmt, reflect.TypeOf(i).String())
+ default:
+ defer catchPanic()
+ appendString(fmt, i.Error())
+ }
+ case Formattable:
+ switch {
+ case fmt.Verbose():
+ ok = false
+ case isNil(i) /* use safer nil check */ :
+ appendNilType(fmt, reflect.TypeOf(i).String())
+ default:
+ defer catchPanic()
+ fmt.buf.B = i.AppendFormat(fmt.buf.B)
+ }
+ case interface{ String() string }:
+ switch {
+ case fmt.Verbose():
+ ok = false
+ case isNil(i) /* use safer nil check */ :
+ appendNilType(fmt, reflect.TypeOf(i).String())
+ default:
+ defer catchPanic()
+ appendString(fmt, i.String())
+ }
+
+ // No quick handler
+ default:
+ ok = false
+ }
+
+ return ok
+}
+
+// appendReflectValue will safely append a reflected value.
+func appendRValue(fmt format, v reflect.Value) {
+ switch v.Kind() {
+ // String and byte types
+ case reflect.Uint8:
+ appendByte(fmt, byte(v.Uint()))
+ case reflect.String:
+ appendString(fmt, v.String())
+
+ // Float tpyes
+ case reflect.Float32, reflect.Float64:
+ appendFloat(fmt, v.Float())
+
+ // Int types
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ appendInt(fmt, v.Int())
+
+ // Uint types
+ case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+ appendUint(fmt, v.Uint())
+
+ // Complex types
+ case reflect.Complex64, reflect.Complex128:
+ appendComplex(fmt, v.Complex())
+
+ // Bool type
+ case reflect.Bool:
+ appendBool(fmt, v.Bool())
+
+ // Slice and array types
+ case reflect.Array:
+ appendArrayType(fmt, v)
+ case reflect.Slice:
+ if v.IsNil() {
+ appendNilType(fmt, v.Type().String())
+ } else {
+ appendArrayType(fmt, v)
+ }
+
+ // Map types
+ case reflect.Map:
+ if v.IsNil() {
+ appendNilType(fmt, v.Type().String())
+ } else {
+ appendMapType(fmt, v)
+ }
+
+ // Struct types
+ case reflect.Struct:
+ appendStructType(fmt, v)
+
+ // Deref'able ptr types
+ case reflect.Ptr, reflect.Interface:
+ if v.IsNil() {
+ appendNilType(fmt, v.Type().String())
+ } else {
+ appendRValue(fmt.IncrDerefs(), v.Elem())
+ }
+
+ // 'raw' pointer types
+ case reflect.UnsafePointer:
+ appendType(fmt, `unsafe.Pointer`)
+ fmt.buf.AppendByte('(')
+ if u := v.Pointer(); u != 0 {
+ fmt.buf.AppendString("0x")
+ fmt.buf.B = strconv.AppendUint(fmt.buf.B, uint64(u), 16)
+ } else {
+ fmt.buf.AppendString(`nil`)
+ }
+ fmt.buf.AppendByte(')')
+ case reflect.Uintptr:
+ appendType(fmt, `uintptr`)
+ fmt.buf.AppendByte('(')
+ if u := v.Uint(); u != 0 {
+ fmt.buf.AppendString("0x")
+ fmt.buf.B = strconv.AppendUint(fmt.buf.B, u, 16)
+ } else {
+ fmt.buf.AppendString(`nil`)
+ }
+ fmt.buf.AppendByte(')')
+
+ // Generic types we don't *exactly* handle
+ case reflect.Func, reflect.Chan:
+ if v.IsNil() {
+ appendNilType(fmt, v.Type().String())
+ } else {
+ fmt.buf.AppendString(v.String())
+ }
+
+ // Unhandled kind
+ default:
+ fmt.buf.AppendString(v.String())
+ }
+}
+
+// appendArrayType Appends an array of unknown type (parsed by reflection) to buf, unlike appendSliceType does NOT catch nil slice.
+func appendArrayType(fmt format, v reflect.Value) {
+ // get no. elements
+ n := v.Len()
+
+ fmt.buf.AppendByte('[')
+
+ // Append values
+ for i := 0; i < n; i++ {
+ appendRValueOrIfaceNext(fmt.SetIsValue(), v.Index(i))
+ fmt.buf.AppendByte(',')
+ }
+
+ // Drop last comma
+ if n > 0 {
+ fmt.buf.Truncate(1)
+ }
+
+ fmt.buf.AppendByte(']')
+}
+
+// appendMapType Appends a map of unknown types (parsed by reflection) to buf.
+func appendMapType(fmt format, v reflect.Value) {
+ // Prepend type if verbose
+ if fmt.Verbose() {
+ appendType(fmt, v.Type().String())
+ }
+
+ // Get a map iterator
+ r := v.MapRange()
+ n := v.Len()
+
+ fmt.buf.AppendByte('{')
+
+ // Iterate pairs
+ for r.Next() {
+ appendRValueOrIfaceNext(fmt.SetIsKey(), r.Key())
+ fmt.buf.AppendByte('=')
+ appendRValueOrIfaceNext(fmt.SetIsValue(), r.Value())
+ fmt.buf.AppendByte(' ')
+ }
+
+ // Drop last space
+ if n > 0 {
+ fmt.buf.Truncate(1)
+ }
+
+ fmt.buf.AppendByte('}')
+}
+
+// appendStructType Appends a struct (as a set of key-value fields) to buf.
+func appendStructType(fmt format, v reflect.Value) {
+ // Get value type & no. fields
+ t := v.Type()
+ n := v.NumField()
+
+ // Prepend type if verbose
+ if fmt.Verbose() {
+ appendType(fmt, v.Type().String())
+ }
+
+ fmt.buf.AppendByte('{')
+
+ // Iterate fields
+ for i := 0; i < n; i++ {
+ vfield := v.Field(i)
+ tfield := t.Field(i)
+
+ // Append field name
+ fmt.buf.AppendString(tfield.Name)
+ fmt.buf.AppendByte('=')
+ appendRValueOrIfaceNext(fmt.SetIsValue(), vfield)
+
+ // Iter written count
+ fmt.buf.AppendByte(' ')
+ }
+
+ // Drop last space
+ if n > 0 {
+ fmt.buf.Truncate(1)
+ }
+
+ fmt.buf.AppendByte('}')
+}
+
+// containsSpaceOrTab checks if "s" contains space or tabs.
+func containsSpaceOrTab(s string) bool {
+ for _, r := range s {
+ if r == ' ' || r == '\t' {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/codeberg.org/gruf/go-format/formatter.go b/vendor/codeberg.org/gruf/go-format/formatter.go
new file mode 100644
index 000000000..640fa3f04
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/formatter.go
@@ -0,0 +1,352 @@
+package format
+
+import (
+ "strings"
+)
+
+// 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 *Buffer, v ...interface{}) {
+ for _, v := range v {
+ appendIfaceOrRValue(format{maxd: f.MaxDepth, buf: buf}, v)
+ buf.AppendByte(' ')
+ }
+ if len(v) > 0 {
+ buf.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 *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{
+ maxd: f.MaxDepth,
+ buf: 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
+ MoveUpTo = func() {
+ if i := strings.IndexByte(s[idx:], '}'); 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 {
+ // Get current window
+ str := Str()
+ if len(str) < 1 {
+ return true
+ }
+
+ // Index HAS to fit within platform int
+ if !can32bitInt(str) && !can64bitInt(str) {
+ return false
+ }
+
+ // Build integer from string
+ carg = 0
+ for _, c := range []byte(str) {
+ carg = carg*10 + int(c-'0')
+ }
+
+ return true
+ }
+
+ // ParseOp parses operands from the current string
+ // window, updating 'last' to 'idx'. The string window
+ // is ASSUMED to contain only valid operand ASCII. This
+ // returns success on parsing of operand logic
+ ParseOp = func() bool {
+ // Get current window
+ str := Str()
+ if len(str) < 1 {
+ return true
+ }
+
+ // (for now) only
+ // accept length = 1
+ if len(str) > 1 {
+ return false
+ }
+
+ switch str[0] {
+ case 'k':
+ fmt.flags |= isKeyBit
+ case 'v':
+ fmt.flags |= isValBit
+ case '?':
+ fmt.flags |= vboseBit
+ }
+
+ return true
+ }
+
+ // 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
+ appendIfaceOrRValue(fmt, a[carg])
+ } else {
+ // No argument found for index
+ buf.AppendString(`!{MISSING_ARG}`)
+ }
+ }
+
+ // Reset will reset the mode to ground, the flags
+ // to empty and parsed 'carg' to empty
+ Reset = func() {
+ mode = modeNone
+ fmt.flags = 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
+ buf.AppendString(Str())
+ mode = modeOpen
+ MoveUp()
+ case '}':
+ // Enter close mode
+ buf.AppendString(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
+ buf.AppendByte('{')
+ mode = modeNone
+ MoveUp()
+ case '}':
+ // Format arg
+ AppendArg()
+ Reset()
+ MoveUp()
+ case ':':
+ // Starting operands
+ mode = modeOp
+ MoveUp()
+ default:
+ // Bad char, missing a close
+ buf.AppendString(`!{MISSING_CLOSE}`)
+ mode = modeNone
+ MoveUpTo()
+ }
+
+ // Encountered close '}'
+ case modeClose:
+ switch c {
+ case '}':
+ // Escaped close bracket
+ buf.AppendByte('}')
+ mode = modeNone
+ MoveUp()
+ default:
+ // Missing an open bracket
+ buf.AppendString(`!{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
+ buf.AppendString(`!{BAD_INDEX}`)
+ mode = modeNone
+ MoveUpTo()
+ } else {
+ // Starting operands
+ mode = modeOp
+ MoveUp()
+ }
+ case '}':
+ if !ParseIndex() {
+ // Unable to parse an integer
+ buf.AppendString(`!{BAD_INDEX}`)
+ } else {
+ // Format arg
+ AppendArg()
+ }
+ Reset()
+ MoveUp()
+ default:
+ // Not a valid index character
+ buf.AppendString(`!{BAD_INDEX}`)
+ mode = modeNone
+ MoveUpTo()
+ }
+
+ // Preparing operands
+ case modeOp:
+ switch c {
+ case 'k', 'v', '?':
+ // TODO: set flags as received
+ case '}':
+ if !ParseOp() {
+ // Unable to parse operands
+ buf.AppendString(`!{BAD_OPERAND}`)
+ } else {
+ // Format arg
+ AppendArg()
+ }
+ Reset()
+ MoveUp()
+ default:
+ // Not a valid operand char
+ buf.AppendString(`!{BAD_OPERAND}`)
+ Reset()
+ MoveUpTo()
+ }
+ }
+ }
+
+ // Append any remaining
+ buf.AppendString(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 *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 *Buffer, s string, a ...interface{}) {
+ formatter.Appendf(buf, s, a...)
+}
diff --git a/vendor/codeberg.org/gruf/go-format/print.go b/vendor/codeberg.org/gruf/go-format/print.go
new file mode 100644
index 000000000..288e6af10
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/print.go
@@ -0,0 +1,88 @@
+package format
+
+import (
+ "io"
+ "os"
+ "sync"
+)
+
+// pool is the global printer buffer pool.
+var pool = sync.Pool{
+ New: func() interface{} {
+ return &Buffer{}
+ },
+}
+
+// getBuf fetches a buffer from pool.
+func getBuf() *Buffer {
+ return pool.Get().(*Buffer)
+}
+
+// putBuf places a Buffer back in pool.
+func putBuf(buf *Buffer) {
+ if buf.Cap() > 64<<10 {
+ return // drop large
+ }
+ buf.Reset()
+ pool.Put(buf)
+}
+
+// Sprint will format supplied values, returning this string.
+func Sprint(v ...interface{}) string {
+ buf := Buffer{}
+ Append(&buf, v...)
+ return buf.String()
+}
+
+// Sprintf will format supplied format string and args, returning this string.
+// See Formatter.Appendf() for more documentation.
+func Sprintf(s string, a ...interface{}) string {
+ buf := Buffer{}
+ Appendf(&buf, s, a...)
+ return buf.String()
+}
+
+// Print will format supplied values, print this to os.Stdout.
+func Print(v ...interface{}) {
+ Fprint(os.Stdout, v...) //nolint
+}
+
+// Printf will format supplied format string and args, printing this to os.Stdout.
+// See Formatter.Appendf() for more documentation.
+func Printf(s string, a ...interface{}) {
+ Fprintf(os.Stdout, s, a...) //nolint
+}
+
+// Println will format supplied values, append a trailing newline and print this to os.Stdout.
+func Println(v ...interface{}) {
+ Fprintln(os.Stdout, v...) //nolint
+}
+
+// Fprint will format supplied values, writing this to an io.Writer.
+func Fprint(w io.Writer, v ...interface{}) (int, error) {
+ buf := getBuf()
+ Append(buf, v...)
+ n, err := w.Write(buf.B)
+ putBuf(buf)
+ return n, err
+}
+
+// Fprintf will format supplied format string and args, writing this to an io.Writer.
+// See Formatter.Appendf() for more documentation.
+func Fprintf(w io.Writer, s string, a ...interface{}) (int, error) {
+ buf := getBuf()
+ Appendf(buf, s, a...)
+ n, err := w.Write(buf.B)
+ putBuf(buf)
+ return n, err
+}
+
+// Println will format supplied values, append a trailing newline and writer this to an io.Writer.
+func Fprintln(w io.Writer, v ...interface{}) (int, error) {
+ buf := getBuf()
+ Append(buf, v...)
+ buf.AppendByte('\n')
+ n, err := w.Write(buf.B)
+ putBuf(buf)
+ return n, err
+}
diff --git a/vendor/codeberg.org/gruf/go-format/util.go b/vendor/codeberg.org/gruf/go-format/util.go
new file mode 100644
index 000000000..68a9e2de3
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-format/util.go
@@ -0,0 +1,13 @@
+package format
+
+import "strconv"
+
+// can32bitInt returns whether it's possible for 's' to contain an int on 32bit platforms.
+func can32bitInt(s string) bool {
+ return strconv.IntSize == 32 && (0 < len(s) && len(s) < 10)
+}
+
+// can64bitInt returns whether it's possible for 's' to contain an int on 64bit platforms.
+func can64bitInt(s string) bool {
+ return strconv.IntSize == 64 && (0 < len(s) && len(s) < 19)
+}