1
0

enhanced batch operations

This commit is contained in:
Sky Johnson 2025-07-02 11:23:16 -05:00
parent cbfc8c184e
commit 3a4cb27473

459
batch.go
View File

@ -8,7 +8,7 @@ package luajit
// Batch table field setting - single C call for multiple fields // Batch table field setting - single C call for multiple fields
int batch_set_string_fields(lua_State *L, int table_idx, int batch_set_string_fields(lua_State *L, int table_idx,
char **keys, char **values, int count) { char **keys, char **values, int count) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
lua_pushstring(L, values[i]); lua_pushstring(L, values[i]);
lua_setfield(L, table_idx, keys[i]); lua_setfield(L, table_idx, keys[i]);
@ -17,7 +17,7 @@ int batch_set_string_fields(lua_State *L, int table_idx,
} }
int batch_set_number_fields(lua_State *L, int table_idx, int batch_set_number_fields(lua_State *L, int table_idx,
char **keys, double *values, int count) { char **keys, double *values, int count) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
lua_pushnumber(L, values[i]); lua_pushnumber(L, values[i]);
lua_setfield(L, table_idx, keys[i]); lua_setfield(L, table_idx, keys[i]);
@ -26,7 +26,7 @@ int batch_set_number_fields(lua_State *L, int table_idx,
} }
int batch_set_bool_fields(lua_State *L, int table_idx, int batch_set_bool_fields(lua_State *L, int table_idx,
char **keys, int *values, int count) { char **keys, int *values, int count) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
lua_pushboolean(L, values[i]); lua_pushboolean(L, values[i]);
lua_setfield(L, table_idx, keys[i]); lua_setfield(L, table_idx, keys[i]);
@ -124,9 +124,118 @@ int batch_extract_bool_array(lua_State *L, int table_idx, int *buffer, int lengt
} }
return 0; 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 "C"
import ( import (
"fmt"
"strings"
"unsafe" "unsafe"
) )
@ -445,3 +554,347 @@ func (tb *BatchTableBuilder) Build() error {
return nil 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
}