package luajit /* #include #include #include #include static size_t get_table_length(lua_State *L, int index) { return lua_objlen(L, index); } */ import "C" import ( "fmt" "strconv" "sync" ) // Use a pool to reduce GC pressure when handling many tables var tablePool = sync.Pool{ New: func() interface{} { return make(map[string]interface{}) }, } // 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]interface{} { table := tablePool.Get().(map[string]interface{}) // 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]interface{}) { tablePool.Put(table) } // PushTable pushes a Go map onto the Lua stack as a table func (s *State) PushTable(table map[string]interface{}) error { // Create table with appropriate capacity hints s.CreateTable(0, len(table)) // Add each key-value pair 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]interface{}) 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]interface{}, 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 { // Check if this is an array-like table isArray := true array := make([]float64, length) for i := 1; i <= length; i++ { s.PushNumber(float64(i)) s.GetTable(absIdx) if !s.IsNumber(-1) { isArray = 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() result[""] = array return result, nil } } // Handle regular table table := getTableFromPool() // 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 putTableToPool(table) // Return the table to the pool on error return nil, err } // Handle nested array tables if m, ok := value.(map[string]interface{}); ok { if arr, ok := m[""]; ok { value = arr } } table[key] = value s.Pop(1) // Pop value, leave key for next iteration } return table, nil }