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

go-playground / form / 18239222419

04 Oct 2025 03:51AM UTC coverage: 99.723% (-0.09%) from 99.814%
18239222419

Pull #73

github

Jecoms
Use smaller test counts to reduce CI time while still detecting regression

Changed from (10, 100, 1000) to (10, 50, 200) values.

Since the bug scales exponentially, 200 values is sufficient to prove
the optimization works (without fix: 16s+, with fix: 150ms).

Benefits:
- Test runs in ~1-2s instead of 25-65s on CI with race detector
- Still catches performance regressions (10x+ speedup is obvious)
- More reliable on slow/variable CI runners
- Faster local development feedback

Verified on:
- Go 1.24.5 without race: 0.5ms, 11ms, 152ms ✓
- Go 1.24.5 with race: 3.2ms, 68ms, 916ms ✓
- Go 1.17.13 with race: 3.7ms, 71ms, 1.07s ✓
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.12 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 {
90✔
52
                d.aliasMap = make(map[string]*recursiveData)
35✔
53
        } else {
55✔
54
                for k := range d.aliasMap {
36✔
55
                        delete(d.aliasMap, k)
16✔
56
                }
16✔
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 {
934✔
69
                        d.maxKeyLen = len(k)
93✔
70
                }
93✔
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) {
854✔
90
                                                dm := make(dataMap, l)
419✔
91
                                                copy(dm, d.dm)
419✔
92
                                                rd = new(recursiveData)
419✔
93
                                                dm[len(d.dm)] = rd
419✔
94
                                                d.dm = dm
419✔
95
                                        } else {
435✔
96
                                                l = len(d.dm)
16✔
97
                                                d.dm = d.dm[:l+1]
16✔
98
                                                rd = d.dm[l]
16✔
99
                                                rd.sliceLen = 0
16✔
100
                                                rd.keys = rd.keys[0:0]
16✔
101
                                        }
16✔
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,277✔
127
                                                rd.sliceLen = ke.ivalue
89✔
128

89✔
129
                                        }
89✔
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

© 2026 Coveralls, Inc