mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 10:00:09 -04:00
179 lines
7.2 KiB
Go
179 lines
7.2 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package copy
|
|
|
|
import (
|
|
"reflect"
|
|
)
|
|
|
|
// DeepCopyValue produces a deep copy of the given value, where the result
|
|
// ideally shares no mutable memory with the given value.
|
|
//
|
|
// There are some limitations on what's possible, however:
|
|
// - This package can't write into an unexported field of a struct, so those
|
|
// will be ignored entirely and thus left as their zero values in the
|
|
// result.
|
|
// - If the given structure contains function pointers then their closures
|
|
// might refer to shared memory that this function cannot copy. If they
|
|
// refer to memory that's also included in the data structure outside of
|
|
// the function pointer then the two will be disconnected in the result.
|
|
// - It isn't really meaningful to "copy" a channel since it's a
|
|
// synchronization primitive rather than a data structure, so the result
|
|
// will share the same channels as the input.
|
|
// - Copying other library-based synchronization primitives like [sync.Mutex]
|
|
// doesn't really make sense either, although this function doesn't
|
|
// understand what they are and so the result is undefined. If your
|
|
// synchronization primitive has a "ready to use" zero value then it _might_
|
|
// be acceptable to store it in an unexported field and thus have it be
|
|
// zeroed in the result, but at that point you're probably better off
|
|
// writing a specialized deepcopy function so that you can actually use
|
|
// the synchronization primitive to prevent data races during copying.
|
|
// - The uintptr and [unsafe.Pointer] types might well refer to some shared
|
|
// memory, but don't give any information about how to copy that memory
|
|
// and so those are just preserved verbatim, making the result point to
|
|
// the same memory as the input.
|
|
// - Broadly, this function needs special handling for each different kind
|
|
// of value in Go, and so if a later version of Go has introduced a new kind
|
|
// of value then this function might not support it yet. That might cause
|
|
// this function to panic, or to ignore part of the structure, or otherwise
|
|
// misbehave.
|
|
//
|
|
// This is intended as a relatively simple utility for straightforward cases,
|
|
// primarily for use in contrived situations like unit tests.
|
|
//
|
|
// It intentionally does not offer any customization; if you need to do
|
|
// something special then it's better to just write your own simple direct code
|
|
// than to pull in all of this reflection trickery. Even if you don't need to do
|
|
// something special it's probably still better to just write some
|
|
// straightforward code that directly describes the behavior you're intending,
|
|
// so that the Go compiler can help you and so you don't force future
|
|
// maintainers to understand all of this metaprogramming if something goes
|
|
// wrong. Seriously... don't use this function.
|
|
func DeepCopyValue[T any](v T) T {
|
|
// We use type parameters in the signature to make usage more convenient
|
|
// for the caller (no type assertions required) but we actually do all
|
|
// our internal work in the realm of package reflect.
|
|
input := reflect.ValueOf(&v).Elem() // if T is an interface type then input is the interface value, not the value inside it
|
|
ty := reflect.TypeFor[T]() // likewise, if T is interface type then this is the static interface type, not the dynamic type
|
|
result := deepCopyValue(input, ty)
|
|
return result.Interface().(T)
|
|
}
|
|
|
|
func deepCopyValue(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
switch ty.Kind() {
|
|
// A large subset of the kinds don't point to mutable sharable memory,
|
|
// or don't refer to something we can possibly copy, and so we can just
|
|
// return them directly without any extra work.
|
|
case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, reflect.String, reflect.UnsafePointer, reflect.Func, reflect.Chan:
|
|
return v
|
|
|
|
case reflect.Array:
|
|
return deepCopyArray(v, ty)
|
|
case reflect.Interface:
|
|
return deepCopyInterface(v, ty)
|
|
case reflect.Map:
|
|
return deepCopyMap(v, ty)
|
|
case reflect.Pointer:
|
|
return deepCopyPointer(v, ty)
|
|
case reflect.Slice:
|
|
return deepCopySlice(v, ty)
|
|
case reflect.Struct:
|
|
return deepCopyStruct(v, ty)
|
|
|
|
default:
|
|
panic("unsupported type kind " + ty.Kind().String())
|
|
}
|
|
}
|
|
|
|
func deepCopyArray(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
// Copying an array really means allocating a new array and then
|
|
// copying each of the elements from the source.
|
|
ret := reflect.New(ty).Elem()
|
|
for i := range ret.Len() {
|
|
newElemV := deepCopyValue(v.Index(i), ty.Elem())
|
|
ret.Index(i).Set(newElemV)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func deepCopyInterface(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
// An interface value is not directly mutable itself, but the value
|
|
// inside it might be and so we'll copy that and then wrap the result
|
|
// in a new interface value of the same type.
|
|
ret := reflect.New(ty).Elem()
|
|
dynV := deepCopyValue(v.Elem(), v.Elem().Type())
|
|
ret.Set(dynV)
|
|
return ret
|
|
}
|
|
|
|
func deepCopyMap(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
ret := reflect.MakeMap(ty)
|
|
for iter := v.MapRange(); iter.Next(); {
|
|
// We don't copy the key because Go does not allow any mutably-aliasable
|
|
// types as map keys. (That would make it very easy to corrupt the
|
|
// internals of the map, after all!)
|
|
k := iter.Key()
|
|
v := deepCopyValue(iter.Value(), ty.Elem())
|
|
ret.SetMapIndex(k, v)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func deepCopyPointer(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
// We copy a pointer by copying what it refers to and then returning
|
|
// a pointer to that copy.
|
|
newTarget := deepCopyValue(v.Elem(), ty.Elem())
|
|
return newTarget.Addr()
|
|
}
|
|
|
|
func deepCopySlice(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
if v.IsNil() {
|
|
return v
|
|
}
|
|
// Copying a slice really means copying the part of its backing array
|
|
// that in could potentially observe. In particular, it's possible to
|
|
// expand the view of the backing array up to the slice's capacity,
|
|
// so we need to copy the entire capacity even if the length is
|
|
// currently shorter to ensure that the result is truly equivalent.
|
|
length := v.Len()
|
|
capacity := v.Cap()
|
|
|
|
// This exposes any elements that are between length and capacity.
|
|
fullView := v.Slice3(0, capacity, capacity)
|
|
// Making a slice also allocates a new backing array for it.
|
|
ret := reflect.MakeSlice(ty, capacity, capacity)
|
|
for i := range capacity {
|
|
ret.Index(i).Set(fullView.Index(i))
|
|
}
|
|
|
|
// We must restore the original length before we return.
|
|
return ret.Slice(0, length)
|
|
}
|
|
|
|
func deepCopyStruct(v reflect.Value, ty reflect.Type) reflect.Value {
|
|
// To copy a struct we must copy each exported field one by one.
|
|
// We can't assign to unexported fields and so we just leave those
|
|
// unset in the new value.
|
|
ret := reflect.New(ty).Elem()
|
|
for i := range ty.NumField() {
|
|
fieldRet := ret.Field(i)
|
|
if !fieldRet.CanSet() {
|
|
// Presumably it's an unexported field, so we can't do anything
|
|
// with it and must leave it zeroed.
|
|
continue
|
|
}
|
|
newVal := deepCopyValue(v.Field(i), ty.Field(i).Type)
|
|
fieldRet.Set(newVal)
|
|
}
|
|
return ret
|
|
}
|