1package lua 2 3import ( 4 "reflect" 5 "unsafe" 6) 7 8// iface is an internal representation of the go-interface. 9type iface struct { 10 itab unsafe.Pointer 11 word unsafe.Pointer 12} 13 14const preloadLimit LNumber = 128 15 16var _fv float64 17var _uv uintptr 18 19var preloads [int(preloadLimit)]LValue 20 21func 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. 28type allocator struct { 29 size int 30 fptrs []float64 31 fheader *reflect.SliceHeader 32 33 scratchValue LValue 34 scratchValueP *iface 35} 36 37func 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. 57func (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} 80