Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 28 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,34 +310,34 @@ Benchmarks
```go
$ go test -cpu=4 -bench=. -benchmem=true
PASS
BenchmarkFieldSuccess-4 10000000 163 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-4 2000000 673 ns/op 400 B/op 4 allocs/op
BenchmarkFieldDiveSuccess-4 500000 3019 ns/op 480 B/op 27 allocs/op
BenchmarkFieldDiveFailure-4 500000 3553 ns/op 880 B/op 31 allocs/op
BenchmarkFieldCustomTypeSuccess-4 5000000 347 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 2000000 645 ns/op 400 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-4 1000000 1177 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-4 1000000 1093 ns/op 432 B/op 6 allocs/op
BenchmarkStructLevelValidationSuccess-4 2000000 702 ns/op 160 B/op 6 allocs/op
BenchmarkStructLevelValidationFailure-4 1000000 1279 ns/op 592 B/op 11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 1000000 1010 ns/op 80 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1544 ns/op 624 B/op 11 allocs/op
BenchmarkStructPartialSuccess-4 1000000 1249 ns/op 400 B/op 11 allocs/op
BenchmarkStructPartialFailure-4 1000000 1797 ns/op 816 B/op 16 allocs/op
BenchmarkStructExceptSuccess-4 2000000 927 ns/op 368 B/op 9 allocs/op
BenchmarkStructExceptFailure-4 1000000 1259 ns/op 400 B/op 11 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 1000000 1076 ns/op 128 B/op 6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1623 ns/op 560 B/op 11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1582 ns/op 176 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2139 ns/op 608 B/op 14 allocs/op
BenchmarkStructSimpleSuccess-4 1000000 1040 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailure-4 1000000 1683 ns/op 624 B/op 11 allocs/op
BenchmarkStructSimpleSuccessParallel-4 5000000 356 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailureParallel-4 2000000 831 ns/op 624 B/op 11 allocs/op
BenchmarkStructComplexSuccess-4 200000 6738 ns/op 512 B/op 30 allocs/op
BenchmarkStructComplexFailure-4 200000 11387 ns/op 3415 B/op 72 allocs/op
BenchmarkStructComplexSuccessParallel-4 500000 2330 ns/op 512 B/op 30 allocs/op
BenchmarkStructComplexFailureParallel-4 300000 4857 ns/op 3416 B/op 72 allocs/op
BenchmarkFieldSuccess-4 10000000 162 ns/op 0 B/op 0 allocs/op
BenchmarkFieldFailure-4 2000000 678 ns/op 400 B/op 4 allocs/op
BenchmarkFieldDiveSuccess-4 500000 3079 ns/op 480 B/op 27 allocs/op
BenchmarkFieldDiveFailure-4 300000 3584 ns/op 880 B/op 31 allocs/op
BenchmarkFieldCustomTypeSuccess-4 5000000 345 ns/op 32 B/op 2 allocs/op
BenchmarkFieldCustomTypeFailure-4 2000000 650 ns/op 400 B/op 4 allocs/op
BenchmarkFieldOrTagSuccess-4 1000000 1188 ns/op 16 B/op 1 allocs/op
BenchmarkFieldOrTagFailure-4 1000000 1088 ns/op 432 B/op 6 allocs/op
BenchmarkStructLevelValidationSuccess-4 2000000 689 ns/op 160 B/op 6 allocs/op
BenchmarkStructLevelValidationFailure-4 1000000 1290 ns/op 592 B/op 11 allocs/op
BenchmarkStructSimpleCustomTypeSuccess-4 2000000 911 ns/op 80 B/op 5 allocs/op
BenchmarkStructSimpleCustomTypeFailure-4 1000000 1446 ns/op 624 B/op 11 allocs/op
BenchmarkStructPartialSuccess-4 1000000 1221 ns/op 384 B/op 10 allocs/op
BenchmarkStructPartialFailure-4 1000000 1764 ns/op 800 B/op 15 allocs/op
BenchmarkStructExceptSuccess-4 2000000 941 ns/op 336 B/op 7 allocs/op
BenchmarkStructExceptFailure-4 1000000 1237 ns/op 384 B/op 10 allocs/op
BenchmarkStructSimpleCrossFieldSuccess-4 2000000 970 ns/op 128 B/op 6 allocs/op
BenchmarkStructSimpleCrossFieldFailure-4 1000000 1560 ns/op 560 B/op 11 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldSuccess-4 1000000 1542 ns/op 176 B/op 9 allocs/op
BenchmarkStructSimpleCrossStructCrossFieldFailure-4 1000000 2147 ns/op 608 B/op 14 allocs/op
BenchmarkStructSimpleSuccess-4 2000000 847 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailure-4 1000000 1497 ns/op 624 B/op 11 allocs/op
BenchmarkStructSimpleSuccessParallel-4 5000000 257 ns/op 48 B/op 3 allocs/op
BenchmarkStructSimpleFailureParallel-4 2000000 586 ns/op 624 B/op 11 allocs/op
BenchmarkStructComplexSuccess-4 300000 5104 ns/op 496 B/op 29 allocs/op
BenchmarkStructComplexFailure-4 200000 9840 ns/op 3400 B/op 71 allocs/op
BenchmarkStructComplexSuccessParallel-4 1000000 1540 ns/op 496 B/op 29 allocs/op
BenchmarkStructComplexFailureParallel-4 500000 3478 ns/op 3400 B/op 71 allocs/op
```

How to Contribute
Expand Down
71 changes: 71 additions & 0 deletions cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package validator

import (
"reflect"
"sync"
)

type cachedField struct {
Idx int
Name string
AltName string
CachedTag *cachedTag
}

type cachedStruct struct {
Name string
fields map[int]cachedField
}

type structCacheMap struct {
lock sync.RWMutex
m map[reflect.Type]*cachedStruct
}

func (s *structCacheMap) Get(key reflect.Type) (*cachedStruct, bool) {
s.lock.RLock()
value, ok := s.m[key]
s.lock.RUnlock()
return value, ok
}

func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
s.lock.Lock()
s.m[key] = value
s.lock.Unlock()
}

type cachedTag struct {
tag string
isOmitEmpty bool
isNoStructLevel bool
isStructOnly bool
diveTag string
tags []*tagVals
}

type tagVals struct {
tagVals [][]string
isOrVal bool
isAlias bool
tag string
}

type tagCacheMap struct {
lock sync.RWMutex
m map[string]*cachedTag
}

func (s *tagCacheMap) Get(key string) (*cachedTag, bool) {
s.lock.RLock()
value, ok := s.m[key]
s.lock.RUnlock()

return value, ok
}

func (s *tagCacheMap) Set(key string, value *cachedTag) {
s.lock.Lock()
s.m[key] = value
s.lock.Unlock()
}
54 changes: 53 additions & 1 deletion util.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,63 @@ func panicIf(err error) {
}
}

func (v *Validate) parseStruct(current reflect.Value, sName string) *cachedStruct {

typ := current.Type()
s := &cachedStruct{Name: sName, fields: map[int]cachedField{}}

numFields := current.NumField()

var fld reflect.StructField
var tag string
var customName string

for i := 0; i < numFields; i++ {

fld = typ.Field(i)

if len(fld.PkgPath) != 0 {
continue
}

tag = fld.Tag.Get(v.tagName)

if tag == skipValidationTag {
continue
}

customName = fld.Name
if len(v.fieldNameTag) != 0 {

name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]

// dash check is for json "-" (aka skipValidationTag) means don't output in json
if name != "" && name != skipValidationTag {
customName = name
}
}

cTag, ok := v.tagCache.Get(tag)
if !ok {
cTag = v.parseTags(tag, fld.Name)
}

s.fields[i] = cachedField{Idx: i, Name: fld.Name, AltName: customName, CachedTag: cTag}
}

v.structCache.Set(typ, s)

return s
}

func (v *Validate) parseTags(tag, fieldName string) *cachedTag {

cTag := &cachedTag{}
cTag := &cachedTag{tag: tag}

v.parseTagsRecursive(cTag, tag, fieldName, blank, false)

v.tagCache.Set(tag, cTag)

return cTag
}

Expand Down
Loading