901 lines
21 KiB
Go
901 lines
21 KiB
Go
package luajit
|
|
|
|
/*
|
|
#include <lua.h>
|
|
#include <lauxlib.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// Batch table field setting - single C call for multiple fields
|
|
int batch_set_string_fields(lua_State *L, int table_idx,
|
|
char **keys, char **values, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushstring(L, values[i]);
|
|
lua_setfield(L, table_idx, keys[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_set_number_fields(lua_State *L, int table_idx,
|
|
char **keys, double *values, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushnumber(L, values[i]);
|
|
lua_setfield(L, table_idx, keys[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_set_bool_fields(lua_State *L, int table_idx,
|
|
char **keys, int *values, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushboolean(L, values[i]);
|
|
lua_setfield(L, table_idx, keys[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch array creation - build entire array in C
|
|
int batch_push_int_array(lua_State *L, int *values, int count) {
|
|
lua_createtable(L, count, 0);
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushnumber(L, i + 1);
|
|
lua_pushnumber(L, values[i]);
|
|
lua_settable(L, -3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_push_float_array(lua_State *L, double *values, int count) {
|
|
lua_createtable(L, count, 0);
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushnumber(L, i + 1);
|
|
lua_pushnumber(L, values[i]);
|
|
lua_settable(L, -3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_push_string_array(lua_State *L, char **values, int count) {
|
|
lua_createtable(L, count, 0);
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushnumber(L, i + 1);
|
|
lua_pushstring(L, values[i]);
|
|
lua_settable(L, -3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_push_bool_array(lua_State *L, int *values, int count) {
|
|
lua_createtable(L, count, 0);
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushnumber(L, i + 1);
|
|
lua_pushboolean(L, values[i]);
|
|
lua_settable(L, -3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch array extraction - extract entire array in C
|
|
int batch_extract_int_array(lua_State *L, int table_idx, int *buffer, int length) {
|
|
for (int i = 1; i <= length; i++) {
|
|
lua_pushnumber(L, i);
|
|
lua_gettable(L, table_idx);
|
|
buffer[i-1] = (int)lua_tonumber(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_extract_float_array(lua_State *L, int table_idx, double *buffer, int length) {
|
|
for (int i = 1; i <= length; i++) {
|
|
lua_pushnumber(L, i);
|
|
lua_gettable(L, table_idx);
|
|
buffer[i-1] = lua_tonumber(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_extract_string_array(lua_State *L, int table_idx, char **buffer, int length) {
|
|
for (int i = 1; i <= length; i++) {
|
|
lua_pushnumber(L, i);
|
|
lua_gettable(L, table_idx);
|
|
size_t len;
|
|
const char *str = lua_tolstring(L, -1, &len);
|
|
if (str) {
|
|
buffer[i-1] = malloc(len + 1);
|
|
memcpy(buffer[i-1], str, len);
|
|
buffer[i-1][len] = '\0';
|
|
} else {
|
|
buffer[i-1] = malloc(1);
|
|
buffer[i-1][0] = '\0';
|
|
}
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_extract_bool_array(lua_State *L, int table_idx, int *buffer, int length) {
|
|
for (int i = 1; i <= length; i++) {
|
|
lua_pushnumber(L, i);
|
|
lua_gettable(L, table_idx);
|
|
buffer[i-1] = lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch global variable operations
|
|
int batch_set_globals(lua_State *L, char **names, char **values, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushstring(L, values[i]);
|
|
lua_setfield(L, LUA_GLOBALSINDEX, names[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int batch_get_globals(lua_State *L, char **names, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_getfield(L, LUA_GLOBALSINDEX, names[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch table field reading
|
|
int batch_get_table_fields(lua_State *L, int table_idx, char **keys, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_getfield(L, table_idx, keys[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch type checking
|
|
int batch_check_types(lua_State *L, int *indices, int *expected_types, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
int actual = lua_type(L, indices[i]);
|
|
if (actual != expected_types[i]) {
|
|
return i + 1; // Return 1-based index of first mismatch
|
|
}
|
|
}
|
|
return 0; // All types match
|
|
}
|
|
|
|
// Batch stack value pushes
|
|
int batch_push_mixed_values(lua_State *L, void **values, int *types, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
switch (types[i]) {
|
|
case LUA_TNIL:
|
|
lua_pushnil(L);
|
|
break;
|
|
case LUA_TBOOLEAN:
|
|
lua_pushboolean(L, *(int*)values[i]);
|
|
break;
|
|
case LUA_TNUMBER:
|
|
lua_pushnumber(L, *(double*)values[i]);
|
|
break;
|
|
case LUA_TSTRING:
|
|
lua_pushstring(L, (char*)values[i]);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch function registration
|
|
int batch_register_functions(lua_State *L, char **names, lua_CFunction *funcs, int count) {
|
|
for (int i = 0; i < count; i++) {
|
|
lua_pushcfunction(L, funcs[i]);
|
|
lua_setglobal(L, names[i]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Batch package path operations
|
|
int batch_add_package_paths(lua_State *L, char **paths, int count) {
|
|
lua_getglobal(L, "package");
|
|
lua_getfield(L, -1, "path");
|
|
|
|
const char *current = lua_tostring(L, -1);
|
|
size_t total_len = strlen(current) + 1;
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
total_len += strlen(paths[i]) + 1; // +1 for semicolon
|
|
}
|
|
|
|
char *new_path = malloc(total_len + 1);
|
|
strcpy(new_path, current);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
strcat(new_path, ";");
|
|
strcat(new_path, paths[i]);
|
|
}
|
|
|
|
lua_pushstring(L, new_path);
|
|
lua_setfield(L, -3, "path");
|
|
lua_pop(L, 2); // Remove package table and old path
|
|
|
|
free(new_path);
|
|
return 0;
|
|
}
|
|
|
|
// Batch error context building
|
|
void batch_build_error_context(lua_State *L, char **context_parts, int count, char *output, size_t max_len) {
|
|
size_t pos = 0;
|
|
for (int i = 0; i < count && pos < max_len - 1; i++) {
|
|
size_t part_len = strlen(context_parts[i]);
|
|
if (pos + part_len + 1 < max_len) {
|
|
if (i > 0) output[pos++] = ' ';
|
|
memcpy(output + pos, context_parts[i], part_len);
|
|
pos += part_len;
|
|
}
|
|
}
|
|
output[pos] = '\0';
|
|
}
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"unsafe"
|
|
)
|
|
|
|
// BatchSetStringFields sets multiple string fields on a table in one C call
|
|
func (s *State) BatchSetStringFields(tableIndex int, fields map[string]string) error {
|
|
if len(fields) == 0 {
|
|
return nil
|
|
}
|
|
|
|
keys := make([]*C.char, len(fields))
|
|
values := make([]*C.char, len(fields))
|
|
|
|
i := 0
|
|
for k, v := range fields {
|
|
keys[i] = C.CString(k)
|
|
values[i] = C.CString(v)
|
|
i++
|
|
}
|
|
|
|
C.batch_set_string_fields(s.L, C.int(tableIndex), &keys[0], &values[0], C.int(len(fields)))
|
|
|
|
for j := range len(fields) {
|
|
C.free(unsafe.Pointer(keys[j]))
|
|
C.free(unsafe.Pointer(values[j]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchSetNumberFields sets multiple number fields on a table in one C call
|
|
func (s *State) BatchSetNumberFields(tableIndex int, fields map[string]float64) error {
|
|
if len(fields) == 0 {
|
|
return nil
|
|
}
|
|
|
|
keys := make([]*C.char, len(fields))
|
|
values := make([]C.double, len(fields))
|
|
|
|
i := 0
|
|
for k, v := range fields {
|
|
keys[i] = C.CString(k)
|
|
values[i] = C.double(v)
|
|
i++
|
|
}
|
|
|
|
C.batch_set_number_fields(s.L, C.int(tableIndex), &keys[0], &values[0], C.int(len(fields)))
|
|
|
|
for j := range len(fields) {
|
|
C.free(unsafe.Pointer(keys[j]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchSetBoolFields sets multiple boolean fields on a table in one C call
|
|
func (s *State) BatchSetBoolFields(tableIndex int, fields map[string]bool) error {
|
|
if len(fields) == 0 {
|
|
return nil
|
|
}
|
|
|
|
keys := make([]*C.char, len(fields))
|
|
values := make([]C.int, len(fields))
|
|
|
|
i := 0
|
|
for k, v := range fields {
|
|
keys[i] = C.CString(k)
|
|
if v {
|
|
values[i] = 1
|
|
} else {
|
|
values[i] = 0
|
|
}
|
|
i++
|
|
}
|
|
|
|
C.batch_set_bool_fields(s.L, C.int(tableIndex), &keys[0], &values[0], C.int(len(fields)))
|
|
|
|
for j := range len(fields) {
|
|
C.free(unsafe.Pointer(keys[j]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchPushIntArray creates and pushes an int array in a single C call
|
|
func (s *State) BatchPushIntArray(values []int) error {
|
|
if len(values) == 0 {
|
|
s.CreateTable(0, 0)
|
|
return nil
|
|
}
|
|
|
|
cValues := make([]C.int, len(values))
|
|
for i, v := range values {
|
|
cValues[i] = C.int(v)
|
|
}
|
|
|
|
C.batch_push_int_array(s.L, &cValues[0], C.int(len(values)))
|
|
return nil
|
|
}
|
|
|
|
// BatchPushFloatArray creates and pushes a float array in a single C call
|
|
func (s *State) BatchPushFloatArray(values []float64) error {
|
|
if len(values) == 0 {
|
|
s.CreateTable(0, 0)
|
|
return nil
|
|
}
|
|
|
|
cValues := make([]C.double, len(values))
|
|
for i, v := range values {
|
|
cValues[i] = C.double(v)
|
|
}
|
|
|
|
C.batch_push_float_array(s.L, &cValues[0], C.int(len(values)))
|
|
return nil
|
|
}
|
|
|
|
// BatchPushStringArray creates and pushes a string array in a single C call
|
|
func (s *State) BatchPushStringArray(values []string) error {
|
|
if len(values) == 0 {
|
|
s.CreateTable(0, 0)
|
|
return nil
|
|
}
|
|
|
|
cValues := make([]*C.char, len(values))
|
|
for i, v := range values {
|
|
cValues[i] = C.CString(v)
|
|
}
|
|
|
|
C.batch_push_string_array(s.L, &cValues[0], C.int(len(values)))
|
|
|
|
for i := range values {
|
|
C.free(unsafe.Pointer(cValues[i]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchPushBoolArray creates and pushes a bool array in a single C call
|
|
func (s *State) BatchPushBoolArray(values []bool) error {
|
|
if len(values) == 0 {
|
|
s.CreateTable(0, 0)
|
|
return nil
|
|
}
|
|
|
|
cValues := make([]C.int, len(values))
|
|
for i, v := range values {
|
|
if v {
|
|
cValues[i] = 1
|
|
} else {
|
|
cValues[i] = 0
|
|
}
|
|
}
|
|
|
|
C.batch_push_bool_array(s.L, &cValues[0], C.int(len(values)))
|
|
return nil
|
|
}
|
|
|
|
// BatchExtractIntArray extracts an entire int array in a single C call
|
|
func (s *State) BatchExtractIntArray(index, length int) ([]int, error) {
|
|
if length <= 0 {
|
|
return []int{}, nil
|
|
}
|
|
|
|
buffer := make([]C.int, length)
|
|
C.batch_extract_int_array(s.L, C.int(index), &buffer[0], C.int(length))
|
|
|
|
result := make([]int, length)
|
|
for i := range length {
|
|
result[i] = int(buffer[i])
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// BatchExtractFloatArray extracts an entire float array in a single C call
|
|
func (s *State) BatchExtractFloatArray(index, length int) ([]float64, error) {
|
|
if length <= 0 {
|
|
return []float64{}, nil
|
|
}
|
|
|
|
buffer := make([]C.double, length)
|
|
C.batch_extract_float_array(s.L, C.int(index), &buffer[0], C.int(length))
|
|
|
|
result := make([]float64, length)
|
|
for i := range length {
|
|
result[i] = float64(buffer[i])
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// BatchExtractStringArray extracts an entire string array in a single C call
|
|
func (s *State) BatchExtractStringArray(index, length int) ([]string, error) {
|
|
if length <= 0 {
|
|
return []string{}, nil
|
|
}
|
|
|
|
buffer := make([]*C.char, length)
|
|
C.batch_extract_string_array(s.L, C.int(index), &buffer[0], C.int(length))
|
|
|
|
result := make([]string, length)
|
|
for i := range length {
|
|
result[i] = C.GoString(buffer[i])
|
|
C.free(unsafe.Pointer(buffer[i]))
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// BatchExtractBoolArray extracts an entire bool array in a single C call
|
|
func (s *State) BatchExtractBoolArray(index, length int) ([]bool, error) {
|
|
if length <= 0 {
|
|
return []bool{}, nil
|
|
}
|
|
|
|
buffer := make([]C.int, length)
|
|
C.batch_extract_bool_array(s.L, C.int(index), &buffer[0], C.int(length))
|
|
|
|
result := make([]bool, length)
|
|
for i := range length {
|
|
result[i] = buffer[i] != 0
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// BatchTableBuilder provides batched table building operations
|
|
type BatchTableBuilder struct {
|
|
state *State
|
|
index int
|
|
stringFields map[string]string
|
|
numberFields map[string]float64
|
|
boolFields map[string]bool
|
|
otherFields map[string]any
|
|
}
|
|
|
|
// NewBatchTableBuilder creates a table builder that batches operations
|
|
func (s *State) NewBatchTableBuilder() *BatchTableBuilder {
|
|
s.NewTable()
|
|
return &BatchTableBuilder{
|
|
state: s,
|
|
index: s.GetTop(),
|
|
stringFields: make(map[string]string),
|
|
numberFields: make(map[string]float64),
|
|
boolFields: make(map[string]bool),
|
|
otherFields: make(map[string]any),
|
|
}
|
|
}
|
|
|
|
// SetString queues a string field for batch setting
|
|
func (tb *BatchTableBuilder) SetString(key, value string) *BatchTableBuilder {
|
|
tb.stringFields[key] = value
|
|
return tb
|
|
}
|
|
|
|
// SetNumber queues a number field for batch setting
|
|
func (tb *BatchTableBuilder) SetNumber(key string, value float64) *BatchTableBuilder {
|
|
tb.numberFields[key] = value
|
|
return tb
|
|
}
|
|
|
|
// SetBool queues a boolean field for batch setting
|
|
func (tb *BatchTableBuilder) SetBool(key string, value bool) *BatchTableBuilder {
|
|
tb.boolFields[key] = value
|
|
return tb
|
|
}
|
|
|
|
// SetNil sets a nil field immediately
|
|
func (tb *BatchTableBuilder) SetNil(key string) *BatchTableBuilder {
|
|
tb.state.PushNil()
|
|
tb.state.SetField(tb.index, key)
|
|
return tb
|
|
}
|
|
|
|
// SetTable sets a table field immediately
|
|
func (tb *BatchTableBuilder) SetTable(key string, value any) *BatchTableBuilder {
|
|
tb.otherFields[key] = value
|
|
return tb
|
|
}
|
|
|
|
// SetArray sets an array field immediately
|
|
func (tb *BatchTableBuilder) SetArray(key string, values []any) *BatchTableBuilder {
|
|
tb.otherFields[key] = values
|
|
return tb
|
|
}
|
|
|
|
// Build executes all batched operations and finalizes the table
|
|
func (tb *BatchTableBuilder) Build() error {
|
|
// Batch string fields
|
|
if len(tb.stringFields) > 0 {
|
|
if err := tb.state.BatchSetStringFields(tb.index, tb.stringFields); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Batch number fields
|
|
if len(tb.numberFields) > 0 {
|
|
if err := tb.state.BatchSetNumberFields(tb.index, tb.numberFields); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Batch boolean fields
|
|
if len(tb.boolFields) > 0 {
|
|
if err := tb.state.BatchSetBoolFields(tb.index, tb.boolFields); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Individual operations for complex types
|
|
for key, value := range tb.otherFields {
|
|
if err := tb.state.PushValue(value); err != nil {
|
|
return err
|
|
}
|
|
tb.state.SetField(tb.index, key)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchSetGlobals sets multiple global variables in one C call
|
|
func (s *State) BatchSetGlobals(globals map[string]string) error {
|
|
if len(globals) == 0 {
|
|
return nil
|
|
}
|
|
|
|
names := make([]*C.char, len(globals))
|
|
values := make([]*C.char, len(globals))
|
|
|
|
i := 0
|
|
for name, value := range globals {
|
|
names[i] = C.CString(name)
|
|
values[i] = C.CString(value)
|
|
i++
|
|
}
|
|
|
|
C.batch_set_globals(s.L, &names[0], &values[0], C.int(len(globals)))
|
|
|
|
for j := range len(globals) {
|
|
C.free(unsafe.Pointer(names[j]))
|
|
C.free(unsafe.Pointer(values[j]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchGetGlobals gets multiple global variables, leaving them on stack
|
|
func (s *State) BatchGetGlobals(names []string) error {
|
|
if len(names) == 0 {
|
|
return nil
|
|
}
|
|
|
|
cNames := make([]*C.char, len(names))
|
|
for i, name := range names {
|
|
cNames[i] = C.CString(name)
|
|
}
|
|
|
|
C.batch_get_globals(s.L, &cNames[0], C.int(len(names)))
|
|
|
|
for i := range names {
|
|
C.free(unsafe.Pointer(cNames[i]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchGetTableFields gets multiple fields from a table, leaving them on stack
|
|
func (s *State) BatchGetTableFields(tableIndex int, keys []string) error {
|
|
if len(keys) == 0 {
|
|
return nil
|
|
}
|
|
|
|
cKeys := make([]*C.char, len(keys))
|
|
for i, key := range keys {
|
|
cKeys[i] = C.CString(key)
|
|
}
|
|
|
|
C.batch_get_table_fields(s.L, C.int(tableIndex), &cKeys[0], C.int(len(keys)))
|
|
|
|
for i := range keys {
|
|
C.free(unsafe.Pointer(cKeys[i]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchCheckTypes validates multiple stack positions have expected types
|
|
func (s *State) BatchCheckTypes(checks []TypeCheck) error {
|
|
if len(checks) == 0 {
|
|
return nil
|
|
}
|
|
|
|
indices := make([]C.int, len(checks))
|
|
expectedTypes := make([]C.int, len(checks))
|
|
|
|
for i, check := range checks {
|
|
indices[i] = C.int(check.Index)
|
|
expectedTypes[i] = C.int(check.ExpectedType)
|
|
}
|
|
|
|
result := C.batch_check_types(s.L, &indices[0], &expectedTypes[0], C.int(len(checks)))
|
|
if result != 0 {
|
|
check := checks[result-1]
|
|
actual := s.GetType(check.Index)
|
|
return fmt.Errorf("type mismatch at index %d: expected %s, got %s",
|
|
check.Index, check.ExpectedType, actual)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// BatchAddPackagePaths adds multiple paths to package.path in one operation
|
|
func (s *State) BatchAddPackagePaths(paths []string) error {
|
|
if len(paths) == 0 {
|
|
return nil
|
|
}
|
|
|
|
cPaths := make([]*C.char, len(paths))
|
|
for i, path := range paths {
|
|
normalizedPath := strings.ReplaceAll(path, "\\", "/")
|
|
cPaths[i] = C.CString(normalizedPath)
|
|
}
|
|
|
|
C.batch_add_package_paths(s.L, &cPaths[0], C.int(len(paths)))
|
|
|
|
for i := range paths {
|
|
C.free(unsafe.Pointer(cPaths[i]))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TypeCheck represents a type validation check
|
|
type TypeCheck struct {
|
|
Index int
|
|
ExpectedType LuaType
|
|
}
|
|
|
|
// BatchValuePusher accumulates values for efficient batch pushing
|
|
type BatchValuePusher struct {
|
|
state *State
|
|
values []any
|
|
}
|
|
|
|
// NewBatchValuePusher creates a new batch value pusher
|
|
func (s *State) NewBatchValuePusher() *BatchValuePusher {
|
|
return &BatchValuePusher{
|
|
state: s,
|
|
values: make([]any, 0, 8),
|
|
}
|
|
}
|
|
|
|
// Add queues a value for batch pushing
|
|
func (bvp *BatchValuePusher) Add(value any) *BatchValuePusher {
|
|
bvp.values = append(bvp.values, value)
|
|
return bvp
|
|
}
|
|
|
|
// Push executes the batch push operation
|
|
func (bvp *BatchValuePusher) Push() error {
|
|
if len(bvp.values) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Separate by type for optimal batching
|
|
var strings []string
|
|
var numbers []float64
|
|
var bools []bool
|
|
var others []any
|
|
|
|
for _, v := range bvp.values {
|
|
switch val := v.(type) {
|
|
case string:
|
|
if len(strings) == len(numbers)+len(bools)+len(others) {
|
|
strings = append(strings, val)
|
|
} else {
|
|
others = append(others, val)
|
|
}
|
|
case int:
|
|
if len(numbers) == len(strings)+len(bools)+len(others) {
|
|
numbers = append(numbers, float64(val))
|
|
} else {
|
|
others = append(others, val)
|
|
}
|
|
case float64:
|
|
if len(numbers) == len(strings)+len(bools)+len(others) {
|
|
numbers = append(numbers, val)
|
|
} else {
|
|
others = append(others, val)
|
|
}
|
|
case bool:
|
|
if len(bools) == len(strings)+len(numbers)+len(others) {
|
|
bools = append(bools, val)
|
|
} else {
|
|
others = append(others, val)
|
|
}
|
|
default:
|
|
others = append(others, val)
|
|
}
|
|
}
|
|
|
|
// Push in batches when beneficial
|
|
if len(strings) > 2 && len(numbers) == 0 && len(bools) == 0 && len(others) == 0 {
|
|
return bvp.pushStringBatch(strings)
|
|
}
|
|
if len(numbers) > 2 && len(strings) == 0 && len(bools) == 0 && len(others) == 0 {
|
|
return bvp.pushNumberBatch(numbers)
|
|
}
|
|
if len(bools) > 2 && len(strings) == 0 && len(numbers) == 0 && len(others) == 0 {
|
|
return bvp.pushBoolBatch(bools)
|
|
}
|
|
|
|
// Fall back to individual pushes for mixed types
|
|
for _, value := range bvp.values {
|
|
if err := bvp.state.PushValue(value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (bvp *BatchValuePusher) pushStringBatch(strings []string) error {
|
|
cValues := make([]*C.char, len(strings))
|
|
for i, s := range strings {
|
|
cValues[i] = C.CString(s)
|
|
}
|
|
|
|
for i, cstr := range cValues {
|
|
C.lua_pushstring(bvp.state.L, cstr)
|
|
C.free(unsafe.Pointer(cstr))
|
|
_ = i // Use i to avoid unused variable
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (bvp *BatchValuePusher) pushNumberBatch(numbers []float64) error {
|
|
for _, n := range numbers {
|
|
C.lua_pushnumber(bvp.state.L, C.lua_Number(n))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (bvp *BatchValuePusher) pushBoolBatch(bools []bool) error {
|
|
for _, b := range bools {
|
|
if b {
|
|
C.lua_pushboolean(bvp.state.L, 1)
|
|
} else {
|
|
C.lua_pushboolean(bvp.state.L, 0)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BatchTableReader efficiently reads multiple table configurations
|
|
type BatchTableReader struct {
|
|
state *State
|
|
index int
|
|
}
|
|
|
|
// NewBatchTableReader creates a table reader for efficient field access
|
|
func (s *State) NewBatchTableReader(index int) *BatchTableReader {
|
|
return &BatchTableReader{
|
|
state: s,
|
|
index: s.absIndex(index),
|
|
}
|
|
}
|
|
|
|
// ReadFields reads multiple fields and returns them as a map
|
|
func (btr *BatchTableReader) ReadFields(keys []string) (map[string]any, error) {
|
|
if len(keys) == 0 {
|
|
return make(map[string]any), nil
|
|
}
|
|
|
|
// Use batch operation for many fields
|
|
if len(keys) > 3 {
|
|
startTop := btr.state.GetTop()
|
|
if err := btr.state.BatchGetTableFields(btr.index, keys); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[string]any, len(keys))
|
|
for i, key := range keys {
|
|
if value, err := btr.state.ToValue(startTop + i + 1); err == nil {
|
|
result[key] = value
|
|
}
|
|
}
|
|
|
|
btr.state.SetTop(startTop) // Restore stack
|
|
return result, nil
|
|
}
|
|
|
|
// Individual access for few fields
|
|
result := make(map[string]any, len(keys))
|
|
for _, key := range keys {
|
|
btr.state.GetField(btr.index, key)
|
|
if value, err := btr.state.ToValue(-1); err == nil {
|
|
result[key] = value
|
|
}
|
|
btr.state.Pop(1)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// BatchGlobalManager manages multiple global operations efficiently
|
|
type BatchGlobalManager struct {
|
|
state *State
|
|
pendingSets map[string]string
|
|
pendingGets []string
|
|
}
|
|
|
|
// NewBatchGlobalManager creates a global variable manager
|
|
func (s *State) NewBatchGlobalManager() *BatchGlobalManager {
|
|
return &BatchGlobalManager{
|
|
state: s,
|
|
pendingSets: make(map[string]string),
|
|
pendingGets: make([]string, 0),
|
|
}
|
|
}
|
|
|
|
// QueueSet queues a global variable for batch setting
|
|
func (bgm *BatchGlobalManager) QueueSet(name, value string) *BatchGlobalManager {
|
|
bgm.pendingSets[name] = value
|
|
return bgm
|
|
}
|
|
|
|
// QueueGet queues a global variable for batch getting
|
|
func (bgm *BatchGlobalManager) QueueGet(name string) *BatchGlobalManager {
|
|
bgm.pendingGets = append(bgm.pendingGets, name)
|
|
return bgm
|
|
}
|
|
|
|
// Execute performs all queued operations
|
|
func (bgm *BatchGlobalManager) Execute() (map[string]any, error) {
|
|
// Execute batch sets
|
|
if len(bgm.pendingSets) > 0 {
|
|
if err := bgm.state.BatchSetGlobals(bgm.pendingSets); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Execute batch gets
|
|
var result map[string]any
|
|
if len(bgm.pendingGets) > 0 {
|
|
startTop := bgm.state.GetTop()
|
|
if err := bgm.state.BatchGetGlobals(bgm.pendingGets); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result = make(map[string]any, len(bgm.pendingGets))
|
|
for i, name := range bgm.pendingGets {
|
|
if value, err := bgm.state.ToValue(startTop + i + 1); err == nil {
|
|
result[name] = value
|
|
}
|
|
}
|
|
|
|
bgm.state.SetTop(startTop) // Restore stack
|
|
}
|
|
|
|
return result, nil
|
|
}
|