github.com/wfusion/gofusion@v1.1.14/common/utils/clone/arena_go120.go (about)

     1  // Copyright 2023 Huan Du. All rights reserved.
     2  // Licensed under the MIT license that can be found in the LICENSE file.
     3  
     4  //go:build go1.20 && goexperiment.arenas
     5  // +build go1.20,goexperiment.arenas
     6  
     7  package clone
     8  
     9  const arenaIsEnabled = true
    10  
    11  // The arenaAllocator allocates memory from arena.
    12  var arenaAllocatorMethods = &AllocatorMethods{
    13  	New:       arenaNew,
    14  	MakeSlice: arenaMakeSlice,
    15  	MakeMap:   arenaMakeMap,
    16  	MakeChan:  arenaMakeChan,
    17  }
    18  
    19  // FromArena creates an allocator using arena a to allocate memory.
    20  func FromArena(a *arena.Arena) *Allocator {
    21  	return NewAllocator(unsafe.Pointer(a), arenaAllocatorMethods)
    22  }
    23  
    24  // ArenaClone recursively deep clones v to a new value in arena a.
    25  // It works in the same way as Clone, except it allocates all memory from arena.
    26  func ArenaClone[T any](a *arena.Arena, v T) (nv T) {
    27  	src := reflect.ValueOf(v)
    28  	cloned := FromArena(a).Clone(src)
    29  
    30  	if !cloned.IsValid() {
    31  		return
    32  	}
    33  
    34  	dst := reflect.ValueOf(&nv).Elem()
    35  	dst.Set(cloned)
    36  	return
    37  }
    38  
    39  // ArenaCloneSlowly recursively deep clones v to a new value in arena a.
    40  // It works in the same way as Slowly, except it allocates all memory from arena.
    41  func ArenaCloneSlowly[T any](a *arena.Arena, v T) (nv T) {
    42  	src := reflect.ValueOf(v)
    43  	cloned := FromArena(a).CloneSlowly(src)
    44  
    45  	if !cloned.IsValid() {
    46  		return
    47  	}
    48  
    49  	dst := reflect.ValueOf(&nv).Elem()
    50  	dst.Set(cloned)
    51  	return
    52  }
    53  
    54  func arenaNew(pool unsafe.Pointer, t reflect.Type) reflect.Value {
    55  	return reflect.ArenaNew((*arena.Arena)(pool), reflect.PtrTo(t))
    56  }
    57  
    58  func arenaMakeSlice(pool unsafe.Pointer, t reflect.Type, len, cap int) reflect.Value {
    59  	a := (*arena.Arena)(pool)
    60  
    61  	// As of go1.20, there is no reflect method to allocate slice in arena.
    62  	// Following code is a hack to allocate a large enough byte buffer
    63  	// and then cast it to T[].
    64  	et := t.Elem()
    65  	l := int(et.Size())
    66  	total := l * cap
    67  
    68  	data := arena.MakeSlice[byte](a, total, total)
    69  	ptr := unsafe.Pointer(&data[0])
    70  	elem := reflect.NewAt(et, ptr)
    71  	slicePtr := reflect.ArenaNew(a, reflect.PtrTo(t))
    72  	*(*sliceHeader)(slicePtr.UnsafePointer()) = sliceHeader{
    73  		Data: elem.Pointer(),
    74  		Len:  l,
    75  		Cap:  cap,
    76  	}
    77  	runtime.KeepAlive(elem)
    78  
    79  	slice := slicePtr.Elem()
    80  	return slice.Slice3(0, len, cap)
    81  }
    82  
    83  func arenaMakeMap(pool unsafe.Pointer, t reflect.Type, n int) reflect.Value {
    84  	// As of go1.20, there is no way to allocate map in arena.
    85  	// Fallback to heap allocation.
    86  	return reflect.MakeMapWithSize(t, n)
    87  }
    88  
    89  func arenaMakeChan(pool unsafe.Pointer, t reflect.Type, buffer int) reflect.Value {
    90  	// As of go1.20, there is no way to allocate chan in arena.
    91  	// Fallback to heap allocation.
    92  	return reflect.MakeChan(t, buffer)
    93  }