Skip to content

Commit

Permalink
add opt.converters and deep_fields cache
Browse files Browse the repository at this point in the history
  • Loading branch information
chyroc committed May 27, 2022
1 parent 3e39b05 commit 04e0f87
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 39 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea/
ttt/
53 changes: 33 additions & 20 deletions copier.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"strings"
"sync"
"unicode"
)

Expand Down Expand Up @@ -41,6 +42,22 @@ type Option struct {
Converters []TypeConverter
}

func (opt Option) converters() map[converterPair]TypeConverter {
var converters = map[converterPair]TypeConverter{}

// save converters into map for faster lookup
for i := range opt.Converters {
pair := converterPair{
SrcType: reflect.TypeOf(opt.Converters[i].SrcType),
DstType: reflect.TypeOf(opt.Converters[i].DstType),
}

converters[pair] = opt.Converters[i]
}

return converters
}

type TypeConverter struct {
SrcType interface{}
DstType interface{}
Expand Down Expand Up @@ -81,23 +98,9 @@ func copier(toValue interface{}, fromValue interface{}, opt Option) (err error)
amount = 1
from = indirect(reflect.ValueOf(fromValue))
to = indirect(reflect.ValueOf(toValue))
converters map[converterPair]TypeConverter
converters = opt.converters()
)

// save convertes into map for faster lookup
for i := range opt.Converters {
if converters == nil {
converters = make(map[converterPair]TypeConverter)
}

pair := converterPair{
SrcType: reflect.TypeOf(opt.Converters[i].SrcType),
DstType: reflect.TypeOf(opt.Converters[i].DstType),
}

converters[pair] = opt.Converters[i]
}

if !to.CanAddr() {
return ErrInvalidCopyDestination
}
Expand Down Expand Up @@ -394,7 +397,17 @@ func shouldIgnore(v reflect.Value, ignoreEmpty bool) bool {
return ignoreEmpty && v.IsZero()
}

var deepFieldsLock sync.RWMutex
var deepFieldsMap = make(map[reflect.Type][]reflect.StructField)

func deepFields(reflectType reflect.Type) []reflect.StructField {
deepFieldsLock.RLock()
cache, ok := deepFieldsMap[reflectType]
deepFieldsLock.RUnlock()
if ok {
return cache
}
var res []reflect.StructField
if reflectType, _ = indirectType(reflectType); reflectType.Kind() == reflect.Struct {
fields := make([]reflect.StructField, 0, reflectType.NumField())

Expand All @@ -411,11 +424,13 @@ func deepFields(reflectType reflect.Type) []reflect.StructField {
}
}
}

return fields
res = fields
}

return nil
deepFieldsLock.Lock()
deepFieldsMap[reflectType] = res
deepFieldsLock.Unlock()
return res
}

func indirect(reflectValue reflect.Value) reflect.Value {
Expand Down Expand Up @@ -537,7 +552,6 @@ func lookupAndCopyWithConverter(to, from reflect.Value, converters map[converter

if cnv, ok := converters[pair]; ok {
result, err := cnv.Fn(from.Interface())

if err != nil {
return false, err
}
Expand Down Expand Up @@ -682,7 +696,6 @@ func getFieldName(fieldName string, flgs flags) (srcFieldName string, destFieldN
}

func driverValuer(v reflect.Value) (i driver.Valuer, ok bool) {

if !v.CanAddr() {
i, ok = v.Interface().(driver.Valuer)
return
Expand Down
3 changes: 0 additions & 3 deletions copier_converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ func TestCopyWithTypeConverters(t *testing.T) {
},
},
})

if err != nil {
t.Fatalf(`Should be able to copy from src to dst object. %v`, err)
return
Expand Down Expand Up @@ -127,7 +126,6 @@ func TestCopyWithConverterAndAnnotation(t *testing.T) {
},
},
})

if err != nil {
t.Fatalf(`Should be able to copy from src to dst object. %v`, err)
return
Expand Down Expand Up @@ -175,7 +173,6 @@ func TestCopyWithConverterStrToStrPointer(t *testing.T) {
},
},
})

if err != nil {
t.Fatalf(`Should be able to copy from src to dst object. %v`, err)
return
Expand Down
31 changes: 15 additions & 16 deletions copier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ func TestCopyFromStructToSlice(t *testing.T) {
}

func TestCopyFromSliceToSlice(t *testing.T) {
users := []User{{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, {Name: "Jinzhu2", Age: 22, Role: "Dev", Notes: []string{"hello world", "hello"}}}
users := []User{
{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}},
{Name: "Jinzhu2", Age: 22, Role: "Dev", Notes: []string{"hello world", "hello"}}}
employees := []Employee{}

if copier.Copy(&employees, users); len(employees) != 2 {
Expand Down Expand Up @@ -786,7 +788,7 @@ func TestMapInterface(t *testing.T) {

t.Run("Test copy map with nested slice map", func(t *testing.T) {
var out map[string]interface{}
var value = map[string]interface{}{
value := map[string]interface{}{
"list": []map[string]interface{}{
{
"shop_id": 123,
Expand Down Expand Up @@ -1203,7 +1205,7 @@ func TestCopyMapOfInt(t *testing.T) {
func TestCopyMapOfSliceValue(t *testing.T) {
// case1: map's value is a simple slice
key, value := 2, 3
src := map[int][]int{key: []int{value} }
src := map[int][]int{key: {value}}
dst1 := map[int][]int{}
var dst2 map[int][]int
err := copier.Copy(&dst1, src)
Expand All @@ -1220,7 +1222,7 @@ func TestCopyMapOfSliceValue(t *testing.T) {
if !ok || len(v1) != len(v2) || k != key {
t.Errorf("Map should be copied")
}
for i, _ := range v1 {
for i := range v1 {
if v2[i] != value {
t.Errorf("Map's slice value shoud be copied")
}
Expand All @@ -1240,10 +1242,10 @@ func TestCopyMapOfSliceValue(t *testing.T) {
// case2: map's value is a slice whose element is map
key1, key2 := 2, 3
value = 4
s := map[int][]map[int]int{key1: []map[int]int{ {key2: value} } }
d1 := map[int][]map[int]int{key1: []map[int]int{ {key1: key2 } } }
d2 := map[int][]map[int]int{key1: []map[int]int{ } }
d3 := map[int][]map[int]int{key1: nil }
s := map[int][]map[int]int{key1: {{key2: value}}}
d1 := map[int][]map[int]int{key1: {{key1: key2}}}
d2 := map[int][]map[int]int{key1: {}}
d3 := map[int][]map[int]int{key1: nil}
d4 := map[int][]map[int]int{}
d5 := map[int][]map[int]int(nil)
ms := []map[int][]map[int]int{d1, d2, d3, d4, d5}
Expand All @@ -1267,7 +1269,7 @@ func TestCopyMapOfSliceValue(t *testing.T) {
for k, v := range m {
if k != key2 || v != value {
t.Errorf("Map's slice value should be copied recursively")
}
}
}
}
}
Expand All @@ -1276,7 +1278,7 @@ func TestCopyMapOfSliceValue(t *testing.T) {
func TestCopyMapOfPtrValue(t *testing.T) {
intV := 3
intv := intV
src := map[int]*int{2: &intv }
src := map[int]*int{2: &intv}
dst1 := map[int]*int{}
var dst2 map[int]*int
err := copier.Copy(&dst1, src)
Expand All @@ -1295,7 +1297,7 @@ func TestCopyMapOfPtrValue(t *testing.T) {
}

v3, ok := dst2[k]
if !ok || v3 == nil ||*v3 != *v1 || *v3 != intV {
if !ok || v3 == nil || *v3 != *v1 || *v3 != intV {
t.Errorf("Map should be copied")
}
}
Expand Down Expand Up @@ -1351,7 +1353,6 @@ func TestScanner(t *testing.T) {
}

func TestScanFromPtrToSqlNullable(t *testing.T) {

var (
from struct {
S string
Expand Down Expand Up @@ -1423,7 +1424,7 @@ func TestScanFromPtrToSqlNullable(t *testing.T) {
}

func TestDeepCopyInterface(t *testing.T) {
var m = make(map[string]string)
m := make(map[string]string)
m["a"] = "ccc"

from := []interface{}{[]int{7, 8, 9}, 2, 3, m, errors.New("aaaa")}
Expand Down Expand Up @@ -1520,7 +1521,6 @@ func TestDeepCopyTime(t *testing.T) {
}
}


func TestNestedPrivateData(t *testing.T) {
type hasPrivate struct {
data int
Expand Down Expand Up @@ -1558,7 +1558,6 @@ func TestNestedPrivateData(t *testing.T) {
}
}


func TestDeepMapCopyTime(t *testing.T) {
t1 := time.Now()
t2 := t1.Add(time.Second)
Expand Down Expand Up @@ -1611,7 +1610,7 @@ func TestDeepCopySimpleTime(t *testing.T) {
}
}

type TimeWrapper struct{
type TimeWrapper struct {
time.Time
}

Expand Down

0 comments on commit 04e0f87

Please sign in to comment.