mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-12-02 17:19:21 +00:00
7ec1e1332e
* 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... 😇
* 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
118 lines
2.5 KiB
Go
118 lines
2.5 KiB
Go
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)
|
|
}
|