diff --git a/batch.go b/batch.go new file mode 100644 index 0000000..29d7169 --- /dev/null +++ b/batch.go @@ -0,0 +1,447 @@ +package luajit + +/* +#include +#include +#include +#include + +// 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; +} +*/ +import "C" +import ( + "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 +}