From 646db5597bf100398ff4c67d34a1f5f28fb23713 Mon Sep 17 00:00:00 2001 From: xieyuschen Date: Tue, 1 Aug 2023 11:54:18 +0800 Subject: [PATCH] fix: resolve bugs in array deep copy --- .gitignore | 2 +- deepcopy.go | 10 +++- deepcopy_test.go | 127 +++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 19 +++++++ 5 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 0975184..ed6175b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ _testmain.go *.out *.log -.idea/ \ No newline at end of file +.idea/ diff --git a/deepcopy.go b/deepcopy.go index ba763ad..3310010 100644 --- a/deepcopy.go +++ b/deepcopy.go @@ -59,7 +59,7 @@ func copyRecursive(original, cpy reflect.Value) { // Get the actual value being pointed to. originalValue := original.Elem() - // if it isn't valid, return. + // if it isn't valid, return. if !originalValue.IsValid() { return } @@ -100,12 +100,18 @@ func copyRecursive(original, cpy reflect.Value) { if original.IsNil() { return } + // Make a new slice and copy each element. cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap())) for i := 0; i < original.Len(); i++ { copyRecursive(original.Index(i), cpy.Index(i)) } - + case reflect.Array: + // since origin is an array, the capacity of array will be conserved + cpy.Set(reflect.New(original.Type()).Elem()) + for i := 0; i < original.Len(); i++ { + copyRecursive(original.Index(i), cpy.Index(i)) + } case reflect.Map: if original.IsNil() { return diff --git a/deepcopy_test.go b/deepcopy_test.go index f150b1a..2772b8f 100644 --- a/deepcopy_test.go +++ b/deepcopy_test.go @@ -6,8 +6,135 @@ import ( "testing" "time" "unsafe" + + "github.com/stretchr/testify/assert" ) +func TestArray(t *testing.T) { + verifyCases := map[string]struct { + modifyAndVerify func(t *testing.T) + }{ + "array of map": { + modifyAndVerify: func(t *testing.T) { + origin := [1]map[string]int{ + { + "1": 1, + }, + } + copied := Copy(&origin) + assert.NotSame(t, origin, copied) + assert.NotSame(t, origin[0], copied.(*[1]map[string]int)[0]) + origin[0]["1"] = 999 + if 1 != copied.(*[1]map[string]int)[0]["1"] { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of *map": { + modifyAndVerify: func(t *testing.T) { + origin := [1]*map[string]int{ + { + "1": 1, + }, + } + copied := Copy(&origin) + assert.NotSame(t, origin, copied) + assert.NotSame(t, origin[0], copied.(*[1]*map[string]int)[0]) + (*origin[0])["1"] = 999 + if 1 != (*copied.(*[1]*map[string]int)[0])["1"] { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of *int": { + modifyAndVerify: func(t *testing.T) { + intp := func(i int) *int { + return &i + } + arrayOfInt := [3]*int{intp(1), intp(2)} + copied := Copy(&arrayOfInt) + assert.NotSame(t, &arrayOfInt, copied) + assert.NotSame(t, arrayOfInt[0], copied.(*[3]*int)[0]) + arrayOfInt[0] = intp(999) + if 1 != *copied.(*[3]*int)[0] { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of int": { + modifyAndVerify: func(t *testing.T) { + arrayOfInt := [3]int{1, 2} + copied := Copy(&arrayOfInt) + assert.NotSame(t, &arrayOfInt, copied) + assert.NotSame(t, &arrayOfInt[0], copied.(*[3]int)[0]) + arrayOfInt[0] = 999 + if 1 != (*copied.(*[3]int))[0] { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of slice": { + modifyAndVerify: func(t *testing.T) { + arraySlice := [3][]int{{1, 2}} + copied := Copy(&arraySlice) + assert.NotSame(t, &arraySlice, copied) + assert.NotSame(t, &arraySlice[0], copied.(*[3][]int)[0]) + assert.NotSame(t, &arraySlice[0][0], copied.(*[3][]int)[0][0]) + arraySlice[0][0] = 999 + if 1 != (*copied.(*[3][]int))[0][0] { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of *slice": { + modifyAndVerify: func(t *testing.T) { + PointerSlice := [3]*[]int{{1, 2}} + copied := Copy(&PointerSlice) + assert.NotSame(t, &PointerSlice, copied) + assert.NotSame(t, PointerSlice[0], copied.(*[3]*[]int)[0]) + assert.NotSame(t, &(*PointerSlice[0])[0], &(*copied.(*[3]*[]int)[0])[0]) + (*PointerSlice[0])[0] = 999 + if 1 != (*(copied.(*[3]*[]int))[0])[0] { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of struct": { + modifyAndVerify: func(t *testing.T) { + type s struct { + I int + } + arrayOfStruct := [3]s{{I: 1}} + copied := Copy(&arrayOfStruct) + assert.NotSame(t, &arrayOfStruct, copied) + assert.NotSame(t, arrayOfStruct[0], copied.(*[3]s)[0]) + arrayOfStruct[0].I = 999 + if 1 != copied.(*[3]s)[0].I { + t.Fatalf("fail to do deep copy") + } + }, + }, + "array of *struct": { + modifyAndVerify: func(t *testing.T) { + type s struct { + I int + } + arrayOfPointerStruct := [3]*s{&s{I: 1}} + copied := Copy(&arrayOfPointerStruct) + assert.NotSame(t, &arrayOfPointerStruct, copied) + assert.NotSame(t, arrayOfPointerStruct[0], copied.(*[3]*s)[0]) + arrayOfPointerStruct[0].I = 999 + if 1 != copied.(*[3]*s)[0].I { + t.Fatalf("fail to do deep copy") + } + }, + }, + } + for key, tt := range verifyCases { + t.Run(key, tt.modifyAndVerify) + } +} + // just basic is this working stuff func TestSimple(t *testing.T) { Strings := []string{"a", "b", "c"} diff --git a/go.mod b/go.mod index b736451..562bd90 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/xieyuschen/deepcopy go 1.14 + +require github.com/stretchr/testify v1.7.5 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a8b13b0 --- /dev/null +++ b/go.sum @@ -0,0 +1,19 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=