Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions allocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,23 @@ func (a *Allocator) SetCustomFunc(t reflect.Type, fn Func) {
a.cachedCustomFuncTypes.Store(t, fn)
}

// SetCustomPtrFunc sets a custom clone function for type t.
// If t is not pointer to struct, SetCustomFunc ignores t.
//
// If fn is nil, remove the custom clone function for type t.
func (a *Allocator) SetCustomPtrFunc(t reflect.Type, fn Func) {
if fn == nil {
a.cachedCustomFuncTypes.Delete(t)
return
}

for t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
return
}

a.cachedCustomFuncTypes.Store(t, fn)
}

func heapNew(pool unsafe.Pointer, t reflect.Type) reflect.Value {
return reflect.New(t)
}
Expand Down
6 changes: 6 additions & 0 deletions clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ func (state *cloneState) clonePtr(v reflect.Value) reflect.Value {

t := v.Type()

if fn, ok := state.allocator.cachedCustomFuncTypes.Load(t); ok {
nv := state.allocator.New(t)
fn.(Func)(state.allocator, v, nv.Elem())
return nv.Elem()
}

if state.allocator.isOpaquePointer(t) {
if v.CanInterface() {
return v
Expand Down
8 changes: 8 additions & 0 deletions structtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ func SetCustomFunc(t reflect.Type, fn Func) {
defaultAllocator.SetCustomFunc(t, fn)
}

// SetCustomPtrFunc sets a custom clone function for type t in heap allocator.
// If t is not pointer to struct, SetCustomFunc ignores t.
//
// If fn is nil, remove the custom clone function for type t.
func SetCustomPtrFunc(t reflect.Type, fn Func) {
defaultAllocator.SetCustomPtrFunc(t, fn)
}

// Init creates a new value of src.Type() and shadow copies all content from src.
// If noCustomFunc is set to true, custom clone function will be ignored.
//
Expand Down
40 changes: 40 additions & 0 deletions structtype_sample_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,46 @@ func ExampleSetCustomFunc() {
// [abc 123]
}

// ExampleSetCustomPtrFunc tests whether we can customize the cloning behavior of a pointer type.
// In this example, the custom function reuses cached values.
func ExampleSetCustomPtrFunc() {
type Data struct {
Name string
Value int
}
refs := make(map[string]*Data)
// Filter nil values in Data when cloning old value.
SetCustomPtrFunc(reflect.TypeOf(&Data{}), func(allocator *Allocator, old, new reflect.Value) {
// The new is a zero value of MyStruct.
// We can get its address to update it.
value := new.Addr().Interface().(**Data)
oldRole := old.Interface().(*Data)

if cached, ok := refs[(*oldRole).Name]; ok {
*value = cached
} else {
*value = &Data{
Name: (*oldRole).Name,
Value: (*oldRole).Value,
}
refs[(*value).Name] = *value
}
})

orig := &Data{
Name: "abc",
Value: 123,
}
cloned1 := Clone(orig).(*Data)
cloned2 := Clone(orig).(*Data)
cloned1.Value = 456
orig.Value = -1
fmt.Println(*orig, *cloned1, *cloned2)

// Output:
// {abc -1} {abc 456} {abc 456}
}

func ExampleSetCustomFunc_partiallyClone() {
type T struct {
Value int
Expand Down