diff --git a/table.go b/table.go index d13e14d..9486595 100644 --- a/table.go +++ b/table.go @@ -6,7 +6,8 @@ package luajit #include #include -static size_t get_table_length(lua_State *L, int index) { +// Simple direct length check +size_t get_table_length(lua_State *L, int index) { return lua_objlen(L, index); } */ @@ -14,70 +15,53 @@ import "C" import ( "fmt" "strconv" - "sync" ) -// Use a pool to reduce GC pressure when handling many tables -var tablePool = sync.Pool{ - New: func() any { - return make(map[string]any) - }, -} - // GetTableLength returns the length of a table at the given index func (s *State) GetTableLength(index int) int { return int(C.get_table_length(s.L, C.int(index))) } -// getTableFromPool gets a map from the pool and ensures it's empty -func getTableFromPool() map[string]any { - table := tablePool.Get().(map[string]any) - // 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]any) { - tablePool.Put(table) -} - // PushTable pushes a Go map onto the Lua stack as a table func (s *State) PushTable(table map[string]any) error { - // Create table with appropriate capacity hints + // Fast path for array tables + if arr, ok := table[""]; ok { + if floatArr, ok := arr.([]float64); ok { + s.CreateTable(len(floatArr), 0) + for i, v := range floatArr { + s.PushNumber(float64(i + 1)) + s.PushNumber(v) + s.SetTable(-3) + } + return nil + } else if anyArr, ok := arr.([]any); ok { + s.CreateTable(len(anyArr), 0) + for i, v := range anyArr { + s.PushNumber(float64(i + 1)) + if err := s.PushValue(v); err != nil { + return err + } + s.SetTable(-3) + } + return nil + } + } + + // Regular table case - optimize capacity hint s.CreateTable(0, len(table)) - // Add each key-value pair + // Add each key-value pair directly for k, v := range table { - // Push key s.PushString(k) - - // Push value if err := s.PushValue(v); err != nil { return err } - - // t[k] = v s.SetTable(-3) } - // Return pooled tables to the pool - if isPooledTable(table) { - putTableToPool(table) - } - return nil } -// isPooledTable detects if a table came from our pool -func isPooledTable(table map[string]any) bool { - // Check for our special marker - used for array tables in the pool - _, hasEmptyKey := table[""] - return len(table) == 1 && hasEmptyKey -} - // ToTable converts a Lua table at the given index to a Go map func (s *State) ToTable(index int) (map[string]any, error) { absIdx := s.absIndex(index) @@ -88,34 +72,41 @@ func (s *State) ToTable(index int) (map[string]any, error) { // Try to detect array-like tables first length := s.GetTableLength(absIdx) if length > 0 { - // Check if this is an array-like table - isArray := true - array := make([]float64, length) + // Fast path for common array case + allNumbers := true - for i := 1; i <= length; i++ { + // Sample first few values to check if it's likely an array of numbers + for i := 1; i <= min(length, 5); i++ { s.PushNumber(float64(i)) s.GetTable(absIdx) if !s.IsNumber(-1) { - isArray = false + allNumbers = false s.Pop(1) break } - - array[i-1] = s.ToNumber(-1) s.Pop(1) } - if isArray { - // Return array as a special pooled table with empty key - result := getTableFromPool() + if allNumbers { + // Efficiently extract array values + array := make([]float64, length) + for i := 1; i <= length; i++ { + s.PushNumber(float64(i)) + s.GetTable(absIdx) + array[i-1] = s.ToNumber(-1) + s.Pop(1) + } + + // Return array as a special table with empty key + result := make(map[string]any, 1) result[""] = array return result, nil } } - // Handle regular table - table := getTableFromPool() + // Handle regular table with pre-allocated capacity + table := make(map[string]any, max(length, 8)) // Iterate through all key-value pairs s.PushNil() // Start iteration with nil key @@ -139,12 +130,11 @@ func (s *State) ToTable(index int) (map[string]any, error) { // Convert and store the value value, err := s.ToValue(-1) if err != nil { - s.Pop(2) // Pop both key and value - putTableToPool(table) // Return the table to the pool on error + s.Pop(2) // Pop both key and value return nil, err } - // Handle nested array tables + // Unwrap nested array tables if m, ok := value.(map[string]any); ok { if arr, ok := m[""]; ok { value = arr @@ -157,3 +147,18 @@ func (s *State) ToTable(index int) (map[string]any, error) { return table, nil } + +// Helper functions for min/max operations +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +}