github.com/xmx/lua@v0.0.0-20230324063450-8a298e091302/alloc.go (about) 1 package lua 2 3 import ( 4 "reflect" 5 "unsafe" 6 ) 7 8 // iface is an internal representation of the go-interface. 9 type iface struct { 10 itab unsafe.Pointer 11 word unsafe.Pointer 12 } 13 14 const preloadLimit LNumber = 128 15 16 var ( 17 _fv float64 18 _uv uintptr 19 ) 20 21 var preloads [int(preloadLimit)]LValue 22 23 func init() { 24 for i := 0; i < int(preloadLimit); i++ { 25 preloads[i] = LNumber(i) 26 } 27 } 28 29 // allocator is a fast bulk memory allocator for the LValue. 30 type allocator struct { 31 size int 32 fptrs []float64 33 fheader *reflect.SliceHeader 34 35 scratchValue LValue 36 scratchValueP *iface 37 } 38 39 func newAllocator(size int) *allocator { 40 al := &allocator{ 41 size: size, 42 fptrs: make([]float64, 0, size), 43 fheader: nil, 44 } 45 al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs)) 46 al.scratchValue = LNumber(0) 47 al.scratchValueP = (*iface)(unsafe.Pointer(&al.scratchValue)) 48 49 return al 50 } 51 52 // LNumber2I takes a number value and returns an interface LValue representing the same number. 53 // Converting an LNumber to a LValue naively, by doing: 54 // `var val LValue = myLNumber` 55 // will result in an individual heap alloc of 8 bytes for the float value. LNumber2I amortizes the cost and memory 56 // overhead of these allocs by allocating blocks of floats instead. 57 // The downside of this is that all of the floats on a given block have to become eligible for gc before the block 58 // as a whole can be gc-ed. 59 func (al *allocator) LNumber2I(v LNumber) LValue { 60 // first check for shared preloaded numbers 61 if v >= 0 && v < preloadLimit && float64(v) == float64(int64(v)) { 62 return preloads[int(v)] 63 } 64 65 // check if we need a new alloc page 66 if cap(al.fptrs) == len(al.fptrs) { 67 al.fptrs = make([]float64, 0, al.size) 68 al.fheader = (*reflect.SliceHeader)(unsafe.Pointer(&al.fptrs)) 69 } 70 71 // alloc a new float, and store our value into it 72 al.fptrs = append(al.fptrs, float64(v)) 73 fptr := &al.fptrs[len(al.fptrs)-1] 74 75 // hack our scratch LValue to point to our allocated value 76 // this scratch lvalue is copied when this function returns meaning the scratch value can be reused 77 // on the next call 78 al.scratchValueP.word = unsafe.Pointer(fptr) 79 80 return al.scratchValue 81 }