Skip to content

Commit e4f2ff6

Browse files
joeybloggsjoeybloggs
authored andcommitted
add caching of field only tags, now less time, memory and only 2 allocations vs 9
1 parent 22d031d commit e4f2ff6

File tree

1 file changed

+65
-32
lines changed

1 file changed

+65
-32
lines changed

validator.go

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
306306

307307
cField.isTime = true
308308

309-
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, cField); fieldError != nil {
309+
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
310310
validationErrors.Errors[fieldError.Field] = fieldError
311311
// free up memory reference
312312
fieldError = nil
@@ -331,7 +331,7 @@ func (v *Validate) structRecursive(top interface{}, current interface{}, s inter
331331

332332
default:
333333

334-
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, cField); fieldError != nil {
334+
if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
335335
validationErrors.Errors[fieldError.Field] = fieldError
336336
// free up memory reference
337337
fieldError = nil
@@ -361,14 +361,17 @@ func (v *Validate) Field(f interface{}, tag string) *FieldError {
361361
// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
362362
func (v *Validate) FieldWithValue(val interface{}, f interface{}, tag string) *FieldError {
363363

364-
return v.fieldWithNameAndValue(nil, val, f, tag, "", nil)
364+
return v.fieldWithNameAndValue(nil, val, f, tag, "", true, nil)
365365
}
366366

367-
func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, cacheField *cachedField) *FieldError {
367+
var cacheFields = map[string][]*cacheTags{}
368+
369+
func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f interface{}, tag string, name string, isSingleField bool, cacheField *cachedField) *FieldError {
368370

369371
// var fieldType reflect.Type
370372
// var fieldKind reflect.Kind
371373
var cField *cachedField
374+
var ok bool
372375

373376
// This is a double check if coming from validate.Struct but need to be here in case function is called directly
374377
if tag == noValidationTag {
@@ -382,14 +385,30 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
382385
if cacheField == nil {
383386
valueField := reflect.ValueOf(f)
384387

385-
cField = &cachedField{name: name, kind: valueField.Kind()}
388+
if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
389+
// valueField = valueField.Elem()
390+
// f = valueField.Interface()
391+
return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, isSingleField, cacheField)
392+
}
393+
394+
// if !ok {
395+
// cacheFields[cField.tag] = cField
396+
397+
// valueField = valueField.Elem()
398+
399+
cField = &cachedField{name: name, kind: valueField.Kind(), tag: tag, typ: valueField.Type()}
386400
// fieldKind = valueField.Kind()
387401

388-
if cField.kind == reflect.Ptr && !valueField.IsNil() {
389-
return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, cacheField)
390-
}
402+
// if cField.kind == reflect.Ptr && !valueField.IsNil() {
403+
// return v.fieldWithNameAndValue(val, current, valueField.Elem().Interface(), tag, name, isSingleField, cacheField)
404+
// }
405+
406+
// cField.typ = valueField.Type()
407+
// cField.tag = tag
391408

392-
cField.typ = valueField.Type()
409+
// if isSingleField {
410+
// cacheFields[cField.tag] = cField
411+
// }
393412
// cField.tags = make([][]string, 0)
394413
// fieldType = valueField.Type()
395414
// for _, t := range strings.Split(tag, tagSeparator) {
@@ -413,6 +432,7 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
413432
// // vals := strings.Split(valTag, tagKeySeparator)
414433
// // key := strings.TrimSpace(vals[0])
415434
// }
435+
// }
416436
} else {
417437
cField = cacheField
418438
// fieldType = cacheField.typ
@@ -429,39 +449,52 @@ func (v *Validate) fieldWithNameAndValue(val interface{}, current interface{}, f
429449
}
430450

431451
if len(cField.tags) == 0 {
432-
for _, t := range strings.Split(tag, tagSeparator) {
433452

434-
orVals := strings.Split(t, orSeparator)
435-
// fmt.Println(len(orVals) - 1)
436-
cTag := &cacheTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))}
437-
cField.tags = append(cField.tags, cTag)
453+
if isSingleField {
454+
cField.tags, ok = cacheFields[tag]
455+
}
438456

439-
for i, val := range orVals {
440-
vals := strings.Split(val, tagKeySeparator)
457+
if !ok {
441458

442-
key := strings.TrimSpace(vals[0])
459+
for _, t := range strings.Split(tag, tagSeparator) {
443460

444-
if len(key) == 0 {
445-
panic(fmt.Sprintf("Invalid validation tag on field %s", name))
446-
}
461+
orVals := strings.Split(t, orSeparator)
462+
// fmt.Println(len(orVals) - 1)
463+
cTag := &cacheTags{isOrVal: len(orVals) > 1, keyVals: make([][]string, len(orVals))}
464+
cField.tags = append(cField.tags, cTag)
447465

448-
param := ""
449-
if len(vals) > 1 {
450-
param = strings.TrimSpace(vals[1])
451-
}
466+
for i, val := range orVals {
467+
vals := strings.Split(val, tagKeySeparator)
468+
469+
key := strings.TrimSpace(vals[0])
452470

453-
// fmt.Println(cTag.keyVals)
454-
cTag.keyVals[i] = []string{key, param}
455-
// cTag.keyVals = append(cTag.keyVals, []string{key, param})
471+
if len(key) == 0 {
472+
panic(fmt.Sprintf("Invalid validation tag on field %s", name))
473+
}
456474

457-
// for vals := range strings.Split(t, tagKeySeparator) {
458-
// cField.tags = append(cField.tags, cacheTags{ isOrVal: len(orVals) > 1, []string{key, param})
475+
param := ""
476+
if len(vals) > 1 {
477+
param = strings.TrimSpace(vals[1])
478+
}
459479

480+
// fmt.Println(cTag.keyVals)
481+
cTag.keyVals[i] = []string{key, param}
482+
// cTag.keyVals = append(cTag.keyVals, []string{key, param})
483+
484+
// for vals := range strings.Split(t, tagKeySeparator) {
485+
// cField.tags = append(cField.tags, cacheTags{ isOrVal: len(orVals) > 1, []string{key, param})
486+
487+
}
488+
489+
// }
490+
// vals := strings.Split(valTag, tagKeySeparator)
491+
// key := strings.TrimSpace(vals[0])
460492
}
461493

462-
// }
463-
// vals := strings.Split(valTag, tagKeySeparator)
464-
// key := strings.TrimSpace(vals[0])
494+
if isSingleField && !ok {
495+
// fmt.Println(cField.tag)
496+
cacheFields[cField.tag] = cField.tags
497+
}
465498
}
466499
}
467500

0 commit comments

Comments
 (0)