updated tag caching for maximum performance

pull/169/head
joeybloggs 9 years ago
parent b213226b01
commit c293315337
  1. 43
      util.go
  2. 69
      validator.go

@ -232,18 +232,18 @@ func panicIf(err error) {
} }
} }
func (v *Validate) parseTags(tag, fieldName string) []*tagCache { func (v *Validate) parseTags(tag, fieldName string) *cachedTag {
tags, _ := v.parseTagsRecursive(tag, fieldName, blank, false) cTag := &cachedTag{}
return tags
}
func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool) ([]*tagCache, bool) { v.parseTagsRecursive(cTag, tag, fieldName, blank, false)
return cTag
}
tags := []*tagCache{} func (v *Validate) parseTagsRecursive(cTag *cachedTag, tag, fieldName, alias string, isAlias bool) bool {
if len(tag) == 0 { if len(tag) == 0 {
return tags, true return true
} }
for _, t := range strings.Split(tag, tagSeparator) { for _, t := range strings.Split(tag, tagSeparator) {
@ -252,11 +252,10 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool
// check map for alias and process new tags, otherwise process as usual // check map for alias and process new tags, otherwise process as usual
if tagsVal, ok := v.config.aliasValidators[t]; ok { if tagsVal, ok := v.config.aliasValidators[t]; ok {
aliasTags, leave := v.parseTagsRecursive(tagsVal, fieldName, t, true) leave := v.parseTagsRecursive(cTag, tagsVal, fieldName, t, true)
tags = append(tags, aliasTags...)
if leave { if leave {
return tags, leave return leave
} }
continue continue
@ -264,16 +263,20 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool
} }
if t == diveTag { if t == diveTag {
tVals := &tagCache{diveTag: tag, tagVals: [][]string{{t}}} cTag.diveTag = tag
tags = append(tags, tVals) tVals := &tagVals{tagVals: [][]string{{t}}}
return tags, true cTag.tags = append(cTag.tags, tVals)
return true
}
if t == omitempty {
cTag.isOmitEmpty = true
} }
// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C" // if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
orVals := strings.Split(t, orSeparator) orVals := strings.Split(t, orSeparator)
cTag := &tagCache{isAlias: isAlias, isOrVal: len(orVals) > 1, tagVals: make([][]string, len(orVals))} tagVal := &tagVals{isAlias: isAlias, isOrVal: len(orVals) > 1, tagVals: make([][]string, len(orVals))}
cTag.tags = append(cTag.tags, tagVal)
tags = append(tags, cTag)
var key string var key string
var param string var param string
@ -282,10 +285,10 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool
vals := strings.SplitN(val, tagKeySeparator, 2) vals := strings.SplitN(val, tagKeySeparator, 2)
key = vals[0] key = vals[0]
cTag.tag = key tagVal.tag = key
if isAlias { if isAlias {
cTag.tag = alias tagVal.tag = alias
} }
if len(key) == 0 { if len(key) == 0 {
@ -296,9 +299,9 @@ func (v *Validate) parseTagsRecursive(tag, fieldName, alias string, isAlias bool
param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1) param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
} }
cTag.tagVals[i] = []string{key, param} tagVal.tagVals[i] = []string{key, param}
} }
} }
return tags, false return false
} }

@ -41,7 +41,7 @@ var (
timeType = reflect.TypeOf(time.Time{}) timeType = reflect.TypeOf(time.Time{})
timePtrType = reflect.TypeOf(&time.Time{}) timePtrType = reflect.TypeOf(&time.Time{})
errsPool = &sync.Pool{New: newValidationErrors} errsPool = &sync.Pool{New: newValidationErrors}
tagsCache = &tagCacheMap{m: map[string][]*tagCache{}} tagsCache = &tagCacheMap{m: map[string]*cachedTag{}}
emptyStructPtr = new(struct{}) emptyStructPtr = new(struct{})
) )
@ -50,28 +50,32 @@ func newValidationErrors() interface{} {
return ValidationErrors{} return ValidationErrors{}
} }
type tagCache struct { type cachedTag struct {
isOmitEmpty bool
diveTag string
tags []*tagVals
}
type tagVals struct {
tagVals [][]string tagVals [][]string
isOrVal bool isOrVal bool
isAlias bool isAlias bool
tag string tag string
diveTag string
// actualTag string
} }
type tagCacheMap struct { type tagCacheMap struct {
lock sync.RWMutex lock sync.RWMutex
m map[string][]*tagCache m map[string]*cachedTag
} }
func (s *tagCacheMap) Get(key string) ([]*tagCache, bool) { func (s *tagCacheMap) Get(key string) (*cachedTag, bool) {
s.lock.RLock() s.lock.RLock()
defer s.lock.RUnlock() defer s.lock.RUnlock()
value, ok := s.m[key] value, ok := s.m[key]
return value, ok return value, ok
} }
func (s *tagCacheMap) Set(key string, value []*tagCache) { func (s *tagCacheMap) Set(key string, value *cachedTag) {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
s.m[key] = value s.m[key] = value
@ -399,11 +403,11 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
return return
} }
tags, isCached := tagsCache.Get(tag) cTag, isCached := tagsCache.Get(tag)
if !isCached { if !isCached {
tags = v.parseTags(tag, name) cTag = v.parseTags(tag, name)
tagsCache.Set(tag, tags) tagsCache.Set(tag, cTag)
} }
current, kind := v.extractType(current) current, kind := v.extractType(current)
@ -411,7 +415,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
switch kind { switch kind {
case reflect.Ptr, reflect.Interface, reflect.Invalid: case reflect.Ptr, reflect.Interface, reflect.Invalid:
if strings.Contains(tag, omitempty) { if cTag.isOmitEmpty {
return return
} }
@ -420,9 +424,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
if kind == reflect.Invalid { if kind == reflect.Invalid {
errs[errPrefix+name] = &FieldError{ errs[errPrefix+name] = &FieldError{
Field: name, Field: name,
Tag: tags[0].tag, Tag: cTag.tags[0].tag,
ActualTag: tags[0].tagVals[0][0], ActualTag: cTag.tags[0].tagVals[0][0],
Param: tags[0].tagVals[0][1], Param: cTag.tags[0].tagVals[0][1],
Kind: kind, Kind: kind,
} }
return return
@ -430,9 +434,9 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
errs[errPrefix+name] = &FieldError{ errs[errPrefix+name] = &FieldError{
Field: name, Field: name,
Tag: tags[0].tag, Tag: cTag.tags[0].tag,
ActualTag: tags[0].tagVals[0][0], ActualTag: cTag.tags[0].tagVals[0][0],
Param: tags[0].tagVals[0][1], Param: cTag.tags[0].tagVals[0][1],
Value: current.Interface(), Value: current.Interface(),
Kind: kind, Kind: kind,
Type: current.Type(), Type: current.Type(),
@ -471,20 +475,19 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
var dive bool var dive bool
var diveSubTag string var diveSubTag string
for _, cTag := range tags { for _, valTag := range cTag.tags {
if cTag.tagVals[0][0] == existsTag { if valTag.tagVals[0][0] == existsTag {
continue continue
} }
if cTag.tagVals[0][0] == diveTag { if valTag.tagVals[0][0] == diveTag {
dive = true dive = true
// fmt.Println(cTag.diveTag)
diveSubTag = strings.TrimLeft(strings.SplitN(cTag.diveTag, diveTag, 2)[1], ",") diveSubTag = strings.TrimLeft(strings.SplitN(cTag.diveTag, diveTag, 2)[1], ",")
break break
} }
if cTag.tagVals[0][0] == omitempty { if valTag.tagVals[0][0] == omitempty {
if !hasValue(v, topStruct, currentStruct, current, typ, kind, blank) { if !hasValue(v, topStruct, currentStruct, current, typ, kind, blank) {
return return
@ -492,7 +495,7 @@ func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.
continue continue
} }
if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, cTag, name) { if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, valTag, name) {
return return
} }
} }
@ -530,16 +533,16 @@ func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Va
} }
// validateField validates a field based on the provided tag's key and param values and returns true if there is an error or false if all ok // validateField validates a field based on the provided tag's key and param values and returns true if there is an error or false if all ok
func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, cTag *tagCache, name string) bool { func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, valTag *tagVals, name string) bool {
var valFunc Func var valFunc Func
var ok bool var ok bool
if cTag.isOrVal { if valTag.isOrVal {
errTag := blank errTag := blank
for _, val := range cTag.tagVals { for _, val := range valTag.tagVals {
valFunc, ok = v.config.ValidationFuncs[val[0]] valFunc, ok = v.config.ValidationFuncs[val[0]]
if !ok { if !ok {
@ -553,10 +556,10 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.
errTag += orSeparator + val[0] errTag += orSeparator + val[0]
} }
if cTag.isAlias { if valTag.isAlias {
errs[errPrefix+name] = &FieldError{ errs[errPrefix+name] = &FieldError{
Field: name, Field: name,
Tag: cTag.tag, Tag: valTag.tag,
ActualTag: errTag[1:], ActualTag: errTag[1:],
Value: current.Interface(), Value: current.Interface(),
Type: currentType, Type: currentType,
@ -576,21 +579,21 @@ func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.
return true return true
} }
valFunc, ok = v.config.ValidationFuncs[cTag.tagVals[0][0]] valFunc, ok = v.config.ValidationFuncs[valTag.tagVals[0][0]]
if !ok { if !ok {
panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name))) panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
} }
if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, cTag.tagVals[0][1]) { if valFunc(v, topStruct, currentStruct, current, currentType, currentKind, valTag.tagVals[0][1]) {
return false return false
} }
errs[errPrefix+name] = &FieldError{ errs[errPrefix+name] = &FieldError{
Field: name, Field: name,
Tag: cTag.tag, Tag: valTag.tag,
ActualTag: cTag.tagVals[0][0], ActualTag: valTag.tagVals[0][0],
Value: current.Interface(), Value: current.Interface(),
Param: cTag.tagVals[0][1], Param: valTag.tagVals[0][1],
Type: currentType, Type: currentType,
Kind: currentKind, Kind: currentKind,
} }

Loading…
Cancel
Save