add caching of field only tags, now less time, memory and only 2 allocations vs 9

pull/59/head
joeybloggs 10 years ago
parent 22d031deb0
commit e4f2ff67bd
  1. 97
      validator.go

@ -306,7 +306,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
cField.isTime = true cField.isTime = true
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, cField); fieldError != nil { if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference // free up memory reference
fieldError = nil fieldError = nil
@ -331,7 +331,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
default: default:
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, cField); fieldError != nil { if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
validationErrors.Errors[fieldError.Field] = fieldError validationErrors.Errors[fieldError.Field] = fieldError
// free up memory reference // free up memory reference
fieldError = nil fieldError = nil
@ -361,14 +361,17 @@ func (v *Validate) Field(f interface{}, tag string) *FieldError {
// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors // FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError { func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError {
return v.fieldWithNameAndValue(nil, val, f, tag, "", nil) return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil)
} }
func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, cacheField *cachedField) *FieldError { var cacheFields = map[string][]*cacheTags{}
func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError {
// var fieldType reflect.Type // var fieldType reflect.Type
// var fieldKind reflect.Kind // var fieldKind reflect.Kind
var cField *cachedField var cField *cachedField
var ok bool
// This is a double check if coming from validate.Struct but need to be here in case function is called directly // This is a double check if coming from validate.Struct but need to be here in case function is called directly
if tag == noValidationTag { if tag == noValidationTag {
@ -382,14 +385,30 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
if cacheField == nil { if cacheField == nil {
valueField := reflect.ValueOf(f) valueField := reflect.ValueOf(f)
cField = &cachedField{name: name, kind: valueField.Kind()} if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
// valueField = valueField.Elem()
// f = valueField.Interface()
return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, isSingleField, cacheField)
}
// if !ok {
// cacheFields[cField.tag] = cField
// valueField = valueField.Elem()
cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()}
// fieldKind = valueField.Kind() // fieldKind = valueField.Kind()
if cField.kind == reflect.Ptr && !valueField.IsNil() { // if cField.kind == reflect.Ptr && !valueField.IsNil() {
return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, cacheField) // return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, isSingleField, cacheField)
} // }
// cField.typ = valueField.Type()
// cField.tag = tag
cField.typ = valueField.Type() // if isSingleField {
// cacheFields[cField.tag] = cField
// }
// cField.tags = make([][]string, 0) // cField.tags = make([][]string, 0)
// fieldType = valueField.Type() // fieldType = valueField.Type()
// for _, t := range strings.Split(tag, tagSeparator) { // for _, t := range strings.Split(tag, tagSeparator) {
@ -413,6 +432,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
// // vals := strings.Split(valTag, tagKeySeparator) // // vals := strings.Split(valTag, tagKeySeparator)
// // key := strings.TrimSpace(vals[0]) // // key := strings.TrimSpace(vals[0])
// } // }
// }
} else { } else {
cField = cacheField cField = cacheField
// fieldType = cacheField.typ // fieldType = cacheField.typ
@ -429,39 +449,52 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
} }
if len(cField.tags) == 0 { if len(cField.tags) == 0 {
for _, t := range strings.Split(tag, tagSeparator) {
orVals := strings.Split(t, orSeparator) if isSingleField {
// fmt.Println(len(orVals) - 1) cField.tags, ok = cacheFields[tag]
cTag := &cacheTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} }
cField.tags = append(cField.tags, cTag)
for i, val := range orVals { if !ok {
vals := strings.Split(val, tagKeySeparator)
key := strings.TrimSpace(vals[0]) for _, t := range strings.Split(tag, tagSeparator) {
if len(key) == 0 { orVals := strings.Split(t, orSeparator)
panic(fmt.Sprintf("Invalid validation tag on field %s", name)) // fmt.Println(len(orVals) - 1)
} cTag := &cacheTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))}
cField.tags = append(cField.tags, cTag)
param := "" for i, val := range orVals {
if len(vals) > 1 { vals := strings.Split(val, tagKeySeparator)
param = strings.TrimSpace(vals[1])
} key := strings.TrimSpace(vals[0])
// fmt.Println(cTag.keyVals) if len(key) == 0 {
cTag.keyVals[i] = []string{key, param} panic(fmt.Sprintf("Invalid validation tag on field %s", name))
// cTag.keyVals = append(cTag.keyVals, []string{key, param}) }
// for vals := range strings.Split(t, tagKeySeparator) { param := ""
// cField.tags = append(cField.tags, cacheTags{ isOrVal: len(orVals) > 1, []string{key, param}) if len(vals) > 1 {
param = strings.TrimSpace(vals[1])
}
// fmt.Println(cTag.keyVals)
cTag.keyVals[i] = []string{key, param}
// cTag.keyVals = append(cTag.keyVals, []string{key, param})
// for vals := range strings.Split(t, tagKeySeparator) {
// cField.tags = append(cField.tags, cacheTags{ isOrVal: len(orVals) > 1, []string{key, param})
}
// }
// vals := strings.Split(valTag, tagKeySeparator)
// key := strings.TrimSpace(vals[0])
} }
// } if isSingleField && !ok {
// vals := strings.Split(valTag, tagKeySeparator) // fmt.Println(cField.tag)
// key := strings.TrimSpace(vals[0]) cacheFields[cField.tag] = cField.tags
}
} }
} }

Loading…
Cancel
Save