summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-structr/util.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-01-19 12:57:29 +0000
committerLibravatar GitHub <noreply@github.com>2024-01-19 12:57:29 +0000
commit7ec1e1332e7d04e74451acef18b41f389722b698 (patch)
tree9c69eca7fc664ab5564279a2e065dfd5c2ddd17b /vendor/codeberg.org/gruf/go-structr/util.go
parent[chore] chore rationalise http return codes for activitypub handlers (#2540) (diff)
downloadgotosocial-7ec1e1332e7d04e74451acef18b41f389722b698.tar.xz
[performance] overhaul struct (+ result) caching library for simplicity, performance and multiple-result lookups (#2535)
* rewrite cache library as codeberg.org/gruf/go-structr, implement in gotosocial * use actual go-structr release version (not just commit hash) * revert go toolchain changes (damn you go for auto changing this) * fix go mod woes * ensure %w is used in calls to errs.Appendf() * fix error checking * fix possible panic * remove unnecessary start/stop functions, move to main Cache{} struct, add note regarding which caches require start/stop * fix copy-paste artifact... :innocent: * fix all comment copy-paste artifacts * remove dropID() function, now we can just use slices.DeleteFunc() * use util.Deduplicate() instead of collate(), move collate to util * move orderByIDs() to util package and "generify" * add a util.DeleteIf() function, use this to delete entries on failed population * use slices.DeleteFunc() instead of util.DeleteIf() (i had the logic mixed up in my head somehow lol) * add note about how collate differs from deduplicate
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)
+}