diff options
author | 2022-01-16 18:52:30 +0100 | |
---|---|---|
committer | 2022-01-16 18:52:30 +0100 | |
commit | 6f5ccf435585e43a00e3cc50f4bcefac36ada818 (patch) | |
tree | ba368d27464b79b1e5d010c0662fd3e340bf108e /vendor/codeberg.org/gruf/go-format/format.go | |
parent | add go-runners to readme (diff) | |
download | gotosocial-6f5ccf435585e43a00e3cc50f4bcefac36ada818.tar.xz |
update dependencies
Diffstat (limited to 'vendor/codeberg.org/gruf/go-format/format.go')
-rw-r--r-- | vendor/codeberg.org/gruf/go-format/format.go | 565 |
1 files changed, 565 insertions, 0 deletions
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 +} |