summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-structr/util.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/codeberg.org/gruf/go-structr/util.go')
-rw-r--r--vendor/codeberg.org/gruf/go-structr/util.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-structr/util.go b/vendor/codeberg.org/gruf/go-structr/util.go
new file mode 100644
index 000000000..d8f227baf
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-structr/util.go
@@ -0,0 +1,118 @@
+package structr
+
+import (
+ "fmt"
+ "reflect"
+ "sync"
+ "unicode"
+ "unicode/utf8"
+
+ "codeberg.org/gruf/go-byteutil"
+ "codeberg.org/gruf/go-mangler"
+)
+
+// findField will search for a struct field with given set of names, where names is a len > 0 slice of names account for nesting.
+func findField(t reflect.Type, names []string, allowZero bool) (sfield structfield, ok bool) {
+ var (
+ // isExported returns whether name is exported
+ // from a package; can be func or struct field.
+ isExported = func(name string) bool {
+ r, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(r)
+ }
+
+ // popName pops the next name from
+ // the provided slice of field names.
+ popName = func() string {
+ // Pop next name.
+ name := names[0]
+ names = names[1:]
+
+ // Ensure valid name.
+ if !isExported(name) {
+ panicf("field is not exported: %s", name)
+ }
+
+ return name
+ }
+
+ // field is the iteratively searched-for
+ // struct field value in below loop.
+ field reflect.StructField
+ )
+
+ for len(names) > 0 {
+ // Pop next name.
+ name := popName()
+
+ // Follow any ptrs leading to field.
+ for t.Kind() == reflect.Pointer {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ // The end type after following ptrs must be struct.
+ panicf("field %s is not struct (ptr): %s", t, name)
+ }
+
+ // Look for next field by name.
+ field, ok = t.FieldByName(name)
+ if !ok {
+ return
+ }
+
+ // Append next set of indices required to reach field.
+ sfield.index = append(sfield.index, field.Index...)
+
+ // Set the next type.
+ t = field.Type
+ }
+
+ // Get final type mangler func.
+ sfield.mangler = mangler.Get(t)
+
+ if allowZero {
+ var buf []byte
+
+ // Allocate field instance.
+ v := reflect.New(field.Type)
+ v = v.Elem()
+
+ // Serialize this zero value into buf.
+ buf = sfield.mangler(buf, v.Interface())
+
+ // Set zero value str.
+ sfield.zero = string(buf)
+ }
+
+ return
+}
+
+// panicf provides a panic with string formatting.
+func panicf(format string, args ...any) {
+ panic(fmt.Sprintf(format, args...))
+}
+
+// bufpool provides a memory pool of byte
+// buffers used when encoding key types.
+var bufPool sync.Pool
+
+// getBuf fetches buffer from memory pool.
+func getBuf() *byteutil.Buffer {
+ v := bufPool.Get()
+ if v == nil {
+ buf := new(byteutil.Buffer)
+ buf.B = make([]byte, 0, 512)
+ v = buf
+ }
+ return v.(*byteutil.Buffer)
+}
+
+// putBuf replaces buffer in memory pool.
+func putBuf(buf *byteutil.Buffer) {
+ if buf.Cap() > int(^uint16(0)) {
+ return // drop large bufs
+ }
+ buf.Reset()
+ bufPool.Put(buf)
+}