Skip to content

Commit

Permalink
feat(core/perf): caching of reflected fields
Browse files Browse the repository at this point in the history
WIP on issue #16
  • Loading branch information
typerandom committed May 7, 2015
1 parent 357489c commit e36e39b
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 18 deletions.
2 changes: 1 addition & 1 deletion bench/competitor_bench.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package test
package bench

import (
"errors"
Expand Down
10 changes: 5 additions & 5 deletions core/normalization.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,22 @@ func normalizeNumeric(value interface{}) (result interface{}, kind reflect.Kind,
// TODO: Normalize slices to arrays?

func normalizeInternal(value interface{}, isNil bool) (*NormalizedValue, error) {
valueType := reflect.ValueOf(value)
kind := valueType.Kind()
reflectedValue := reflect.ValueOf(value)
kind := reflectedValue.Kind()

switch valueType.Kind() {
switch reflectedValue.Kind() {
// Dereference the pointer and normalize that value
case reflect.Ptr:
// If it's a nil pointer then flag the value as nil, obtain the inner element and create/return a zero value for the type
if valueType.IsNil() {
if reflectedValue.IsNil() {
isNil = true

innerElement := reflect.TypeOf(value).Elem()
kind = innerElement.Kind()

value = reflect.Zero(innerElement).Interface()
} else {
value = valueType.Elem().Interface()
value = reflectedValue.Elem().Interface()
}

return normalizeInternal(value, isNil)
Expand Down
35 changes: 24 additions & 11 deletions core/reflection.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import (
)

type ReflectedField struct {
Index int
Parent *ReflectedField
Name string
Value interface{}
TagGroups []TagGroup
}

func (this *ReflectedField) GetValue(sourceStruct reflect.Value) interface{} {
return sourceStruct.Field(this.Index).Interface()
}

func (this *ReflectedField) FullName(postfix ...string) string {
fullName := this.Name
parent := this.Parent
Expand All @@ -35,24 +39,31 @@ func (this *ReflectedField) FullName(postfix ...string) string {
return fullName
}

func reflectValue(value interface{}) (reflect.Type, reflect.Value) {
reflectValue := reflect.ValueOf(value)
valueType := reflectValue.Type()
func reflectValue(value interface{}) reflect.Type {
reflectedValueType := reflect.TypeOf(value)

if valueType.Kind() == reflect.Ptr {
valueType = valueType.Elem()
if reflectedValueType.Kind() == reflect.Ptr {
reflectedValueType = reflectedValueType.Elem()
}

return valueType, reflect.Indirect(reflectValue)
return reflectedValueType
}

var structFieldCache map[reflect.Type][]*ReflectedField = map[reflect.Type][]*ReflectedField{}

func GetStructFields(value interface{}, tagName string) ([]*ReflectedField, error) {
var fields []*ReflectedField

valueType, reflectedValue := reflectValue(value)
//reflectedValue := reflect.Indirect(reflect.ValueOf(value))

reflectedType := reflectValue(value)

for i := 0; i < valueType.NumField(); i++ {
field := valueType.Field(i)
if cachedFields, ok := structFieldCache[reflectedType]; ok {
return cachedFields, nil
}

for i := 0; i < reflectedType.NumField(); i++ {
field := reflectedType.Field(i)
if unicode.IsUpper(rune(field.Name[0])) { // only grab exported fields
tagValue := field.Tag.Get(tagName)

Expand All @@ -63,15 +74,17 @@ func GetStructFields(value interface{}, tagName string) ([]*ReflectedField, erro
}

reflectedField := &ReflectedField{
Index: i,
Name: field.Name,
Value: reflectedValue.Field(i).Interface(),
TagGroups: tagGroups,
}

fields = append(fields, reflectedField)
}
}

structFieldCache[reflectedType] = fields

return fields, nil
}

Expand Down
6 changes: 5 additions & 1 deletion walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ func walkValidateStruct(context *context, normalized *core.NormalizedValue, pare
return
}

sourceStruct := reflect.Indirect(reflect.ValueOf(normalized.Value))

for _, field := range fields {
normalizedFieldValue, err := core.Normalize(field.Value)
fieldValue := field.GetValue(sourceStruct)

normalizedFieldValue, err := core.Normalize(fieldValue)

if err != nil {
context.errors.Add(core.NewValidatorError(field, nil, err))
Expand Down

0 comments on commit e36e39b

Please sign in to comment.