diff --git a/dbscan/dbscan.go b/dbscan/dbscan.go index 1a4faf9..0ea7899 100644 --- a/dbscan/dbscan.go +++ b/dbscan/dbscan.go @@ -6,6 +6,7 @@ import ( "reflect" "regexp" "strings" + "sync" ) // Rows is an abstract database rows that dbscan can iterate over and get the data from. @@ -54,6 +55,7 @@ func SnakeCaseMapper(str string) string { // API is the core type in dbscan. It implements all the logic and exposes functionality available in the package. // With API type users can create a custom API instance and override default settings hence configure dbscan. +// API should not be copied after first use. type API struct { structTagKey string columnSeparator string @@ -61,6 +63,8 @@ type API struct { scannableTypesOption []interface{} scannableTypesReflect []reflect.Type allowUnknownColumns bool + // columnToIndexFieldMapCache stores a map of reflect.Type -> map[string][]int + columnToIndexFieldMapCache sync.Map } // APIOption is a function type that changes API configuration. diff --git a/dbscan/structref.go b/dbscan/structref.go index 27a8782..f202754 100644 --- a/dbscan/structref.go +++ b/dbscan/structref.go @@ -12,6 +12,18 @@ type toTraverse struct { } func (api *API) getColumnToFieldIndexMap(structType reflect.Type) map[string][]int { + resultIface, ok := api.columnToIndexFieldMapCache.Load(structType) + if ok { + return resultIface.(map[string][]int) + } + + result := api.buildColumnToFieldIndexMap(structType) + resultIface, _ = api.columnToIndexFieldMapCache.LoadOrStore(structType, result) + result = resultIface.(map[string][]int) + return result +} + +func (api *API) buildColumnToFieldIndexMap(structType reflect.Type) map[string][]int { result := make(map[string][]int, structType.NumField()) var queue []*toTraverse queue = append(queue, &toTraverse{Type: structType, IndexPrefix: nil, ColumnPrefix: ""})