-
Notifications
You must be signed in to change notification settings - Fork 0
/
resolver.go
236 lines (220 loc) · 6.44 KB
/
resolver.go
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// Package fastmsgpack is a msgpack decoder. See the README.
package fastmsgpack
import (
"errors"
"strings"
"github.com/Jille/genericz/slicez"
"github.com/hexon/fastmsgpack/internal"
)
// Decode the given data (with the optional given dictionary).
// Any []byte and string in the return value might point into memory from the given data. Don't modify the input data until you're done with the return value.
// The dictionary is optional and can be nil.
func Decode(data []byte, opts ...DecodeOption) (any, error) {
var opt internal.DecodeOptions
for _, o := range opts {
o(&opt)
}
v, _, err := decodeValue(data, opt)
return v, err
}
// NewResolver prepares a new resolver. It can be reused for multiple Resolve calls.
// You can't query the same field twice. You can't even query a child of something else you request (e.g. both "person.properties" and "person.properties.age"). This is the only reason NewResolver might return an error.
// The dictionary is optional and can be nil.
func NewResolver(fields []string, opts ...DecodeOption) (*Resolver, error) {
interests := map[string]any{}
r := &Resolver{interests, opts, len(fields)}
for n, f := range fields {
if err := r.addField(f, n); err != nil {
return nil, err
}
}
return r, nil
}
type subresolver struct {
interests map[string]any
destination int
numFields int
}
type Resolver struct {
interests map[string]any
decodeOptions []DecodeOption
numFields int
}
// AddArrayResolver allows resolving inside array fields. For example like this pseudocode: `r.AddArrayResolve("person.addresses", NewResolver(["street"]))`.
// It returns the offset in the return value from Resolve(), which will be of type [][]any.
// AddArrayResolver can not be called concurrently with itself or Resolve.
// The dict that was given to the subresolver is not used.
//
// r, err := NewResolver([]string{"person.properties.age"}, nil)
// sub, err := NewResolver([]string{"street", "number"}, nil)
// addrOffset, err := r.AddArrayResolver("person.addresses", sub)
// found, err := r.Resolve(msgpackData)
// age := found[0] // e.g. 5
// addresses := found[addrOffset] // e.g. [][]any{[]any{"Main Street", 1}, []any{"Second Street", 2}}
func (r *Resolver) AddArrayResolver(field string, sub *Resolver) (int, error) {
dst := r.numFields
if err := r.addField(field, subresolver{sub.interests, dst, sub.numFields}); err != nil {
return -1, err
}
r.numFields++
return dst, nil
}
func (r *Resolver) addField(field string, what any) error {
sp := strings.Split(field, ".")
dst := r.interests
for len(sp) > 1 {
v := dst[sp[0]]
m, ok := v.(map[string]any)
if !ok {
if v != nil {
return errors.New("NewResolver: conflicting fields requested")
}
m = map[string]any{}
dst[sp[0]] = m
}
dst = m
sp = sp[1:]
}
if dst[sp[0]] != nil {
return errors.New("NewResolver: conflicting fields requested: " + field)
}
dst[sp[0]] = what
return nil
}
type SubresolverDescription struct {
Fields []string
Subresolvers map[string]SubresolverDescription
Index int
}
// Describe returns which fields and subresolvers were registered to this Resolver.
// The returned values should not be modified.
func (r *Resolver) Describe() ([]string, map[string]SubresolverDescription) {
fields := make([]string, r.numFields)
subs := map[string]SubresolverDescription{}
recurseInterests(fields, subs, r.interests, "")
fields = fields[:len(fields)-len(subs)]
return fields, subs
}
func recurseInterests(fields []string, subs map[string]SubresolverDescription, i any, prefix string) {
switch i := i.(type) {
case int:
fields[i] = prefix
case map[string]any:
if prefix != "" {
prefix += "."
}
for k, v := range i {
recurseInterests(fields, subs, v, prefix+k)
}
case subresolver:
sd := SubresolverDescription{
Index: i.destination,
Fields: make([]string, i.numFields),
Subresolvers: map[string]SubresolverDescription{},
}
recurseInterests(sd.Fields, sd.Subresolvers, i.interests, "")
sd.Fields = sd.Fields[:len(sd.Fields)-len(sd.Subresolvers)]
subs[prefix] = sd
}
}
// Resolve scans through the given data and returns an array with the fields you've requested from this Resolver.
// Any []byte and string in the return value might point into memory from the given data. Don't modify the input data until you're done with the return value.
func (r *Resolver) Resolve(data []byte, opts ...DecodeOption) (foundFields []any, retErr error) {
rc := resolveCall{
decoder: NewDecoder(data, slicez.Concat(r.decodeOptions, opts)...),
result: make([]any, r.numFields),
}
if err := rc.recurseMap(r.interests, false); err != nil {
return nil, err
}
return rc.result, nil
}
type resolveCall struct {
decoder *Decoder
result []any
}
func (rc *resolveCall) recurseMap(interests map[string]any, mustSkip bool) error {
elements, err := rc.decoder.DecodeMapLen()
if err != nil {
if err == ErrVoid {
if err := rc.decoder.Skip(); err != nil {
return err
}
}
return err
}
sought := len(interests)
for elements > 0 {
elements--
k, err := rc.decoder.DecodeString()
if err != nil {
if err == ErrVoid {
if err := rc.decoder.Skip(); err != nil {
return err
}
if err := rc.decoder.Skip(); err != nil {
return err
}
continue
}
return err
}
switch x := interests[k].(type) {
case int:
rc.result[x], err = rc.decoder.DecodeValue()
sought--
case map[string]any:
sought--
err = rc.recurseMap(x, mustSkip || sought > 0)
case subresolver:
sought--
err = rc.recurseArray(x, mustSkip || sought > 0)
default:
err = rc.decoder.Skip()
}
if err == ErrVoid {
err = rc.decoder.Skip()
}
if err != nil {
return err
}
if elements == 0 {
break
}
if sought == 0 {
if mustSkip {
return rc.decoder.Break()
}
return nil
}
}
return nil
}
func (rc *resolveCall) recurseArray(sub subresolver, mustSkip bool) error {
elements, err := rc.decoder.DecodeArrayLen()
if err != nil {
if err == ErrVoid {
if err := rc.decoder.Skip(); err != nil {
return err
}
}
return err
}
parentResults := rc.result
results := make([][]any, elements)
var voided int
for i := 0; elements > i; i++ {
rc.result = make([]any, sub.numFields)
if err := rc.recurseMap(sub.interests, mustSkip || i < elements-1); err != nil {
if err == ErrVoid {
voided++
continue
}
return err
}
results[i-voided] = rc.result
}
rc.result = parentResults
rc.result[sub.destination] = results[:len(results)-voided]
return nil
}