From 3a4cb27473d1af2cc76c42ba4ffe5e4b130f1490 Mon Sep 17 00:00:00 2001 From: Sky Johnson Date: Wed, 2 Jul 2025 11:23:16 -0500 Subject: [PATCH] enhanced batch operations --- batch.go | 459 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 456 insertions(+), 3 deletions(-) diff --git a/batch.go b/batch.go index 29d7169..b1b98d9 100644 --- a/batch.go +++ b/batch.go @@ -8,7 +8,7 @@ package luajit // 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) { + 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]); @@ -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, - char **keys, double *values, int count) { + 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]); @@ -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, - char **keys, int *values, int count) { + 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]); @@ -124,9 +124,118 @@ int batch_extract_bool_array(lua_State *L, int table_idx, int *buffer, int lengt } 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" ) @@ -445,3 +554,347 @@ func (tb *BatchTableBuilder) Build() error { 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 +}