LuaJIT-to-Go/table.go

145 lines
3.1 KiB
Go
Raw Normal View History

2025-01-24 19:53:09 -06:00
package luajit
/*
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <stdlib.h>
static int get_table_length(lua_State *L, int index) {
return lua_objlen(L, index);
}
*/
import "C"
import (
2025-02-13 07:11:13 -06:00
"strconv"
"sync"
2025-01-24 19:53:09 -06:00
)
2025-02-13 07:11:13 -06:00
var tablePool = sync.Pool{
New: func() interface{} {
return make(map[string]interface{})
},
}
2025-01-24 19:53:09 -06:00
// TableValue represents any value that can be stored in a Lua table
type TableValue interface {
~string | ~float64 | ~bool | ~int | ~map[string]interface{} | ~[]float64 | ~[]interface{}
}
func (s *State) GetTableLength(index int) int { return int(C.get_table_length(s.L, C.int(index))) }
2025-02-13 07:11:13 -06:00
// getTableFromPool gets a map from the pool and ensures it's empty
func getTableFromPool() map[string]interface{} {
table := tablePool.Get().(map[string]interface{})
// Clear any existing entries
for k := range table {
delete(table, k)
}
return table
}
// putTableToPool returns a map to the pool
func putTableToPool(table map[string]interface{}) {
tablePool.Put(table)
}
// PushTable pushes a Go map onto the Lua stack as a table
func (s *State) PushTable(table map[string]interface{}) error {
2025-02-13 07:11:13 -06:00
s.CreateTable(0, len(table))
2025-01-24 19:53:09 -06:00
for k, v := range table {
if err := s.PushValue(v); err != nil {
2025-01-24 19:53:09 -06:00
return err
}
s.SetField(-2, k)
}
2025-02-13 07:11:13 -06:00
// If this is a pooled table, return it
if _, hasEmptyKey := table[""]; len(table) == 1 && hasEmptyKey {
putTableToPool(table)
}
2025-01-24 19:53:09 -06:00
return nil
}
// ToTable converts a Lua table to a Go map
func (s *State) ToTable(index int) (map[string]interface{}, error) {
2025-01-24 19:53:09 -06:00
absIdx := s.absIndex(index)
2025-02-13 07:11:13 -06:00
table := getTableFromPool()
2025-01-24 19:53:09 -06:00
// Check if it's an array-like table
length := s.GetTableLength(absIdx)
if length > 0 {
array := make([]float64, length)
isArray := true
// Try to convert to array
for i := 1; i <= length; i++ {
s.PushNumber(float64(i))
s.GetTable(absIdx)
if s.GetType(-1) != TypeNumber {
isArray = false
s.Pop(1)
break
}
array[i-1] = s.ToNumber(-1)
s.Pop(1)
}
if isArray {
2025-02-13 07:11:13 -06:00
putTableToPool(table) // Return unused table to pool
result := getTableFromPool()
result[""] = array
return result, nil
2025-01-24 19:53:09 -06:00
}
}
// Handle regular table
s.PushNil()
for C.lua_next(s.L, C.int(absIdx)) != 0 {
key := ""
valueType := C.lua_type(s.L, -2)
if valueType == C.LUA_TSTRING {
key = s.ToString(-2)
} else if valueType == C.LUA_TNUMBER {
2025-02-13 07:11:13 -06:00
key = strconv.FormatFloat(s.ToNumber(-2), 'g', -1, 64)
2025-01-24 19:53:09 -06:00
}
value, err := s.ToValue(-1)
2025-01-24 19:53:09 -06:00
if err != nil {
s.Pop(1)
2025-02-13 07:11:13 -06:00
putTableToPool(table) // Return table to pool on error
2025-01-24 19:53:09 -06:00
return nil, err
}
// Handle nested array case
if m, ok := value.(map[string]interface{}); ok {
if arr, ok := m[""]; ok {
value = arr
}
}
table[key] = value
s.Pop(1)
}
return table, nil
}
// NewTable creates a new table and pushes it onto the stack
func (s *State) NewTable() {
C.lua_createtable(s.L, 0, 0)
}
// SetTable sets a table field
2025-01-24 19:53:09 -06:00
func (s *State) SetTable(index int) {
C.lua_settable(s.L, C.int(index))
2025-01-24 19:53:09 -06:00
}
// GetTable gets a table field
2025-01-24 19:53:09 -06:00
func (s *State) GetTable(index int) {
C.lua_gettable(s.L, C.int(index))
2025-01-24 19:53:09 -06:00
}
2025-02-13 07:11:13 -06:00
func (s *State) CreateTable(narr, nrec int) {
C.lua_createtable(s.L, C.int(narr), C.int(nrec))
}