• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-playground / form / 18267045333

06 Oct 2025 01:03AM UTC coverage: 99.723% (-0.09%) from 99.814%
18267045333

Pull #73

github

web-flow
Improve nested structure decoding performance

Optimize nested structure decoding by replacing O(n) linear search
with O(1) hash map lookup in the findAlias function.

Changes:
- Add aliasMap (map[string]*recursiveData) to decoder struct for O(1) alias lookups
- Initialize and clear aliasMap in parseMapData
- Replace linear search loop in findAlias with direct map lookup
- Add performance regression tests for nested array decoding (10, 50, 200 elements)
- Add benchmark tests for 100 and 1000 nested elements
- Support both normal and race detector modes with appropriate thresholds

Performance improvements:
- 100 nested elements: ~28% faster (55ms -> 39ms)
- 1000 nested elements: ~3.6x faster (16.9s -> 4.7s)

The fix scales linearly instead of exponentially with nested structure depth.

Improves #71
Pull Request #73: Fix issue #71: Optimize nested structure decoding performance

9 of 9 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

1080 of 1083 relevant lines covered (99.72%)

43077.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.83
/decoder.go
1
package form
2

3
import (
4
        "fmt"
5
        "log"
6
        "net/url"
7
        "reflect"
8
        "strconv"
9
        "time"
10
)
11

12
const (
13
        errArraySize           = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)"
14
        errMissingStartBracket = "Invalid formatting for key '%s' missing '[' bracket"
15
        errMissingEndBracket   = "Invalid formatting for key '%s' missing ']' bracket"
16
)
17

18
type decoder struct {
19
        d         *Decoder
20
        errs      DecodeErrors
21
        dm        dataMap
22
        aliasMap  map[string]*recursiveData
23
        values    url.Values
24
        maxKeyLen int
25
        namespace []byte
26
}
27

28
func (d *decoder) setError(namespace []byte, err error) {
64✔
29
        if d.errs == nil {
70✔
30
                d.errs = make(DecodeErrors)
6✔
31
        }
6✔
32
        d.errs[string(namespace)] = err
64✔
33
}
34

35
func (d *decoder) findAlias(ns string) *recursiveData {
343,042✔
36
        if d.aliasMap != nil {
686,084✔
37
                return d.aliasMap[ns]
343,042✔
38
        }
343,042✔
UNCOV
39
        return nil
×
40
}
41

42
func (d *decoder) parseMapData() {
512,488✔
43
        // already parsed
512,488✔
44
        if len(d.dm) > 0 {
1,024,921✔
45
                return
512,433✔
46
        }
512,433✔
47

48
        d.maxKeyLen = 0
55✔
49
        d.dm = d.dm[0:0]
55✔
50

55✔
51
        if d.aliasMap == nil {
89✔
52
                d.aliasMap = make(map[string]*recursiveData)
34✔
53
        } else {
55✔
54
                for k := range d.aliasMap {
90✔
55
                        delete(d.aliasMap, k)
69✔
56
                }
69✔
57
        }
58

59
        var i int
55✔
60
        var idx int
55✔
61
        var l int
55✔
62
        var insideBracket bool
55✔
63
        var rd *recursiveData
55✔
64
        var isNum bool
55✔
65

55✔
66
        for k := range d.values {
896✔
67

841✔
68
                if len(k) > d.maxKeyLen {
938✔
69
                        d.maxKeyLen = len(k)
97✔
70
                }
97✔
71

72
                for i = 0; i < len(k); i++ {
17,577✔
73

16,736✔
74
                        switch k[i] {
16,736✔
75
                        case '[':
1,529✔
76
                                idx = i
1,529✔
77
                                insideBracket = true
1,529✔
78
                                isNum = true
1,529✔
79
                        case ']':
1,528✔
80

1,528✔
81
                                if !insideBracket {
1,530✔
82
                                        log.Panicf(errMissingStartBracket, k)
2✔
83
                                }
2✔
84

85
                                if rd = d.findAlias(k[:idx]); rd == nil {
1,961✔
86

435✔
87
                                        l = len(d.dm) + 1
435✔
88

435✔
89
                                        if l > cap(d.dm) {
801✔
90
                                                dm := make(dataMap, l)
366✔
91
                                                copy(dm, d.dm)
366✔
92
                                                rd = new(recursiveData)
366✔
93
                                                dm[len(d.dm)] = rd
366✔
94
                                                d.dm = dm
366✔
95
                                        } else {
435✔
96
                                                l = len(d.dm)
69✔
97
                                                d.dm = d.dm[:l+1]
69✔
98
                                                rd = d.dm[l]
69✔
99
                                                rd.sliceLen = 0
69✔
100
                                                rd.keys = rd.keys[0:0]
69✔
101
                                        }
69✔
102

103
                                        rd.alias = k[:idx]
435✔
104
                                        d.aliasMap[rd.alias] = rd
435✔
105
                                }
106

107
                                // is map + key
108
                                ke := key{
1,526✔
109
                                        ivalue:      -1,
1,526✔
110
                                        value:       k[idx+1 : i],
1,526✔
111
                                        searchValue: k[idx : i+1],
1,526✔
112
                                }
1,526✔
113

1,526✔
114
                                // is key is number, most likely array key, keep track of just in case an array/slice.
1,526✔
115
                                if isNum {
2,714✔
116

1,188✔
117
                                        // no need to check for error, it will always pass
1,188✔
118
                                        // as we have done the checking to ensure
1,188✔
119
                                        // the value is a number ahead of time.
1,188✔
120
                                        var err error
1,188✔
121
                                        ke.ivalue, err = strconv.Atoi(ke.value)
1,188✔
122
                                        if err != nil {
1,190✔
123
                                                ke.ivalue = -1
2✔
124
                                        }
2✔
125

126
                                        if ke.ivalue > rd.sliceLen {
1,276✔
127
                                                rd.sliceLen = ke.ivalue
88✔
128

88✔
129
                                        }
88✔
130
                                }
131

132
                                rd.keys = append(rd.keys, ke)
1,526✔
133

1,526✔
134
                                insideBracket = false
1,526✔
135
                        default:
13,679✔
136
                                // checking if not a number, 0-9 is 48-57 in byte, see for yourself fmt.Println('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
13,679✔
137
                                if insideBracket && (k[i] > 57 || k[i] < 48) {
14,278✔
138
                                        isNum = false
599✔
139
                                }
599✔
140
                        }
141
                }
142

143
                // if still inside bracket, that means no ending bracket was ever specified
144
                if insideBracket {
841✔
145
                        log.Panicf(errMissingEndBracket, k)
2✔
146
                }
2✔
147
        }
148
}
149

150
func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []byte) (set bool) {
171,008✔
151

171,008✔
152
        l := len(namespace)
171,008✔
153
        first := l == 0
171,008✔
154

171,008✔
155
        // anonymous structs will still work for caching as the whole definition is stored
171,008✔
156
        // including tags
171,008✔
157
        s, ok := d.d.structCache.Get(typ)
171,008✔
158
        if !ok {
171,060✔
159
                s = d.d.structCache.parseStruct(d.d.mode, v, typ, d.d.tagName)
52✔
160
        }
52✔
161

162
        for _, f := range s.fields {
512,662✔
163
                namespace = namespace[:l]
341,654✔
164

341,654✔
165
                if f.isAnonymous {
341,656✔
166
                        if d.setFieldByType(v.Field(f.idx), namespace, 0) {
4✔
167
                                set = true
2✔
168
                        }
2✔
169
                }
170

171
                if first {
341,912✔
172
                        namespace = append(namespace, f.name...)
258✔
173
                } else {
341,654✔
174
                        namespace = append(namespace, d.d.namespacePrefix...)
341,396✔
175
                        namespace = append(namespace, f.name...)
341,396✔
176
                        namespace = append(namespace, d.d.namespaceSuffix...)
341,396✔
177
                }
341,396✔
178

179
                if d.setFieldByType(v.Field(f.idx), namespace, 0) {
683,184✔
180
                        set = true
341,530✔
181
                }
341,530✔
182
        }
183

184
        return
171,004✔
185
}
186

187
func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx int) (set bool) {
1,024,644✔
188

1,024,644✔
189
        var err error
1,024,644✔
190
        v, kind := ExtractType(current)
1,024,644✔
191

1,024,644✔
192
        arr, ok := d.values[string(namespace)]
1,024,644✔
193

1,024,644✔
194
        if d.d.customTypeFuncs != nil {
1,024,856✔
195

212✔
196
                if ok {
320✔
197
                        if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
117✔
198
                                val, err := cf(arr[idx:])
9✔
199
                                if err != nil {
11✔
200
                                        d.setError(namespace, err)
2✔
201
                                        return
2✔
202
                                }
2✔
203

204
                                v.Set(reflect.ValueOf(val))
7✔
205
                                set = true
7✔
206
                                return
7✔
207
                        }
208
                }
209
        }
210
        switch kind {
1,024,635✔
211
        case reflect.Interface:
5✔
212
                if !ok || idx == len(arr) {
9✔
213
                        return
4✔
214
                }
4✔
215
                v.Set(reflect.ValueOf(arr[idx]))
1✔
216
                set = true
1✔
217

218
        case reflect.Ptr:
171,004✔
219
                newVal := reflect.New(v.Type().Elem())
171,004✔
220
                if set = d.setFieldByType(newVal.Elem(), namespace, idx); set {
341,995✔
221
                        v.Set(newVal)
170,991✔
222
                }
170,991✔
223

224
        case reflect.String:
340,940✔
225
                if !ok || idx == len(arr) {
340,957✔
226
                        return
17✔
227
                }
17✔
228
                v.SetString(arr[idx])
340,923✔
229
                set = true
340,923✔
230

231
        case reflect.Uint, reflect.Uint64:
26✔
232
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
29✔
233
                        return
3✔
234
                }
3✔
235
                var u64 uint64
23✔
236
                if u64, err = strconv.ParseUint(arr[idx], 10, 64); err != nil {
25✔
237
                        d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
238
                        return
2✔
239
                }
2✔
240
                v.SetUint(u64)
21✔
241
                set = true
21✔
242

243
        case reflect.Uint8:
9✔
244
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
10✔
245
                        return
1✔
246
                }
1✔
247
                var u64 uint64
8✔
248
                if u64, err = strconv.ParseUint(arr[idx], 10, 8); err != nil {
10✔
249
                        d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
250
                        return
2✔
251
                }
2✔
252
                v.SetUint(u64)
6✔
253
                set = true
6✔
254

255
        case reflect.Uint16:
9✔
256
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
10✔
257
                        return
1✔
258
                }
1✔
259
                var u64 uint64
8✔
260
                if u64, err = strconv.ParseUint(arr[idx], 10, 16); err != nil {
10✔
261
                        d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
262
                        return
2✔
263
                }
2✔
264
                v.SetUint(u64)
6✔
265
                set = true
6✔
266

267
        case reflect.Uint32:
9✔
268
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
10✔
269
                        return
1✔
270
                }
1✔
271
                var u64 uint64
8✔
272
                if u64, err = strconv.ParseUint(arr[idx], 10, 32); err != nil {
10✔
273
                        d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
274
                        return
2✔
275
                }
2✔
276
                v.SetUint(u64)
6✔
277
                set = true
6✔
278

279
        case reflect.Int, reflect.Int64:
49✔
280
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
52✔
281
                        return
3✔
282
                }
3✔
283
                var i64 int64
46✔
284
                if i64, err = strconv.ParseInt(arr[idx], 10, 64); err != nil {
50✔
285
                        d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
4✔
286
                        return
4✔
287
                }
4✔
288
                v.SetInt(i64)
42✔
289
                set = true
42✔
290

291
        case reflect.Int8:
9✔
292
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
10✔
293
                        return
1✔
294
                }
1✔
295
                var i64 int64
8✔
296
                if i64, err = strconv.ParseInt(arr[idx], 10, 8); err != nil {
10✔
297
                        d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
298
                        return
2✔
299
                }
2✔
300
                v.SetInt(i64)
6✔
301
                set = true
6✔
302

303
        case reflect.Int16:
9✔
304
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
10✔
305
                        return
1✔
306
                }
1✔
307
                var i64 int64
8✔
308
                if i64, err = strconv.ParseInt(arr[idx], 10, 16); err != nil {
10✔
309
                        d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
310
                        return
2✔
311
                }
2✔
312
                v.SetInt(i64)
6✔
313
                set = true
6✔
314

315
        case reflect.Int32:
9✔
316
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
10✔
317
                        return
1✔
318
                }
1✔
319
                var i64 int64
8✔
320
                if i64, err = strconv.ParseInt(arr[idx], 10, 32); err != nil {
10✔
321
                        d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
322
                        return
2✔
323
                }
2✔
324
                v.SetInt(i64)
6✔
325
                set = true
6✔
326

327
        case reflect.Float32:
27✔
328
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
28✔
329
                        return
1✔
330
                }
1✔
331
                var f float64
26✔
332
                if f, err = strconv.ParseFloat(arr[idx], 32); err != nil {
28✔
333
                        d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
334
                        return
2✔
335
                }
2✔
336
                v.SetFloat(f)
24✔
337
                set = true
24✔
338

339
        case reflect.Float64:
10✔
340
                if !ok || idx == len(arr) || len(arr[idx]) == 0 {
11✔
341
                        return
1✔
342
                }
1✔
343
                var f float64
9✔
344
                if f, err = strconv.ParseFloat(arr[idx], 64); err != nil {
11✔
345
                        d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
346
                        return
2✔
347
                }
2✔
348
                v.SetFloat(f)
7✔
349
                set = true
7✔
350

351
        case reflect.Bool:
26✔
352
                if !ok || idx == len(arr) {
29✔
353
                        return
3✔
354
                }
3✔
355
                var b bool
23✔
356
                if b, err = parseBool(arr[idx]); err != nil {
25✔
357
                        d.setError(namespace, fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
2✔
358
                        return
2✔
359
                }
2✔
360
                v.SetBool(b)
21✔
361
                set = true
21✔
362

363
        case reflect.Slice:
171,020✔
364
                d.parseMapData()
171,020✔
365
                // slice elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}
171,020✔
366

171,020✔
367
                if ok && len(arr) > 0 {
341,439✔
368
                        var varr reflect.Value
170,419✔
369

170,419✔
370
                        var ol int
170,419✔
371
                        l := len(arr)
170,419✔
372

170,419✔
373
                        if v.IsNil() {
340,825✔
374
                                varr = reflect.MakeSlice(v.Type(), len(arr), len(arr))
170,406✔
375
                        } else {
170,419✔
376

13✔
377
                                ol = v.Len()
13✔
378
                                l += ol
13✔
379

13✔
380
                                if v.Cap() <= l {
24✔
381
                                        varr = reflect.MakeSlice(v.Type(), l, l)
11✔
382
                                } else {
13✔
383
                                        // preserve predefined capacity, possibly for reuse after decoding
2✔
384
                                        varr = reflect.MakeSlice(v.Type(), l, v.Cap())
2✔
385
                                }
2✔
386
                                reflect.Copy(varr, v)
13✔
387
                        }
388

389
                        for i := ol; i < l; i++ {
340,860✔
390
                                newVal := reflect.New(v.Type().Elem()).Elem()
170,441✔
391

170,441✔
392
                                if d.setFieldByType(newVal, namespace, i-ol) {
340,882✔
393
                                        set = true
170,441✔
394
                                        varr.Index(i).Set(newVal)
170,441✔
395
                                }
170,441✔
396
                        }
397

398
                        v.Set(varr)
170,419✔
399
                }
400

401
                // maybe it's an numbered array i.e. Phone[0].Number
402
                if rd := d.findAlias(string(namespace)); rd != nil {
171,611✔
403

595✔
404
                        var varr reflect.Value
595✔
405
                        var kv key
595✔
406

595✔
407
                        sl := rd.sliceLen + 1
595✔
408

595✔
409
                        // checking below for maxArraySize, but if array exists and already
595✔
410
                        // has sufficient capacity allocated then we do not check as the code
595✔
411
                        // obviously allows a capacity greater than the maxArraySize.
595✔
412

595✔
413
                        if v.IsNil() {
1,179✔
414

584✔
415
                                if sl > d.d.maxArraySize {
586✔
416
                                        d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
2✔
417
                                        return
2✔
418
                                }
2✔
419

420
                                varr = reflect.MakeSlice(v.Type(), sl, sl)
582✔
421

422
                        } else if v.Len() < sl {
19✔
423

8✔
424
                                if v.Cap() <= sl {
14✔
425

6✔
426
                                        if sl > d.d.maxArraySize {
8✔
427
                                                d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
2✔
428
                                                return
2✔
429
                                        }
2✔
430

431
                                        varr = reflect.MakeSlice(v.Type(), sl, sl)
4✔
432
                                } else {
2✔
433
                                        varr = reflect.MakeSlice(v.Type(), sl, v.Cap())
2✔
434
                                }
2✔
435

436
                                reflect.Copy(varr, v)
6✔
437

438
                        } else {
3✔
439
                                varr = v
3✔
440
                        }
3✔
441

442
                        for i := 0; i < len(rd.keys); i++ {
171,634✔
443

171,043✔
444
                                kv = rd.keys[i]
171,043✔
445
                                newVal := reflect.New(varr.Type().Elem()).Elem()
171,043✔
446

171,043✔
447
                                if kv.ivalue == -1 {
171,046✔
448
                                        d.setError(namespace, fmt.Errorf("invalid slice index '%s'", kv.value))
3✔
449
                                        continue
3✔
450
                                }
451

452
                                if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
342,078✔
453
                                        set = true
171,038✔
454
                                        varr.Index(kv.ivalue).Set(newVal)
171,038✔
455
                                }
171,038✔
456
                        }
457

458
                        if !set {
596✔
459
                                return
5✔
460
                        }
5✔
461

462
                        v.Set(varr)
586✔
463
                }
464

465
        case reflect.Array:
7✔
466
                d.parseMapData()
7✔
467

7✔
468
                // array elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}
7✔
469

7✔
470
                if ok && len(arr) > 0 {
13✔
471
                        var varr reflect.Value
6✔
472
                        l := len(arr)
6✔
473
                        overCapacity := v.Len() < l
6✔
474
                        if overCapacity {
7✔
475
                                // more values than array capacity, ignore values over capacity as it's possible some would just want
1✔
476
                                // to grab the first x number of elements; in the future strict mode logic should return an error
1✔
477
                                fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
1✔
478
                        }
1✔
479
                        varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
6✔
480
                        reflect.Copy(varr, v)
6✔
481

6✔
482
                        if v.Len() < len(arr) {
7✔
483
                                l = v.Len()
1✔
484
                        }
1✔
485
                        for i := 0; i < l; i++ {
16✔
486
                                newVal := reflect.New(v.Type().Elem()).Elem()
10✔
487

10✔
488
                                if d.setFieldByType(newVal, namespace, i) {
20✔
489
                                        set = true
10✔
490
                                        varr.Index(i).Set(newVal)
10✔
491
                                }
10✔
492
                        }
493
                        v.Set(varr)
6✔
494
                }
495

496
                // maybe it's an numbered array i.e. Phone[0].Number
497
                if rd := d.findAlias(string(namespace)); rd != nil {
11✔
498
                        var varr reflect.Value
4✔
499
                        var kv key
4✔
500

4✔
501
                        overCapacity := rd.sliceLen >= v.Len()
4✔
502
                        if overCapacity {
5✔
503
                                // more values than array capacity, ignore values over capacity as it's possible some would just want
1✔
504
                                // to grab the first x number of elements; in the future strict mode logic should return an error
1✔
505
                                fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
1✔
506
                        }
1✔
507
                        varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
4✔
508
                        reflect.Copy(varr, v)
4✔
509

4✔
510
                        for i := 0; i < len(rd.keys); i++ {
9✔
511
                                kv = rd.keys[i]
5✔
512
                                if kv.ivalue >= v.Len() {
6✔
513
                                        continue
1✔
514
                                }
515
                                newVal := reflect.New(varr.Type().Elem()).Elem()
4✔
516

4✔
517
                                if kv.ivalue == -1 {
5✔
518
                                        d.setError(namespace, fmt.Errorf("invalid array index '%s'", kv.value))
1✔
519
                                        continue
1✔
520
                                }
521

522
                                if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
6✔
523
                                        set = true
3✔
524
                                        varr.Index(kv.ivalue).Set(newVal)
3✔
525
                                }
3✔
526
                        }
527

528
                        if !set {
5✔
529
                                return
1✔
530
                        }
1✔
531
                        v.Set(varr)
3✔
532
                }
533

534
        case reflect.Map:
170,493✔
535
                var rd *recursiveData
170,493✔
536

170,493✔
537
                d.parseMapData()
170,493✔
538

170,493✔
539
                // no natural map support so skip directly to dm lookup
170,493✔
540
                if rd = d.findAlias(string(namespace)); rd == nil {
170,495✔
541
                        return
2✔
542
                }
2✔
543

544
                var existing bool
170,491✔
545
                var kv key
170,491✔
546
                var mp reflect.Value
170,491✔
547
                var mk reflect.Value
170,491✔
548

170,491✔
549
                typ := v.Type()
170,491✔
550

170,491✔
551
                if v.IsNil() {
340,968✔
552
                        mp = reflect.MakeMap(typ)
170,477✔
553
                } else {
170,491✔
554
                        existing = true
14✔
555
                        mp = v
14✔
556
                }
14✔
557

558
                for i := 0; i < len(rd.keys); i++ {
340,984✔
559
                        newVal := reflect.New(typ.Elem()).Elem()
170,493✔
560
                        mk = reflect.New(typ.Key()).Elem()
170,493✔
561
                        kv = rd.keys[i]
170,493✔
562

170,493✔
563
                        if err := d.getMapKey(kv.value, mk, namespace); err != nil {
170,521✔
564
                                d.setError(namespace, err)
28✔
565
                                continue
28✔
566
                        }
567

568
                        if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
340,930✔
569
                                set = true
170,465✔
570
                                mp.SetMapIndex(mk, newVal)
170,465✔
571
                        }
170,465✔
572
                }
573

574
                if !set || existing {
170,533✔
575
                        return
42✔
576
                }
42✔
577

578
                v.Set(mp)
170,449✔
579

580
        case reflect.Struct:
170,974✔
581
                typ := v.Type()
170,974✔
582

170,974✔
583
                // if we get here then no custom time function declared so use RFC3339 by default
170,974✔
584
                if typ == timeType {
170,980✔
585

6✔
586
                        if !ok || len(arr[idx]) == 0 {
7✔
587
                                return
1✔
588
                        }
1✔
589

590
                        t, err := time.Parse(time.RFC3339, arr[idx])
5✔
591
                        if err != nil {
7✔
592
                                d.setError(namespace, err)
2✔
593
                        }
2✔
594

595
                        v.Set(reflect.ValueOf(t))
5✔
596
                        set = true
5✔
597
                        return
5✔
598
                }
599

600
                d.parseMapData()
170,968✔
601

170,968✔
602
                // we must be recursing infinitly...but that's ok we caught it on the very first overun.
170,968✔
603
                if len(namespace) > d.maxKeyLen {
170,974✔
604
                        return
6✔
605
                }
6✔
606

607
                set = d.traverseStruct(v, typ, namespace)
170,962✔
608
        }
609
        return
1,024,503✔
610
}
611

612
func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte) (err error) {
170,498✔
613

170,498✔
614
        v, kind := ExtractType(current)
170,498✔
615

170,498✔
616
        if d.d.customTypeFuncs != nil {
170,552✔
617
                if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
58✔
618

4✔
619
                        val, er := cf([]string{key})
4✔
620
                        if er != nil {
6✔
621
                                err = er
2✔
622
                                return
2✔
623
                        }
2✔
624

625
                        v.Set(reflect.ValueOf(val))
2✔
626
                        return
2✔
627
                }
628
        }
629

630
        switch kind {
170,494✔
631
        case reflect.Interface:
4✔
632
                // If interface would have been set on the struct before decoding,
4✔
633
                // say to a struct value we would not get here but kind would be struct.
4✔
634
                v.Set(reflect.ValueOf(key))
4✔
635
                return
4✔
636
        case reflect.Ptr:
5✔
637
                newVal := reflect.New(v.Type().Elem())
5✔
638
                if err = d.getMapKey(key, newVal.Elem(), namespace); err == nil {
10✔
639
                        v.Set(newVal)
5✔
640
                }
5✔
641

642
        case reflect.String:
170,411✔
643
                v.SetString(key)
170,411✔
644

645
        case reflect.Uint, reflect.Uint64:
4✔
646

4✔
647
                u64, e := strconv.ParseUint(key, 10, 64)
4✔
648
                if e != nil {
6✔
649
                        err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
650
                        return
2✔
651
                }
2✔
652

653
                v.SetUint(u64)
2✔
654

655
        case reflect.Uint8:
6✔
656

6✔
657
                u64, e := strconv.ParseUint(key, 10, 8)
6✔
658
                if e != nil {
8✔
659
                        err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
660
                        return
2✔
661
                }
2✔
662

663
                v.SetUint(u64)
4✔
664

665
        case reflect.Uint16:
6✔
666

6✔
667
                u64, e := strconv.ParseUint(key, 10, 16)
6✔
668
                if e != nil {
8✔
669
                        err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
670
                        return
2✔
671
                }
2✔
672

673
                v.SetUint(u64)
4✔
674

675
        case reflect.Uint32:
6✔
676

6✔
677
                u64, e := strconv.ParseUint(key, 10, 32)
6✔
678
                if e != nil {
8✔
679
                        err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
680
                        return
2✔
681
                }
2✔
682

683
                v.SetUint(u64)
4✔
684

685
        case reflect.Int, reflect.Int64:
12✔
686

12✔
687
                i64, e := strconv.ParseInt(key, 10, 64)
12✔
688
                if e != nil {
14✔
689
                        err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
690
                        return
2✔
691
                }
2✔
692

693
                v.SetInt(i64)
10✔
694

695
        case reflect.Int8:
6✔
696

6✔
697
                i64, e := strconv.ParseInt(key, 10, 8)
6✔
698
                if e != nil {
8✔
699
                        err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
700
                        return
2✔
701
                }
2✔
702

703
                v.SetInt(i64)
4✔
704

705
        case reflect.Int16:
6✔
706

6✔
707
                i64, e := strconv.ParseInt(key, 10, 16)
6✔
708
                if e != nil {
8✔
709
                        err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
710
                        return
2✔
711
                }
2✔
712

713
                v.SetInt(i64)
4✔
714

715
        case reflect.Int32:
6✔
716

6✔
717
                i64, e := strconv.ParseInt(key, 10, 32)
6✔
718
                if e != nil {
8✔
719
                        err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
720
                        return
2✔
721
                }
2✔
722

723
                v.SetInt(i64)
4✔
724

725
        case reflect.Float32:
8✔
726

8✔
727
                f, e := strconv.ParseFloat(key, 32)
8✔
728
                if e != nil {
10✔
729
                        err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
730
                        return
2✔
731
                }
2✔
732

733
                v.SetFloat(f)
6✔
734

735
        case reflect.Float64:
6✔
736

6✔
737
                f, e := strconv.ParseFloat(key, 64)
6✔
738
                if e != nil {
8✔
739
                        err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
740
                        return
2✔
741
                }
2✔
742

743
                v.SetFloat(f)
4✔
744

745
        case reflect.Bool:
4✔
746

4✔
747
                b, e := parseBool(key)
4✔
748
                if e != nil {
6✔
749
                        err = fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
2✔
750
                        return
2✔
751
                }
2✔
752

753
                v.SetBool(b)
2✔
754

755
        default:
4✔
756
                err = fmt.Errorf("Unsupported Map Key '%s', Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
4✔
757
        }
758

759
        return
170,468✔
760
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc