-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathcache.go
More file actions
541 lines (479 loc) · 16.4 KB
/
Copy pathcache.go
File metadata and controls
541 lines (479 loc) · 16.4 KB
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
// Copyright (c) Faye Amacker. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.
package cbor
import (
"bytes"
"cmp"
"errors"
"fmt"
"reflect"
"slices"
"strconv"
"strings"
"sync"
)
type encodeFuncs struct {
ef encodeFunc
ief isEmptyFunc
izf isZeroFunc
}
var (
decodingStructTypeCache sync.Map // map[reflect.Type]*decodingStructType
encodingStructTypeCache sync.Map // map[reflect.Type]*encodingStructType
encodeFuncCache sync.Map // map[reflect.Type]encodeFuncs
typeInfoCache sync.Map // map[reflect.Type]*typeInfo
)
type specialType int
const (
specialTypeNone specialType = iota
specialTypeUnmarshalerIface
specialTypeUnexportedUnmarshalerIface
specialTypeEmptyIface
specialTypeIface
specialTypeTag
specialTypeTime
specialTypeJSONUnmarshalerIface
)
type typeInfo struct {
elemTypeInfo *typeInfo
keyTypeInfo *typeInfo
typ reflect.Type
kind reflect.Kind
nonPtrType reflect.Type
nonPtrKind reflect.Kind
spclType specialType
implBinaryUnmarshaler bool
implTextUnmarshaler bool
elemIsUint8 bool
nonPtrTypeIsString bool
typeIsString bool
keyNeedsHashableValueCheck bool
}
func newTypeInfo(t reflect.Type, newTypeInfos map[reflect.Type]*typeInfo) *typeInfo {
tInfo := typeInfo{
typ: t,
kind: t.Kind(),
typeIsString: t == typeString,
}
newTypeInfos[t] = &tInfo
for t.Kind() == reflect.Pointer {
t = t.Elem()
}
k := t.Kind()
tInfo.nonPtrType = t
tInfo.nonPtrKind = k
if k == reflect.Interface {
if t.NumMethod() == 0 {
tInfo.spclType = specialTypeEmptyIface
} else {
tInfo.spclType = specialTypeIface
}
} else if t == typeTag {
tInfo.spclType = specialTypeTag
} else if t == typeTime {
tInfo.spclType = specialTypeTime
} else if reflect.PointerTo(t).Implements(typeUnexportedUnmarshaler) {
tInfo.spclType = specialTypeUnexportedUnmarshalerIface
} else if reflect.PointerTo(t).Implements(typeUnmarshaler) {
tInfo.spclType = specialTypeUnmarshalerIface
} else if reflect.PointerTo(t).Implements(typeJSONUnmarshaler) {
tInfo.spclType = specialTypeJSONUnmarshalerIface
}
tInfo.implBinaryUnmarshaler = reflect.PointerTo(t).Implements(typeBinaryUnmarshaler)
tInfo.implTextUnmarshaler = reflect.PointerTo(t).Implements(typeTextUnmarshaler)
tInfo.nonPtrTypeIsString = t == typeString
switch k {
case reflect.Array, reflect.Slice:
tInfo.elemTypeInfo = getTypeInfoWithNewTypeInfos(t.Elem(), newTypeInfos)
tInfo.elemIsUint8 = tInfo.elemTypeInfo.kind == reflect.Uint8
case reflect.Map:
tInfo.keyTypeInfo = getTypeInfoWithNewTypeInfos(t.Key(), newTypeInfos)
tInfo.keyNeedsHashableValueCheck = needsHashableValueCheck(t.Key())
tInfo.elemTypeInfo = getTypeInfoWithNewTypeInfos(t.Elem(), newTypeInfos)
tInfo.elemIsUint8 = tInfo.elemTypeInfo.kind == reflect.Uint8
}
return &tInfo
}
// needsHashableValueCheck returns true if the hashability of a value of
// the given type can't be determined by the static type.
func needsHashableValueCheck(typ reflect.Type) bool {
switch typ.Kind() {
case reflect.Interface:
return true
case reflect.Array:
return needsHashableValueCheck(typ.Elem())
case reflect.Struct:
for i := range typ.NumField() {
if needsHashableValueCheck(typ.Field(i).Type) {
return true
}
}
}
return false
}
type decodingStructType struct {
fields decodingFields
fieldIndicesByName map[string]int // Only populated if toArray is false
fieldIndicesByIntKey map[int64]int // Only populated if toArray is false
err error
toArray bool
}
func getDecodingStructType(t reflect.Type) (*decodingStructType, error) {
if v, _ := decodingStructTypeCache.Load(t); v != nil {
structType := v.(*decodingStructType)
if structType.err != nil {
return nil, structType.err
}
return structType, nil
}
flds, structOptions := collectFields(t)
hasToArray, hasToIndefArray, err := parseArrayStructOptions(t, structOptions)
if err != nil {
structType := &decodingStructType{err: err}
decodingStructTypeCache.Store(t, structType)
return nil, err
}
// Both options describe a struct laid out as a CBOR array. The decoder
// accepts definite- and indefinite-length arrays interchangeably, so
// the same code path serves both options.
if hasToArray || hasToIndefArray {
return getDecodingStructToArrayType(t, flds)
}
fieldIndicesByName := make(map[string]int, len(flds))
var fieldIndicesByIntKey map[int64]int
decFlds := make(decodingFields, len(flds))
for i, f := range flds {
// nameAsInt is set in collectFields() except for fields with an unparsable tagged name.
// Atoi() is called here to catch and save parsing errors.
if f.keyAsInt && f.nameAsInt == 0 {
if _, numErr := strconv.Atoi(f.name); numErr != nil {
structType := &decodingStructType{
err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"),
}
decodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
}
if f.keyAsInt {
if fieldIndicesByIntKey == nil {
fieldIndicesByIntKey = make(map[int64]int, len(flds))
}
// The duplication check is only a safeguard, since collectFields() already deduplicates fields.
if _, ok := fieldIndicesByIntKey[f.nameAsInt]; ok {
structType := &decodingStructType{
err: fmt.Errorf("cbor: two or more fields of %v have the same keyasint value %d", t, f.nameAsInt),
}
decodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
fieldIndicesByIntKey[f.nameAsInt] = i
} else {
// The duplication check is only a safeguard, since collectFields() already deduplicates fields.
if _, ok := fieldIndicesByName[f.name]; ok {
structType := &decodingStructType{
err: fmt.Errorf("cbor: two or more fields of %v have the same name %q", t, f.name),
}
decodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
fieldIndicesByName[f.name] = i
}
decFlds[i] = &decodingField{
field: *f,
typInfo: getTypeInfo(f.typ),
}
}
structType := &decodingStructType{
fields: decFlds,
fieldIndicesByName: fieldIndicesByName,
fieldIndicesByIntKey: fieldIndicesByIntKey,
}
decodingStructTypeCache.Store(t, structType)
return structType, nil
}
func getDecodingStructToArrayType(t reflect.Type, flds fields) (*decodingStructType, error) {
decFlds := make(decodingFields, len(flds))
for i, f := range flds {
// nameAsInt is set in collectFields() except for fields with an unparsable tagged name.
// Atoi() is called here to catch and save parsing errors.
if f.keyAsInt && f.nameAsInt == 0 {
if _, numErr := strconv.Atoi(f.name); numErr != nil {
structType := &decodingStructType{
err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"),
}
decodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
}
decFlds[i] = &decodingField{
field: *f,
typInfo: getTypeInfo(f.typ),
}
}
structType := &decodingStructType{
fields: decFlds,
toArray: true,
}
decodingStructTypeCache.Store(t, structType)
return structType, nil
}
type encodingStructType struct {
fields encodingFields
bytewiseFields encodingFields // Only populated if struct is not array-shaped
lengthFirstFields encodingFields // Only populated if struct is not array-shaped
omitEmptyFieldsIdx []int // Only populated if struct is not array-shaped
err error
toArray bool // True iff the struct declares the `toarray` option
toIndefArray bool // True iff the struct declares the `toindefarray` option
}
func (st *encodingStructType) getFields(em *encMode) encodingFields {
switch em.sort {
case SortNone, SortFastShuffle:
return st.fields
case SortLengthFirst:
return st.lengthFirstFields
default:
return st.bytewiseFields
}
}
func bytewiseFieldCmp(fi, fj *encodingField) int {
return bytes.Compare(fi.cborName, fj.cborName)
}
func lengthFirstFieldCmp(fi, fj *encodingField) int {
if len(fi.cborName) != len(fj.cborName) {
return cmp.Compare(len(fi.cborName), len(fj.cborName))
}
return bytes.Compare(fi.cborName, fj.cborName)
}
func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
if v, _ := encodingStructTypeCache.Load(t); v != nil {
structType := v.(*encodingStructType)
if structType.err != nil {
return nil, structType.err
}
return structType, nil
}
flds, structOptions := collectFields(t)
hasToArray, hasToIndefArray, err := parseArrayStructOptions(t, structOptions)
if err != nil {
structType := &encodingStructType{err: err}
encodingStructTypeCache.Store(t, structType)
return nil, err
}
if hasToArray || hasToIndefArray {
return getEncodingStructToArrayType(t, flds, hasToArray, hasToIndefArray)
}
var hasKeyAsInt bool
var hasKeyAsStr bool
var omitEmptyIdx []int
encFlds := make(encodingFields, len(flds))
for i, f := range flds {
encFlds[i] = &encodingField{field: *f}
ef := encFlds[i]
// Get field's encodeFunc
ef.ef, ef.ief, ef.izf = getEncodeFunc(f.typ)
if ef.ef == nil {
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
encodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
// Encode field name
if f.keyAsInt {
if f.nameAsInt == 0 {
// nameAsInt is set in collectFields() except for fields with an unparsable tagged name.
// Atoi() is called here to catch and save parsing errors.
if _, numErr := strconv.Atoi(f.name); numErr != nil {
structType := &encodingStructType{
err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"),
}
encodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
}
nameAsInt := f.nameAsInt
if nameAsInt >= 0 {
ef.cborName = make([]byte, 0, encodedHeadLength(uint64(nameAsInt)))
ef.cborName = appendHead(ef.cborName, byte(cborTypePositiveInt), uint64(nameAsInt))
} else {
n := nameAsInt*(-1) - 1
ef.cborName = make([]byte, 0, encodedHeadLength(uint64(n)))
ef.cborName = appendHead(ef.cborName, byte(cborTypeNegativeInt), uint64(n))
}
hasKeyAsInt = true
} else {
ef.cborName = make([]byte, 0, encodedHeadLength(uint64(len(f.name)))+len(f.name))
ef.cborName = appendHead(ef.cborName, byte(cborTypeTextString), uint64(len(f.name)))
ef.cborName = append(ef.cborName, f.name...)
// If cborName contains a text string, then cborNameByteString contains a
// string that has the byte string major type but is otherwise identical to
// cborName.
ef.cborNameByteString = make([]byte, len(ef.cborName))
copy(ef.cborNameByteString, ef.cborName)
// Reset encoded CBOR type to byte string, preserving the "additional
// information" bits:
ef.cborNameByteString[0] = byte(cborTypeByteString) |
getAdditionalInformation(ef.cborNameByteString[0])
hasKeyAsStr = true
}
// Check if field can be omitted when empty
if f.omitEmpty {
omitEmptyIdx = append(omitEmptyIdx, i)
}
}
// Sort fields by canonical order
bytewiseFields := make(encodingFields, len(encFlds))
copy(bytewiseFields, encFlds)
slices.SortFunc(bytewiseFields, bytewiseFieldCmp)
lengthFirstFields := bytewiseFields
if hasKeyAsInt && hasKeyAsStr {
lengthFirstFields = make(encodingFields, len(encFlds))
copy(lengthFirstFields, encFlds)
slices.SortFunc(lengthFirstFields, lengthFirstFieldCmp)
}
structType := &encodingStructType{
fields: encFlds,
bytewiseFields: bytewiseFields,
lengthFirstFields: lengthFirstFields,
omitEmptyFieldsIdx: omitEmptyIdx,
}
encodingStructTypeCache.Store(t, structType)
return structType, nil
}
func getEncodingStructToArrayType(t reflect.Type, flds fields, toArray, toIndefArray bool) (*encodingStructType, error) {
encFlds := make(encodingFields, len(flds))
for i, f := range flds {
encFlds[i] = &encodingField{field: *f}
encFlds[i].ef, encFlds[i].ief, encFlds[i].izf = getEncodeFunc(f.typ)
if encFlds[i].ef == nil {
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
encodingStructTypeCache.Store(t, structType)
return nil, structType.err
}
}
structType := &encodingStructType{
fields: encFlds,
toArray: toArray,
toIndefArray: toIndefArray,
}
encodingStructTypeCache.Store(t, structType)
return structType, nil
}
type inProgressEncodeFuncs struct {
encodeFuncs
complete bool
indirectUsed bool
}
func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc, isZeroFunc) {
if v, _ := encodeFuncCache.Load(t); v != nil {
fs := v.(encodeFuncs)
return fs.ef, fs.ief, fs.izf
}
var rebuild bool
newEncodeFuncs := make(map[reflect.Type]*inProgressEncodeFuncs)
for unsupportedTypeCount := 0; ; {
newEncodeFunc(t, newEncodeFuncs)
newEncodeFuncs, rebuild = needRebuild(newEncodeFuncs)
if !rebuild {
break
}
if _, unsupported := newEncodeFuncs[t]; unsupported {
break
}
// Every rebuild must find at least one new unsupported type.
// This check is to ensure termination.
if len(newEncodeFuncs) <= unsupportedTypeCount {
newEncodeFuncs[t] = &inProgressEncodeFuncs{complete: true}
break
}
unsupportedTypeCount = len(newEncodeFuncs)
}
for typ, fs := range newEncodeFuncs {
encodeFuncCache.Store(typ, fs.encodeFuncs)
}
return newEncodeFuncs[t].ef, newEncodeFuncs[t].ief, newEncodeFuncs[t].izf
}
// needRebuild returns true if a type in the inProgressEncodeFuncs has a nil encodeFunc
// and was also indirectly used.
// In other words, rebuild is needed if a type:
// - was used in a closure encodeFunc during type building and
// - was resolved to be unsupported when type building was completed.
// If rebuild is needed, resolved unsupported types are returned as seed for the next rebuilding;
// otherwise, the unmodified newEncodeFuncs is returned.
// NOTE: rebuilding is not needed if all types are supported.
func needRebuild(newEncodeFuncs map[reflect.Type]*inProgressEncodeFuncs) (map[reflect.Type]*inProgressEncodeFuncs, bool) {
rebuild := false
unsupported := make(map[reflect.Type]*inProgressEncodeFuncs)
for typ, fs := range newEncodeFuncs {
if fs.ef == nil {
if fs.indirectUsed {
rebuild = true
}
unsupported[typ] = &inProgressEncodeFuncs{encodeFuncs: fs.encodeFuncs, complete: true}
}
}
if !rebuild {
return newEncodeFuncs, false
}
return unsupported, true
}
func getEncodeFuncWithNewEncodeFuncs(t reflect.Type, newEncodeFuncs map[reflect.Type]*inProgressEncodeFuncs) encodeFunc {
if fs, found := newEncodeFuncs[t]; found {
if fs.complete {
return fs.ef
}
fs.indirectUsed = true
return func(dst []byte, em *encMode, v reflect.Value) ([]byte, error) {
if fs.ef == nil {
return dst, &UnsupportedTypeError{t}
}
return fs.ef(dst, em, v)
}
}
if v, _ := encodeFuncCache.Load(t); v != nil {
fs := v.(encodeFuncs)
return fs.ef
}
ef, _, _ := newEncodeFunc(t, newEncodeFuncs)
return ef
}
func getTypeInfo(t reflect.Type) *typeInfo {
if v, _ := typeInfoCache.Load(t); v != nil {
return v.(*typeInfo)
}
newTypeInfos := make(map[reflect.Type]*typeInfo)
tInfo := newTypeInfo(t, newTypeInfos)
for typ, ti := range newTypeInfos {
typeInfoCache.Store(typ, ti)
}
return tInfo
}
func getTypeInfoWithNewTypeInfos(t reflect.Type, newTypeInfos map[reflect.Type]*typeInfo) *typeInfo {
if tInfo, found := newTypeInfos[t]; found {
return tInfo
}
if v, _ := typeInfoCache.Load(t); v != nil {
return v.(*typeInfo)
}
return newTypeInfo(t, newTypeInfos)
}
func hasToArrayOption(tag string) bool {
s := ",toarray"
idx := strings.Index(tag, s)
return idx >= 0 && (len(tag) == idx+len(s) || tag[idx+len(s)] == ',')
}
func hasToIndefArrayOption(tag string) bool {
s := ",toindefarray"
idx := strings.Index(tag, s)
return idx >= 0 && (len(tag) == idx+len(s) || tag[idx+len(s)] == ',')
}
// parseArrayStructOptions reports whether the struct options request encoding as
// a CBOR array (definite- or indefinite-length), and returns an error if the
// two options are specified together (they are mutually exclusive).
func parseArrayStructOptions(t reflect.Type, structOptions string) (hasToArray, hasToIndefArray bool, err error) {
hasToArray = hasToArrayOption(structOptions)
hasToIndefArray = hasToIndefArrayOption(structOptions)
if hasToArray && hasToIndefArray {
return false, false, fmt.Errorf("cbor: struct %v cannot have both \"toarray\" and \"toindefarray\" options", t)
}
return hasToArray, hasToIndefArray, nil
}