diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..8318c84 --- /dev/null +++ b/cache.go @@ -0,0 +1,67 @@ +package validator + +import "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[string]*cachedStruct +} + +func (s *structCacheMap) Get(key string) (*cachedStruct, bool) { + s.lock.RLock() + value, ok := s.m[key] + s.lock.RUnlock() + return value, ok +} + +func (s *structCacheMap) Set(key string, value *cachedStruct) { + s.lock.Lock() + s.m[key] = value + s.lock.Unlock() +} + +type cachedTag struct { + 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() +} diff --git a/util.go b/util.go index 10626bc..3a35fa8 100644 --- a/util.go +++ b/util.go @@ -247,11 +247,62 @@ func panicIf(err error) { } } +func (v *Validate) parseStruct(topStruct reflect.Type, sName string) *cachedStruct { + + s := &cachedStruct{Name: sName, fields: map[int]cachedField{}} + + numFields := topStruct.NumField() + + var fld reflect.StructField + var tag string + var customName string + + for i := 0; i < numFields; i++ { + + fld = topStruct.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(sName, s) + + return s +} + func (v *Validate) parseTags(tag, fieldName string) *cachedTag { cTag := &cachedTag{} v.parseTagsRecursive(cTag, tag, fieldName, blank, false) + + v.tagCache.Set(tag, cTag) + return cTag } diff --git a/validator.go b/validator.go index 4347095..0b13bae 100644 --- a/validator.go +++ b/validator.go @@ -47,39 +47,6 @@ var ( emptyStructPtr = new(struct{}) ) -type cachedTag struct { - 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() -} - // StructLevel contains all of the information and helper methods // for reporting errors during struct level validation type StructLevel struct { @@ -154,7 +121,8 @@ type Validate struct { hasCustomFuncs bool hasAliasValidators bool hasStructLevelFuncs bool - tagsCache *tagCacheMap + tagCache *tagCacheMap + structCache *structCacheMap errsPool *sync.Pool } @@ -227,7 +195,8 @@ func New(config *Config) *Validate { v := &Validate{ tagName: config.TagName, fieldNameTag: config.FieldNameTag, - tagsCache: &tagCacheMap{m: map[string]*cachedTag{}}, + tagCache: &tagCacheMap{m: map[string]*cachedTag{}}, + structCache: &structCacheMap{m: map[string]*cachedStruct{}}, errsPool: &sync.Pool{New: func() interface{} { return ValidationErrors{} }}} @@ -545,11 +514,10 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect. return } - cTag, isCached := v.tagsCache.Get(tag) + cTag, isCached := v.tagCache.Get(tag) if !isCached { cTag = v.parseTags(tag, name) - v.tagsCache.Set(tag, cTag) } current, kind := v.ExtractType(current)