Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Merge pull request #36 from vancluever/add-shallow-copiers
Browse files Browse the repository at this point in the history
Add ShallowCopiers for shallow copying pointer values
  • Loading branch information
mitchellh authored May 5, 2021
2 parents 669aced + 6f65fd0 commit d4ce1f9
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 8 deletions.
48 changes: 43 additions & 5 deletions copystructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ type CopierFunc func(interface{}) (interface{}, error)
// this map as well as to Copy in a mutex.
var Copiers map[reflect.Type]CopierFunc = make(map[reflect.Type]CopierFunc)

// ShallowCopiers is a map of pointer types that behave specially
// when they are copied. If a type is found in this map while deep
// copying, the pointer value will be shallow copied and not walked
// into.
//
// The key should be the type, obtained using: reflect.TypeOf(value
// with type).
//
// It is unsafe to write to this map after Copies have started. If you
// are writing to this map while also copying, wrap all modifications to
// this map as well as to Copy in a mutex.
var ShallowCopiers map[reflect.Type]struct{} = make(map[reflect.Type]struct{})

// Must is a helper that wraps a call to a function returning
// (interface{}, error) and panics if the error is non-nil. It is intended
// for use in variable initializations and should only be used when a copy
Expand All @@ -73,6 +86,11 @@ type Config struct {
// Copiers is a map of types associated with a CopierFunc. Use the global
// Copiers map if this is nil.
Copiers map[reflect.Type]CopierFunc

// ShallowCopiers is a map of pointer types that when they are
// shallow copied no matter where they are encountered. Use the
// global ShallowCopiers if this is nil.
ShallowCopiers map[reflect.Type]struct{}
}

func (c Config) Copy(v interface{}) (interface{}, error) {
Expand All @@ -90,6 +108,11 @@ func (c Config) Copy(v interface{}) (interface{}, error) {
}
w.copiers = c.Copiers

if c.ShallowCopiers == nil {
c.ShallowCopiers = ShallowCopiers
}
w.shallowCopiers = c.ShallowCopiers

err := reflectwalk.Walk(v, w)
if err != nil {
return nil, err
Expand Down Expand Up @@ -117,11 +140,12 @@ func ifaceKey(pointers, depth int) uint64 {
type walker struct {
Result interface{}

copiers map[reflect.Type]CopierFunc
depth int
ignoreDepth int
vals []reflect.Value
cs []reflect.Value
copiers map[reflect.Type]CopierFunc
shallowCopiers map[reflect.Type]struct{}
depth int
ignoreDepth int
vals []reflect.Value
cs []reflect.Value

// This stores the number of pointers we've walked over, indexed by depth.
ps []int
Expand Down Expand Up @@ -288,6 +312,20 @@ func (w *walker) PointerExit(v bool) error {
return nil
}

func (w *walker) Pointer(v reflect.Value) error {
if _, ok := w.shallowCopiers[v.Type()]; ok {
// Shallow copy this value. Use the same logic as primitive, then
// return skip.
if err := w.Primitive(v); err != nil {
return err
}

return reflectwalk.SkipEntry
}

return nil
}

func (w *walker) Interface(v reflect.Value) error {
if !v.IsValid() {
return nil
Expand Down
21 changes: 21 additions & 0 deletions copystructure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1187,3 +1187,24 @@ func TestCopy_customCopierConfig(t *testing.T) {
func customCopier(v interface{}) (interface{}, error) {
return v.(nestedValue), nil
}

func TestCopy_customCopierShallowValue(t *testing.T) {
type T struct{}
v := &T{}

cfg := Config{
ShallowCopiers: map[reflect.Type]struct{}{
reflect.TypeOf(T{}): struct{}{},
},
}
result, err := cfg.Copy(v)
if err != nil {
t.Fatal(err)
}

copiedVal := result.(*T)

if v != copiedVal {
t.Fatal("value not shallow copied")
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ module github.com/mitchellh/copystructure

go 1.15

require github.com/mitchellh/reflectwalk v1.0.1
require github.com/mitchellh/reflectwalk v1.0.2
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=

0 comments on commit d4ce1f9

Please sign in to comment.