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