1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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)
}
|