From e4f2ff67bd457930b9d224e9e05f5d7194bae7d2 Mon Sep 17 00:00:00 2001 From: joeybloggs Date: Sun, 7 Jun 2015 22:24:26 -0400 Subject: [PATCH] add caching of field only tags, now less time, memory and only 2 allocations vs 9 --- validator.go | 97 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 32 deletions(-) diff --git a/validator.go b/validator.go index 2bf65b5..bb27feb 100644 --- a/validator.go +++ b/validator.go @@ -306,7 +306,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter 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 // free up memory reference fieldError = nil @@ -331,7 +331,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter 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 // free up memory reference 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 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 fieldKind reflect.Kind 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 if tag == noValidationTag { @@ -382,14 +385,30 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f if cacheField == nil { 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() - if cField.kind == reflect.Ptr && !valueField.IsNil() { - return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, cacheField) - } + // if cField.kind == reflect.Ptr && !valueField.IsNil() { + // 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) // fieldType = valueField.Type() // 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) // // key := strings.TrimSpace(vals[0]) // } + // } } else { cField = cacheField // fieldType = cacheField.typ @@ -429,39 +449,52 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f } if len(cField.tags) == 0 { - for _, t := range strings.Split(tag, tagSeparator) { - orVals := strings.Split(t, orSeparator) - // fmt.Println(len(orVals) - 1) - cTag := &cacheTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} - cField.tags = append(cField.tags, cTag) + if isSingleField { + cField.tags, ok = cacheFields[tag] + } - for i, val := range orVals { - vals := strings.Split(val, tagKeySeparator) + if !ok { - key := strings.TrimSpace(vals[0]) + for _, t := range strings.Split(tag, tagSeparator) { - if len(key) == 0 { - panic(fmt.Sprintf("Invalid validation tag on field %s", name)) - } + orVals := strings.Split(t, orSeparator) + // fmt.Println(len(orVals) - 1) + cTag := &cacheTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))} + cField.tags = append(cField.tags, cTag) - param := "" - if len(vals) > 1 { - param = strings.TrimSpace(vals[1]) - } + for i, val := range orVals { + vals := strings.Split(val, tagKeySeparator) + + key := strings.TrimSpace(vals[0]) - // fmt.Println(cTag.keyVals) - cTag.keyVals[i] = []string{key, param} - // cTag.keyVals = append(cTag.keyVals, []string{key, param}) + if len(key) == 0 { + panic(fmt.Sprintf("Invalid validation tag on field %s", name)) + } - // for vals := range strings.Split(t, tagKeySeparator) { - // cField.tags = append(cField.tags, cacheTags{ isOrVal: len(orVals) > 1, []string{key, param}) + 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]) } - // } - // vals := strings.Split(valTag, tagKeySeparator) - // key := strings.TrimSpace(vals[0]) + if isSingleField && !ok { + // fmt.Println(cField.tag) + cacheFields[cField.tag] = cField.tags + } } }