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