Skip to content

Commit 0794474

Browse files
Dean KarnDean Karn
authored andcommitted
Merge pull request #218 from go-playground/v8-development
Add struct field + associated tags caching
2 parents 638ea8a + 589c2ad commit 0794474

File tree

4 files changed

+221
-98
lines changed

4 files changed

+221
-98
lines changed

README.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -310,34 +310,34 @@ Benchmarks
310310
```go
311311
$ go test -cpu=4 -bench=. -benchmem=true
312312
PASS
313-
BenchmarkFieldSuccess-4 10000000 163 ns/op 0 B/op 0 allocs/op
314-
BenchmarkFieldFailure-4 2000000 673 ns/op 400 B/op 4 allocs/op
315-
BenchmarkFieldDiveSuccess-4 500000 3019 ns/op 480 B/op 27 allocs/op
316-
BenchmarkFieldDiveFailure-4 500000 3553 ns/op 880 B/op 31 allocs/op
317-
BenchmarkFieldCustomTypeSuccess-4 5000000 347 ns/op 32 B/op 2 allocs/op
318-
BenchmarkFieldCustomTypeFailure-4 2000000 645 ns/op 400 B/op 4 allocs/op
319-
BenchmarkFieldOrTagSuccess-4 1000000 1177 ns/op 16 B/op 1 allocs/op
320-
BenchmarkFieldOrTagFailure-4 1000000 1093 ns/op 432 B/op 6 allocs/op
321-
BenchmarkStructLevelValidationSuccess-4 2000000 702 ns/op 160 B/op 6 allocs/op
322-
BenchmarkStructLevelValidationFailure-4 1000000 1279 ns/op 592 B/op 11 allocs/op
323-
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1010 ns/op 80 B/op 5 allocs/op
324-
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1544 ns/op 624 B/op 11 allocs/op
325-
BenchmarkStructPartialSuccess-4 1000000 1249 ns/op 400 B/op 11 allocs/op
326-
BenchmarkStructPartialFailure-4 1000000 1797 ns/op 816 B/op 16 allocs/op
327-
BenchmarkStructExceptSuccess-4 2000000 927 ns/op 368 B/op 9 allocs/op
328-
BenchmarkStructExceptFailure-4 1000000 1259 ns/op 400 B/op 11 allocs/op
329-
BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1076 ns/op 128 B/op 6 allocs/op
330-
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1623 ns/op 560 B/op 11 allocs/op
331-
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1582 ns/op 176 B/op 9 allocs/op
332-
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2139 ns/op 608 B/op 14 allocs/op
333-
BenchmarkStructSimpleSuccess-4 1000000 1040 ns/op 48 B/op 3 allocs/op
334-
BenchmarkStructSimpleFailure-4 1000000 1683 ns/op 624 B/op 11 allocs/op
335-
BenchmarkStructSimpleSuccessParallel-4 5000000 356 ns/op 48 B/op 3 allocs/op
336-
BenchmarkStructSimpleFailureParallel-4 2000000 831 ns/op 624 B/op 11 allocs/op
337-
BenchmarkStructComplexSuccess-4 200000 6738 ns/op 512 B/op 30 allocs/op
338-
BenchmarkStructComplexFailure-4 200000 11387 ns/op 3415 B/op 72 allocs/op
339-
BenchmarkStructComplexSuccessParallel-4 500000 2330 ns/op 512 B/op 30 allocs/op
340-
BenchmarkStructComplexFailureParallel-4 300000 4857 ns/op 3416 B/op 72 allocs/op
313+
BenchmarkFieldSuccess-4 10000000 162 ns/op 0 B/op 0 allocs/op
314+
BenchmarkFieldFailure-4 2000000 678 ns/op 400 B/op 4 allocs/op
315+
BenchmarkFieldDiveSuccess-4 500000 3079 ns/op 480 B/op 27 allocs/op
316+
BenchmarkFieldDiveFailure-4 300000 3584 ns/op 880 B/op 31 allocs/op
317+
BenchmarkFieldCustomTypeSuccess-4 5000000 345 ns/op 32 B/op 2 allocs/op
318+
BenchmarkFieldCustomTypeFailure-4 2000000 650 ns/op 400 B/op 4 allocs/op
319+
BenchmarkFieldOrTagSuccess-4 1000000 1188 ns/op 16 B/op 1 allocs/op
320+
BenchmarkFieldOrTagFailure-4 1000000 1088 ns/op 432 B/op 6 allocs/op
321+
BenchmarkStructLevelValidationSuccess-4 2000000 689 ns/op 160 B/op 6 allocs/op
322+
BenchmarkStructLevelValidationFailure-4 1000000 1290 ns/op 592 B/op 11 allocs/op
323+
BenchmarkStructSimpleCustomTypeSuccess-4 2000000 911 ns/op 80 B/op 5 allocs/op
324+
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1446 ns/op 624 B/op 11 allocs/op
325+
BenchmarkStructPartialSuccess-4 1000000 1221 ns/op 384 B/op 10 allocs/op
326+
BenchmarkStructPartialFailure-4 1000000 1764 ns/op 800 B/op 15 allocs/op
327+
BenchmarkStructExceptSuccess-4 2000000 941 ns/op 336 B/op 7 allocs/op
328+
BenchmarkStructExceptFailure-4 1000000 1237 ns/op 384 B/op 10 allocs/op
329+
BenchmarkStructSimpleCrossFieldSuccess-4 2000000 970 ns/op 128 B/op 6 allocs/op
330+
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1560 ns/op 560 B/op 11 allocs/op
331+
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1542 ns/op 176 B/op 9 allocs/op
332+
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2147 ns/op 608 B/op 14 allocs/op
333+
BenchmarkStructSimpleSuccess-4 2000000 847 ns/op 48 B/op 3 allocs/op
334+
BenchmarkStructSimpleFailure-4 1000000 1497 ns/op 624 B/op 11 allocs/op
335+
BenchmarkStructSimpleSuccessParallel-4 5000000 257 ns/op 48 B/op 3 allocs/op
336+
BenchmarkStructSimpleFailureParallel-4 2000000 586 ns/op 624 B/op 11 allocs/op
337+
BenchmarkStructComplexSuccess-4 300000 5104 ns/op 496 B/op 29 allocs/op
338+
BenchmarkStructComplexFailure-4 200000 9840 ns/op 3400 B/op 71 allocs/op
339+
BenchmarkStructComplexSuccessParallel-4 1000000 1540 ns/op 496 B/op 29 allocs/op
340+
BenchmarkStructComplexFailureParallel-4 500000 3478 ns/op 3400 B/op 71 allocs/op
341341
```
342342

343343
How to Contribute

cache.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package validator
2+
3+
import (
4+
"reflect"
5+
"sync"
6+
)
7+
8+
type cachedField struct {
9+
Idx int
10+
Name string
11+
AltName string
12+
CachedTag *cachedTag
13+
}
14+
15+
type cachedStruct struct {
16+
Name string
17+
fields map[int]cachedField
18+
}
19+
20+
type structCacheMap struct {
21+
lock sync.RWMutex
22+
m map[reflect.Type]*cachedStruct
23+
}
24+
25+
func (s *structCacheMap) Get(key reflect.Type) (*cachedStruct, bool) {
26+
s.lock.RLock()
27+
value, ok := s.m[key]
28+
s.lock.RUnlock()
29+
return value, ok
30+
}
31+
32+
func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
33+
s.lock.Lock()
34+
s.m[key] = value
35+
s.lock.Unlock()
36+
}
37+
38+
type cachedTag struct {
39+
tag string
40+
isOmitEmpty bool
41+
isNoStructLevel bool
42+
isStructOnly bool
43+
diveTag string
44+
tags []*tagVals
45+
}
46+
47+
type tagVals struct {
48+
tagVals [][]string
49+
isOrVal bool
50+
isAlias bool
51+
tag string
52+
}
53+
54+
type tagCacheMap struct {
55+
lock sync.RWMutex
56+
m map[string]*cachedTag
57+
}
58+
59+
func (s *tagCacheMap) Get(key string) (*cachedTag, bool) {
60+
s.lock.RLock()
61+
value, ok := s.m[key]
62+
s.lock.RUnlock()
63+
64+
return value, ok
65+
}
66+
67+
func (s *tagCacheMap) Set(key string, value *cachedTag) {
68+
s.lock.Lock()
69+
s.m[key] = value
70+
s.lock.Unlock()
71+
}

util.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,63 @@ func panicIf(err error) {
247247
}
248248
}
249249

250+
func (v *Validate) parseStruct(current reflect.Value, sName string) *cachedStruct {
251+
252+
typ := current.Type()
253+
s := &cachedStruct{Name: sName, fields: map[int]cachedField{}}
254+
255+
numFields := current.NumField()
256+
257+
var fld reflect.StructField
258+
var tag string
259+
var customName string
260+
261+
for i := 0; i < numFields; i++ {
262+
263+
fld = typ.Field(i)
264+
265+
if len(fld.PkgPath) != 0 {
266+
continue
267+
}
268+
269+
tag = fld.Tag.Get(v.tagName)
270+
271+
if tag == skipValidationTag {
272+
continue
273+
}
274+
275+
customName = fld.Name
276+
if len(v.fieldNameTag) != 0 {
277+
278+
name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
279+
280+
// dash check is for json "-" (aka skipValidationTag) means don't output in json
281+
if name != "" && name != skipValidationTag {
282+
customName = name
283+
}
284+
}
285+
286+
cTag, ok := v.tagCache.Get(tag)
287+
if !ok {
288+
cTag = v.parseTags(tag, fld.Name)
289+
}
290+
291+
s.fields[i] = cachedField{Idx: i, Name: fld.Name, AltName: customName, CachedTag: cTag}
292+
}
293+
294+
v.structCache.Set(typ, s)
295+
296+
return s
297+
}
298+
250299
func (v *Validate) parseTags(tag, fieldName string) *cachedTag {
251300

252-
cTag := &cachedTag{}
301+
cTag := &cachedTag{tag: tag}
253302

254303
v.parseTagsRecursive(cTag, tag, fieldName, blank, false)
304+
305+
v.tagCache.Set(tag, cTag)
306+
255307
return cTag
256308
}
257309

0 commit comments

Comments
 (0)