alloc.go 2.1 KB

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