optimize table
This commit is contained in:
parent
fed0c2ad34
commit
29679349ef
121
table.go
121
table.go
|
@ -6,7 +6,8 @@ package luajit
|
||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
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);
|
return lua_objlen(L, index);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -14,70 +15,53 @@ import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"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
|
// GetTableLength returns the length of a table at the given index
|
||||||
func (s *State) GetTableLength(index int) int {
|
func (s *State) GetTableLength(index int) int {
|
||||||
return int(C.get_table_length(s.L, C.int(index)))
|
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
|
// PushTable pushes a Go map onto the Lua stack as a table
|
||||||
func (s *State) PushTable(table map[string]any) error {
|
func (s *State) PushTable(table map[string]any) error {
|
||||||
// Create table with appropriate capacity hints
|
// Fast path for array tables
|
||||||
s.CreateTable(0, len(table))
|
if arr, ok := table[""]; ok {
|
||||||
|
if floatArr, ok := arr.([]float64); ok {
|
||||||
// Add each key-value pair
|
s.CreateTable(len(floatArr), 0)
|
||||||
for k, v := range table {
|
for i, v := range floatArr {
|
||||||
// Push key
|
s.PushNumber(float64(i + 1))
|
||||||
s.PushString(k)
|
s.PushNumber(v)
|
||||||
|
s.SetTable(-3)
|
||||||
// Push value
|
}
|
||||||
|
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 {
|
if err := s.PushValue(v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
s.SetTable(-3)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// t[k] = v
|
// 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)
|
s.SetTable(-3)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return pooled tables to the pool
|
|
||||||
if isPooledTable(table) {
|
|
||||||
putTableToPool(table)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
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
|
// ToTable converts a Lua table at the given index to a Go map
|
||||||
func (s *State) ToTable(index int) (map[string]any, error) {
|
func (s *State) ToTable(index int) (map[string]any, error) {
|
||||||
absIdx := s.absIndex(index)
|
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
|
// Try to detect array-like tables first
|
||||||
length := s.GetTableLength(absIdx)
|
length := s.GetTableLength(absIdx)
|
||||||
if length > 0 {
|
if length > 0 {
|
||||||
// Check if this is an array-like table
|
// Fast path for common array case
|
||||||
isArray := true
|
allNumbers := true
|
||||||
array := make([]float64, length)
|
|
||||||
|
|
||||||
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.PushNumber(float64(i))
|
||||||
s.GetTable(absIdx)
|
s.GetTable(absIdx)
|
||||||
|
|
||||||
if !s.IsNumber(-1) {
|
if !s.IsNumber(-1) {
|
||||||
isArray = false
|
allNumbers = false
|
||||||
s.Pop(1)
|
s.Pop(1)
|
||||||
break
|
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)
|
array[i-1] = s.ToNumber(-1)
|
||||||
s.Pop(1)
|
s.Pop(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isArray {
|
// Return array as a special table with empty key
|
||||||
// Return array as a special pooled table with empty key
|
result := make(map[string]any, 1)
|
||||||
result := getTableFromPool()
|
|
||||||
result[""] = array
|
result[""] = array
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle regular table
|
// Handle regular table with pre-allocated capacity
|
||||||
table := getTableFromPool()
|
table := make(map[string]any, max(length, 8))
|
||||||
|
|
||||||
// Iterate through all key-value pairs
|
// Iterate through all key-value pairs
|
||||||
s.PushNil() // Start iteration with nil key
|
s.PushNil() // Start iteration with nil key
|
||||||
|
@ -140,11 +131,10 @@ func (s *State) ToTable(index int) (map[string]any, error) {
|
||||||
value, err := s.ToValue(-1)
|
value, err := s.ToValue(-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Pop(2) // Pop both key and value
|
s.Pop(2) // Pop both key and value
|
||||||
putTableToPool(table) // Return the table to the pool on error
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle nested array tables
|
// Unwrap nested array tables
|
||||||
if m, ok := value.(map[string]any); ok {
|
if m, ok := value.(map[string]any); ok {
|
||||||
if arr, ok := m[""]; ok {
|
if arr, ok := m[""]; ok {
|
||||||
value = arr
|
value = arr
|
||||||
|
@ -157,3 +147,18 @@ func (s *State) ToTable(index int) (map[string]any, error) {
|
||||||
|
|
||||||
return table, nil
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user