Somewhat smarter strategy for error_guard calls, less ungodly slow.

Also add raw_length table function
This commit is contained in:
kyren 2017-06-05 00:41:48 -04:00
parent e4052bb4d4
commit b3218137e1
5 changed files with 62 additions and 45 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rlua" name = "rlua"
version = "0.4.3" version = "0.4.4"
authors = ["kyren <catherine@chucklefish.org>"] authors = ["kyren <catherine@chucklefish.org>"]
description = "High level bindings to Lua 5.3" description = "High level bindings to Lua 5.3"
repository = "https://github.com/chucklefish/rlua" repository = "https://github.com/chucklefish/rlua"

View File

@ -106,6 +106,7 @@ extern "C" {
pub fn lua_setmetatable(state: *mut lua_State, index: c_int); pub fn lua_setmetatable(state: *mut lua_State, index: c_int);
pub fn lua_len(state: *mut lua_State, index: c_int); pub fn lua_len(state: *mut lua_State, index: c_int);
pub fn lua_rawlen(state: *mut lua_State, index: c_int) -> usize;
pub fn lua_next(state: *mut lua_State, index: c_int) -> c_int; pub fn lua_next(state: *mut lua_State, index: c_int) -> c_int;
pub fn lua_rawequal(state: *mut lua_State, index1: c_int, index2: c_int) -> c_int; pub fn lua_rawequal(state: *mut lua_State, index1: c_int, index2: c_int) -> c_int;

View File

@ -152,21 +152,38 @@ pub struct LuaTable<'lua>(LuaRef<'lua>);
impl<'lua> LuaTable<'lua> { impl<'lua> LuaTable<'lua> {
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> { pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> {
let lua = self.0.lua; let lua = self.0.lua;
let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?;
unsafe { unsafe {
stack_guard(lua.state, 0, || { error_guard(lua.state, 0, 0, |state| {
check_stack(lua.state, 3)?; check_stack(state, 3)?;
lua.push_ref(lua.state, &self.0); lua.push_ref(state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?)?; lua.push_value(state, key)?;
lua.push_value(lua.state, value.to_lua(lua)?)?; lua.push_value(state, value)?;
error_guard(lua.state, 3, 0, |state| {
ffi::lua_settable(state, -3); ffi::lua_settable(state, -3);
Ok(()) Ok(())
})?;
Ok(())
}) })
} }
} }
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
unsafe {
let res = error_guard(lua.state, 0, 0, |state| {
check_stack(state, 2)?;
lua.push_ref(state, &self.0);
lua.push_value(state, key.to_lua(lua)?)?;
ffi::lua_gettable(state, -2);
let res = lua.pop_value(state)?;
ffi::lua_pop(state, 1);
Ok(res)
})?;
V::from_lua(res, lua)
}
}
/// Set a field in the table, without invoking metamethods
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> { pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> LuaResult<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
@ -182,24 +199,7 @@ impl<'lua> LuaTable<'lua> {
} }
} }
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> { /// Get a field in the table, without invoking metamethods
let lua = self.0.lua;
unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 2)?;
lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?)?;
error_guard(lua.state, 2, 2, |state| {
ffi::lua_gettable(state, -2);
Ok(())
})?;
let res = V::from_lua(lua.pop_value(lua.state)?, lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res)
})
}
}
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> { pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> LuaResult<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
@ -217,12 +217,27 @@ impl<'lua> LuaTable<'lua> {
/// Equivalent to the result of the lua '#' operator. /// Equivalent to the result of the lua '#' operator.
pub fn length(&self) -> LuaResult<LuaInteger> { pub fn length(&self) -> LuaResult<LuaInteger> {
let lua = self.0.lua;
unsafe {
error_guard(lua.state, 0, 0, |state| {
check_stack(state, 1)?;
lua.push_ref(state, &self.0);
Ok(ffi::luaL_len(state, -1))
})
}
}
/// Equivalent to the result of the lua '#' operator, without invoking the
/// __len metamethod.
pub fn raw_length(&self) -> LuaResult<LuaInteger> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_guard(lua.state, 0, || { stack_guard(lua.state, 0, || {
check_stack(lua.state, 1)?; check_stack(lua.state, 1)?;
lua.push_ref(lua.state, &self.0); lua.push_ref(lua.state, &self.0);
error_guard(lua.state, 1, 0, |state| Ok(ffi::luaL_len(state, -1))) let len = ffi::lua_rawlen(lua.state, -1);
ffi::lua_pop(lua.state, 1);
Ok(len as LuaInteger)
}) })
} }
} }
@ -264,7 +279,7 @@ impl<'lua> LuaTable<'lua> {
check_stack(lua.state, 4)?; check_stack(lua.state, 4)?;
lua.push_ref(lua.state, &self.0); lua.push_ref(lua.state, &self.0);
let len = error_guard(lua.state, 1, 1, |state| Ok(ffi::luaL_len(state, -1)))?; let len = ffi::lua_rawlen(lua.state, -1) as ffi::lua_Integer;
ffi::lua_pushnil(lua.state); ffi::lua_pushnil(lua.state);
while ffi::lua_next(lua.state, -2) != 0 { while ffi::lua_next(lua.state, -2) != 0 {
@ -1271,14 +1286,3 @@ impl Lua {
static LUA_USERDATA_REGISTRY_KEY: u8 = 0; static LUA_USERDATA_REGISTRY_KEY: u8 = 0;
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0; static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
static TOP_STATE_REGISTRY_KEY: u8 = 0; static TOP_STATE_REGISTRY_KEY: u8 = 0;
// If the return code indicates an error, pops the error off of the stack and
// returns Err. If the error was actually a rust panic, clears the current lua
// stack and panics.
unsafe fn handle_error(state: *mut ffi::lua_State, ret: c_int) -> LuaResult<()> {
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
Err(pop_error(state))
} else {
Ok(())
}
}

View File

@ -558,9 +558,10 @@ fn test_table_error() {
.unwrap(); .unwrap();
let bad_table: LuaTable = lua.get("table").unwrap(); let bad_table: LuaTable = lua.get("table").unwrap();
assert!(bad_table.set("key", 1).is_err()); assert!(bad_table.set(1, 1).is_err());
assert!(bad_table.get::<_, i32>("key").is_err()); assert!(bad_table.get::<_, i32>(1).is_err());
assert!(bad_table.length().is_err()); assert!(bad_table.length().is_err());
assert!(bad_table.raw_set("key", 1).is_ok()); assert!(bad_table.raw_set(1, 1).is_ok());
assert!(bad_table.raw_get::<_, i32>("key").is_ok()); assert!(bad_table.raw_get::<_, i32>(1).is_ok());
assert_eq!(bad_table.raw_length().unwrap(), 1);
} }

View File

@ -102,6 +102,17 @@ pub unsafe fn error_guard<F, R>(state: *mut ffi::lua_State,
Ok(res.unwrap()) Ok(res.unwrap())
} }
// If the return code indicates an error, pops the error off of the stack and
// returns Err. If the error was actually a rust panic, clears the current lua
// stack and panics.
pub unsafe fn handle_error(state: *mut ffi::lua_State, ret: c_int) -> LuaResult<()> {
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
Err(pop_error(state))
} else {
Ok(())
}
}
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) { pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
} }