package luajit /* #include #include #include #include // Simple direct length check size_t get_table_length(lua_State *L, int index) { return lua_objlen(L, index); } */ import "C" import ( "fmt" "strconv" ) // 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))) } // PushTable pushes a Go map onto the Lua stack as a table func (s *State) PushTable(table map[string]any) error { // 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 directly for k, v := range table { s.PushString(k) if err := s.PushValue(v); err != nil { return err } s.SetTable(-3) } return nil } // 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) if !s.IsTable(absIdx) { return nil, fmt.Errorf("value at index %d is not a table", index) } // Try to detect array-like tables first length := s.GetTableLength(absIdx) if length > 0 { // Fast path for common array case allNumbers := true // 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) { allNumbers = false s.Pop(1) break } s.Pop(1) } 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 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 for s.Next(absIdx) { // Stack now has key at -2 and value at -1 // Convert key to string var key string keyType := s.GetType(-2) switch keyType { case TypeString: key = s.ToString(-2) case TypeNumber: key = strconv.FormatFloat(s.ToNumber(-2), 'g', -1, 64) default: // Skip non-string/non-number keys s.Pop(1) // Pop value, leave key for next iteration continue } // Convert and store the value value, err := s.ToValue(-1) if err != nil { s.Pop(2) // Pop both key and value return nil, err } // Unwrap nested array tables if m, ok := value.(map[string]any); ok { if arr, ok := m[""]; ok { value = arr } } table[key] = value s.Pop(1) // Pop value, leave key for next iteration } 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 }