summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lua.go113
-rw-r--r--lua_common.go139
-rw-r--r--luajit.go113
3 files changed, 149 insertions, 216 deletions
diff --git a/lua.go b/lua.go
index f90ba38..fb0b72a 100644
--- a/lua.go
+++ b/lua.go
@@ -25,9 +25,7 @@ import "C"
import (
"errors"
"fmt"
- "reflect"
"runtime/cgo"
- "slices"
"time"
"unsafe"
)
@@ -205,6 +203,11 @@ func (l *Lua) GetTableItem(key string) {
C.lua_getfield(l.l, -1, C.CString(key))
}
+// RawGetI pushes onto the stack value from table from tableOffset at index i.
+func (l *Lua) RawGetI(tableOffset, i int) {
+ C.lua_rawgeti(l.l, C.int(tableOffset), C.longlong(i))
+}
+
// PushAny pushes value v onto the stack.
func (l *Lua) PushAny(v any) error {
switch v.(type) {
@@ -348,112 +351,6 @@ func (l *Lua) LoadAndCall(code string) error {
return nil
}
-// Scan scans values from the Lua stack into vars according to their types.
-func (l *Lua) Scan(vars ...any) error {
- slices.Reverse(vars)
- for i, v := range vars {
- t := reflect.TypeOf(v)
- // unwrap pointers
- for t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
- tk := t.Kind()
- if t.Name() == "LuaRef" {
- if l.IsFunction(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be function",
- len(vars)-i,
- )
- }
- *v.(*LuaRef) = l.PopToRef()
- } else if t.String() == "cgo.Handle" {
- if l.IsNumber(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be Go handler",
- len(vars)-i,
- )
- }
- *v.(*cgo.Handle) = cgo.Handle(uintptr(l.ToInt(-1)))
- l.Pop(1)
- } else if tk == reflect.String {
- if l.IsString(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be string",
- len(vars)-i,
- )
- }
- *v.(*string) = l.ToString(-1)
- l.Pop(1)
- } else if tk == reflect.Int {
- if l.IsNumber(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be number",
- len(vars)-i,
- )
- }
- *v.(*int) = l.ToInt(-1)
- l.Pop(1)
- } else if tk == reflect.Map && t.Key().Kind() == reflect.String {
- // TODO: should be possible to use maps with any types
- // of value, not only strings.
- vm, _ := v.(*map[string]string)
- l.PushNil()
- for l.Next() {
- if !l.IsString(-1) {
- return fmt.Errorf(
- "map arg #%d must only have string values",
- len(vars)-i,
- )
- }
- if !l.IsString(-2) {
- return fmt.Errorf(
- "map arg #%d must only have string keys",
- len(vars)-i,
- )
- }
- v := l.ToString(-1)
- l.Pop(1)
- // We must not pop the item key from the stack
- // because otherwise C.lua_next won't work
- // properly.
- k := l.ToString(-1)
- (*vm)[k] = v
- }
- l.Pop(1)
- } else if tk == reflect.Slice &&
- t.Elem().Kind() == reflect.Interface {
- if !l.IsTable(-1) {
- return fmt.Errorf(
- "passed arg #%d must be a table",
- len(vars)-i,
- )
- }
- va, _ := v.(*[]any)
- l.PushNil()
- for l.Next() {
- var v any = nil
- if l.IsString(-1) {
- v = l.ToString(-1)
- } else if l.IsNumber(-1) {
- v = l.ToInt(-1)
- } else if l.IsBoolean(-1) {
- v = l.ToBoolean(-1)
- } else if l.IsNil(-1) {
- v = nil
- } else {
- return fmt.Errorf("unknown value in array")
- }
- l.Pop(1)
- (*va) = append((*va), v)
- }
- l.Pop(1)
- } else {
- return fmt.Errorf("unknown var type: %s", t)
- }
- }
- return nil
-}
-
// RestoreStackFunc remembers the Lua stack size and then restores it when a
// returned function is called. It's a helper function to avoid stack leakage.
func (l *Lua) RestoreStackFunc() func () {
diff --git a/lua_common.go b/lua_common.go
new file mode 100644
index 0000000..4e58d69
--- /dev/null
+++ b/lua_common.go
@@ -0,0 +1,139 @@
+package main
+
+import (
+ "reflect"
+ "slices"
+ "fmt"
+ "runtime/cgo"
+)
+
+// Scan scans values from the Lua stack into vars according to their types.
+func (l *Lua) Scan(vars ...any) error {
+ slices.Reverse(vars)
+ for i, v := range vars {
+ t := reflect.TypeOf(v)
+ // unwrap pointers
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ tk := t.Kind()
+ if t.Name() == "LuaRef" {
+ if l.IsFunction(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be function",
+ len(vars)-i,
+ )
+ }
+ *v.(*LuaRef) = l.PopToRef()
+ } else if t.String() == "cgo.Handle" {
+ if l.IsNumber(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be Go handler",
+ len(vars)-i,
+ )
+ }
+ *v.(*cgo.Handle) = cgo.Handle(uintptr(l.ToInt(-1)))
+ l.Pop(1)
+ } else if tk == reflect.String {
+ if l.IsString(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be string",
+ len(vars)-i,
+ )
+ }
+ *v.(*string) = l.ToString(-1)
+ l.Pop(1)
+ } else if tk == reflect.Int {
+ if l.IsNumber(-1) == false {
+ return fmt.Errorf(
+ "passed arg #%d must be number",
+ len(vars)-i,
+ )
+ }
+ *v.(*int) = l.ToInt(-1)
+ l.Pop(1)
+ } else if tk == reflect.Map && t.Key().Kind() == reflect.String {
+ // TODO: should be possible to use maps with any types
+ // of value, not only strings.
+ vm, _ := v.(*map[string]string)
+ l.PushNil()
+ for l.Next() {
+ if !l.IsString(-1) {
+ return fmt.Errorf(
+ "map arg #%d must only have string values",
+ len(vars)-i,
+ )
+ }
+ if !l.IsString(-2) {
+ return fmt.Errorf(
+ "map arg #%d must only have string keys",
+ len(vars)-i,
+ )
+ }
+ v := l.ToString(-1)
+ l.Pop(1)
+ // We must not pop the item key from the stack
+ // because otherwise C.lua_next won't work
+ // properly.
+ k := l.ToString(-1)
+ (*vm)[k] = v
+ }
+ l.Pop(1)
+ } else if tk == reflect.Slice && t.Elem().Kind() == reflect.Interface {
+ if !l.IsTable(-1) {
+ return fmt.Errorf(
+ "passed arg #%d must be a table",
+ len(vars)-i,
+ )
+ }
+ va, _ := v.(*[]any)
+
+ // Check if table contains element "n" with
+ // a count of elements. Useful when an array may
+ // contain nils.
+ l.GetTableItem("n")
+ n := 0
+ if l.IsNumber(-1) {
+ n = l.ToInt(-1)
+ }
+ l.Pop(1)
+
+ scanAndAppend := func () error {
+ var v any = nil
+ if l.IsString(-1) {
+ v = l.ToString(-1)
+ } else if l.IsNumber(-1) {
+ v = l.ToInt(-1)
+ } else if l.IsBoolean(-1) {
+ v = l.ToBoolean(-1)
+ } else if l.IsNil(-1) {
+ v = nil
+ } else {
+ return fmt.Errorf("unknown value in array")
+ }
+ l.Pop(1)
+ (*va) = append((*va), v)
+ return nil
+ }
+ if n > 0 {
+ for i := 0; i < n; i++ {
+ l.RawGetI(-1, i + 1)
+ if err := scanAndAppend(); err != nil {
+ return err
+ }
+ }
+ } else {
+ l.PushNil()
+ for l.Next() {
+ if err := scanAndAppend(); err != nil {
+ return err
+ }
+ }
+ }
+ l.Pop(1)
+ } else {
+ return fmt.Errorf("unknown var type: %s", t)
+ }
+ }
+ return nil
+}
diff --git a/luajit.go b/luajit.go
index 40b91ba..0c51f13 100644
--- a/luajit.go
+++ b/luajit.go
@@ -25,9 +25,7 @@ import "C"
import (
"errors"
"fmt"
- "reflect"
"runtime/cgo"
- "slices"
"time"
"unsafe"
)
@@ -199,6 +197,11 @@ func (l *Lua) GetTableItem(key string) {
C.lua_getfield(l.l, -1, C.CString(key))
}
+// RawGetI pushes onto the stack value from table from tableOffset at index i.
+func (l *Lua) RawGetI(tableOffset, i int) {
+ C.lua_rawgeti(l.l, C.int(tableOffset), C.int(i))
+}
+
// PushAny pushes value v onto the stack.
func (l *Lua) PushAny(v any) error {
switch v.(type) {
@@ -341,112 +344,6 @@ func (l *Lua) LoadAndCall(code string) error {
return nil
}
-// Scan scans values from the Lua stack into vars according to their types.
-func (l *Lua) Scan(vars ...any) error {
- slices.Reverse(vars)
- for i, v := range vars {
- t := reflect.TypeOf(v)
- // unwrap pointers
- for t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
- tk := t.Kind()
- if t.Name() == "LuaRef" {
- if l.IsFunction(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be function",
- len(vars)-i,
- )
- }
- *v.(*LuaRef) = l.PopToRef()
- } else if t.String() == "cgo.Handle" {
- if l.IsNumber(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be Go handler",
- len(vars)-i,
- )
- }
- *v.(*cgo.Handle) = cgo.Handle(uintptr(l.ToInt(-1)))
- l.Pop(1)
- } else if tk == reflect.String {
- if l.IsString(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be string",
- len(vars)-i,
- )
- }
- *v.(*string) = l.ToString(-1)
- l.Pop(1)
- } else if tk == reflect.Int {
- if l.IsNumber(-1) == false {
- return fmt.Errorf(
- "passed arg #%d must be number",
- len(vars)-i,
- )
- }
- *v.(*int) = l.ToInt(-1)
- l.Pop(1)
- } else if tk == reflect.Map && t.Key().Kind() == reflect.String {
- // TODO: should be possible to use maps with any types
- // of value, not only strings.
- vm, _ := v.(*map[string]string)
- l.PushNil()
- for l.Next() {
- if !l.IsString(-1) {
- return fmt.Errorf(
- "map arg #%d must only have string values",
- len(vars)-i,
- )
- }
- if !l.IsString(-2) {
- return fmt.Errorf(
- "map arg #%d must only have string keys",
- len(vars)-i,
- )
- }
- v := l.ToString(-1)
- l.Pop(1)
- // We must not pop the item key from the stack
- // because otherwise C.lua_next won't work
- // properly.
- k := l.ToString(-1)
- (*vm)[k] = v
- }
- l.Pop(1)
- } else if tk == reflect.Slice &&
- t.Elem().Kind() == reflect.Interface {
- if !l.IsTable(-1) {
- return fmt.Errorf(
- "passed arg #%d must be a table",
- len(vars)-i,
- )
- }
- va, _ := v.(*[]any)
- l.PushNil()
- for l.Next() {
- var v any = nil
- if l.IsString(-1) {
- v = l.ToString(-1)
- } else if l.IsNumber(-1) {
- v = l.ToInt(-1)
- } else if l.IsBoolean(-1) {
- v = l.ToBoolean(-1)
- } else if l.IsNil(-1) {
- v = nil
- } else {
- return fmt.Errorf("unknown value in array")
- }
- l.Pop(1)
- (*va) = append((*va), v)
- }
- l.Pop(1)
- } else {
- return fmt.Errorf("unknown var type: %s", t)
- }
- }
- return nil
-}
-
// RestoreStackFunc remembers the Lua stack size and then restores it when a
// returned function is called. It's a helper function to avoid stack leakage.
func (l *Lua) RestoreStackFunc() func () {