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 }