A lot of performance changes.

Okay, so this is kind of a mega-commit of a lot of performance related changes
to rlua, some of which are pretty complicated.

There are some small improvements here and there, but most of the benefits of
this change are from a few big changes.  The simplest big change is that there
is now `protect_lua` as well as `protect_lua_call`, which allows skipping a
lightuserdata parameter and some stack manipulation in some cases.  Second
simplest is the change to use Vec instead of VecDeque for MultiValue, and to
have MultiValue be used as a sort of "backwards-only" Vec so that ToLuaMulti /
FromLuaMulti still work correctly.

The most complex change, though, is a change to the way LuaRef works, so that
LuaRef can optionally point into the Lua stack instead of only registry values.
At state creation a set number of stack slots is reserved for the first N LuaRef
types (currently 16), and space for these are also allocated separately
allocated at callback time.  There is a huge breaking change here, which is that
now any LuaRef types MUST only be used with the Lua on which they were created,
and CANNOT be used with any other Lua callback instance.  This mostly will
affect people using LuaRef types from inside a scope callback, but hopefully in
those cases `Function::bind` will be a suitable replacement.  On the plus side,
the rules for LuaRef types are easier to state now.

There is probably more easy-ish perf on the table here, but here's the
preliminary results, based on my very limited benchmarks:

create table            time:   [314.13 ns 315.71 ns 317.44 ns]
                        change: [-36.154% -35.670% -35.205%] (p = 0.00 < 0.05)
create array 10         time:   [2.9731 us 2.9816 us 2.9901 us]
                        change: [-16.996% -16.600% -16.196%] (p = 0.00 < 0.05)
                        Performance has improved.
create string table 10  time:   [5.6904 us 5.7164 us 5.7411 us]
                        change: [-53.536% -53.309% -53.079%] (p = 0.00 < 0.05)
                        Performance has improved.
call add function 3 10  time:   [5.1134 us 5.1222 us 5.1320 us]
                        change: [-4.1095% -3.6910% -3.1781%] (p = 0.00 < 0.05)
                        Performance has improved.
call callback add 2 10  time:   [5.4408 us 5.4480 us 5.4560 us]
                        change: [-6.4203% -5.7780% -5.0013%] (p = 0.00 < 0.05)
                        Performance has improved.
call callback append 10 time:   [9.8243 us 9.8410 us 9.8586 us]
                        change: [-26.937% -26.702% -26.469%] (p = 0.00 < 0.05)
                        Performance has improved.
create registry 10      time:   [3.7005 us 3.7089 us 3.7174 us]
                        change: [-8.4965% -8.1042% -7.6926%] (p = 0.00 < 0.05)
                        Performance has improved.

I think that a lot of these benchmarks are too "easy", and most API usage is
going to be more like the 'create string table 10' benchmark, where there are a
lot of handles and tables and strings, so I think that 25%-50% improvement is a
good guess for most use cases.
This commit is contained in:
kyren 2018-03-11 23:20:10 -04:00
parent 84ee394b1d
commit 601e9f4cac
13 changed files with 729 additions and 497 deletions

View File

@ -2,7 +2,7 @@ use std::collections::{BTreeMap, HashMap};
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::string::String as StdString; use std::string::String as StdString;
use error::*; use error::{Error, Result};
use types::{Integer, LightUserData, Number}; use types::{Integer, LightUserData, Number};
use string::String; use string::String;
use table::Table; use table::Table;

View File

@ -123,6 +123,7 @@ extern "C" {
pub fn lua_rotate(state: *mut lua_State, index: c_int, n: c_int); pub fn lua_rotate(state: *mut lua_State, index: c_int, n: c_int);
pub fn lua_copy(state: *mut lua_State, from: c_int, to: c_int); pub fn lua_copy(state: *mut lua_State, from: c_int, to: c_int);
pub fn lua_absindex(state: *mut lua_State, index: c_int) -> c_int; pub fn lua_absindex(state: *mut lua_State, index: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
pub fn lua_isinteger(state: *mut lua_State, index: c_int) -> c_int; pub fn lua_isinteger(state: *mut lua_State, index: c_int) -> c_int;
pub fn lua_isnumber(state: *mut lua_State, index: c_int) -> c_int; pub fn lua_isnumber(state: *mut lua_State, index: c_int) -> c_int;

View File

@ -2,8 +2,9 @@ use std::ptr;
use std::os::raw::c_int; use std::os::raw::c_int;
use ffi; use ffi;
use error::*; use error::{Error, Result};
use util::*; use util::{check_stack, check_stack_err, error_traceback, pop_error, protect_lua_closure,
stack_guard};
use types::LuaRef; use types::LuaRef;
use value::{FromLuaMulti, MultiValue, ToLuaMulti}; use value::{FromLuaMulti, MultiValue, ToLuaMulti};
@ -63,16 +64,16 @@ impl<'lua> Function<'lua> {
pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> { pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
check_stack_err(lua.state, nargs + 3)?; check_stack_err(lua.state, nargs + 3)?;
ffi::lua_pushcfunction(lua.state, error_traceback); ffi::lua_pushcfunction(lua.state, error_traceback);
let stack_start = ffi::lua_gettop(lua.state); let stack_start = ffi::lua_gettop(lua.state);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
for arg in args { for arg in args {
lua.push_value(lua.state, arg); lua.push_value(arg);
} }
let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start); let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start);
if ret != ffi::LUA_OK { if ret != ffi::LUA_OK {
@ -82,7 +83,7 @@ impl<'lua> Function<'lua> {
let mut results = MultiValue::new(); let mut results = MultiValue::new();
check_stack(lua.state, 2); check_stack(lua.state, 2);
for _ in 0..nresults { for _ in 0..nresults {
results.push_front(lua.pop_value(lua.state)); results.push_front(lua.pop_value());
} }
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);
R::from_lua_multi(results, lua) R::from_lua_multi(results, lua)
@ -144,7 +145,7 @@ impl<'lua> Function<'lua> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
@ -152,18 +153,18 @@ impl<'lua> Function<'lua> {
return Err(Error::BindError); return Err(Error::BindError);
} }
check_stack_err(lua.state, nargs + 3)?; check_stack_err(lua.state, nargs + 5)?;
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer); ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer);
for arg in args { for arg in args {
lua.push_value(lua.state, arg); lua.push_value(arg);
} }
protect_lua_call(lua.state, nargs + 2, 1, |state| { protect_lua_closure(lua.state, nargs + 2, 1, |state| {
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2); ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
})?; })?;
Ok(Function(lua.pop_ref(lua.state))) Ok(Function(lua.pop_ref()))
}) })
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -2,9 +2,9 @@ use std::ops::{Deref, DerefMut};
use std::iter::FromIterator; use std::iter::FromIterator;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use error::*; use error::Result;
use value::*; use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti};
use lua::*; use lua::Lua;
/// Result is convertible to `MultiValue` following the common Lua idiom of returning the result /// Result is convertible to `MultiValue` following the common Lua idiom of returning the result
/// on success, or in the case of an error, returning `nil` and an error message. /// on success, or in the case of an error, returning `nil` and an error message.
@ -13,10 +13,10 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
let mut result = MultiValue::new(); let mut result = MultiValue::new();
match self { match self {
Ok(v) => result.push_back(v.to_lua(lua)?), Ok(v) => result.push_front(v.to_lua(lua)?),
Err(e) => { Err(e) => {
result.push_back(Nil); result.push_front(e.to_lua(lua)?);
result.push_back(e.to_lua(lua)?); result.push_front(Nil);
} }
} }
@ -27,7 +27,7 @@ impl<'lua, T: ToLua<'lua>, E: ToLua<'lua>> ToLuaMulti<'lua> for StdResult<T, E>
impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T { impl<'lua, T: ToLua<'lua>> ToLuaMulti<'lua> for T {
fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> { fn to_lua_multi(self, lua: &'lua Lua) -> Result<MultiValue<'lua>> {
let mut v = MultiValue::new(); let mut v = MultiValue::new();
v.push_back(self.to_lua(lua)?); v.push_front(self.to_lua(lua)?);
Ok(v) Ok(v)
} }
} }

View File

@ -71,7 +71,7 @@ impl<'lua> String<'lua> {
unsafe { unsafe {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
rlua_assert!( rlua_assert!(
ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING, ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING,
"string ref is not string type" "string ref is not string type"
@ -82,7 +82,6 @@ impl<'lua> String<'lua> {
// string type // string type
let data = ffi::lua_tolstring(lua.state, -1, &mut size); let data = ffi::lua_tolstring(lua.state, -1, &mut size);
ffi::lua_pop(lua.state, 1);
slice::from_raw_parts(data as *const u8, size + 1) slice::from_raw_parts(data as *const u8, size + 1)
}) })
} }

View File

@ -1,9 +1,10 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os::raw::c_int;
use ffi; use ffi;
use error::Result; use error::Result;
use util::*; use util::{check_stack, protect_lua, protect_lua_closure, stack_guard};
use types::{Integer, LuaRef}; use types::{Integer, LuaRef, RefType};
use value::{FromLua, ToLua}; use value::{FromLua, ToLua};
/// Handle to an internal Lua table. /// Handle to an internal Lua table.
@ -51,14 +52,17 @@ impl<'lua> Table<'lua> {
pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> { pub fn set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
lua.push_value(lua.state, value.to_lua(lua)?); lua.push_value(value.to_lua(lua)?);
protect_lua_call(lua.state, 3, 0, |state| {
unsafe extern "C" fn set_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_settable(state, -3); ffi::lua_settable(state, -3);
}) 1
}
protect_lua(lua.state, 3, set_table)
}) })
} }
} }
@ -94,12 +98,18 @@ impl<'lua> Table<'lua> {
pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> { pub fn get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 5); check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
V::from_lua(lua.pop_value(lua.state), lua) unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettable(state, -2);
1
}
protect_lua(lua.state, 2, get_table)?;
V::from_lua(lua.pop_value(), lua)
}) })
} }
} }
@ -108,13 +118,18 @@ impl<'lua> Table<'lua> {
pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> { pub fn contains_key<K: ToLua<'lua>>(&self, key: K) -> Result<bool> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 5); check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
unsafe extern "C" fn get_table(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettable(state, -2);
1
}
protect_lua(lua.state, 2, get_table)?;
let has = ffi::lua_isnil(lua.state, -1) == 0; let has = ffi::lua_isnil(lua.state, -1) == 0;
ffi::lua_pop(lua.state, 1);
Ok(has) Ok(has)
}) })
} }
@ -124,14 +139,18 @@ impl<'lua> Table<'lua> {
pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> { pub fn raw_set<K: ToLua<'lua>, V: ToLua<'lua>>(&self, key: K, value: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
lua.push_value(lua.state, value.to_lua(lua)?); lua.push_value(value.to_lua(lua)?);
protect_lua_call(lua.state, 3, 0, |state| {
unsafe extern "C" fn raw_set(state: *mut ffi::lua_State) -> c_int {
ffi::lua_rawset(state, -3); ffi::lua_rawset(state, -3);
})?; 0
}
protect_lua(lua.state, 3, raw_set)?;
Ok(()) Ok(())
}) })
} }
@ -141,13 +160,12 @@ impl<'lua> Table<'lua> {
pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> { pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(key.to_lua(lua)?);
ffi::lua_rawget(lua.state, -2); ffi::lua_rawget(lua.state, -2);
let res = V::from_lua(lua.pop_value(lua.state), lua)?; let res = V::from_lua(lua.pop_value(), lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res) Ok(res)
}) })
} }
@ -161,10 +179,10 @@ impl<'lua> Table<'lua> {
pub fn len(&self) -> Result<Integer> { pub fn len(&self) -> Result<Integer> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 4); check_stack(lua.state, 4);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
protect_lua_call(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) protect_lua_closure(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
}) })
} }
} }
@ -175,9 +193,8 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
let len = ffi::lua_rawlen(lua.state, -1); let len = ffi::lua_rawlen(lua.state, -1);
ffi::lua_pop(lua.state, 1);
len as Integer len as Integer
}) })
} }
@ -191,13 +208,11 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 { if ffi::lua_getmetatable(lua.state, -1) == 0 {
ffi::lua_pop(lua.state, 1);
None None
} else { } else {
let table = Table(lua.pop_ref(lua.state)); let table = Table(lua.pop_ref());
ffi::lua_pop(lua.state, 1);
Some(table) Some(table)
} }
}) })
@ -213,14 +228,13 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
stack_guard(lua.state, move || { stack_guard(lua.state, move || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
if let Some(metatable) = metatable { if let Some(metatable) = metatable {
lua.push_ref(lua.state, &metatable.0); lua.push_ref(&metatable.0);
} else { } else {
ffi::lua_pushnil(lua.state); ffi::lua_pushnil(lua.state);
} }
ffi::lua_setmetatable(lua.state, -2); ffi::lua_setmetatable(lua.state, -2);
ffi::lua_pop(lua.state, 1);
}) })
} }
} }
@ -265,8 +279,7 @@ impl<'lua> Table<'lua> {
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> { pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
let next_key = Some(LuaRef { let next_key = Some(LuaRef {
lua: self.0.lua, lua: self.0.lua,
registry_id: ffi::LUA_REFNIL, ref_type: RefType::Nil,
drop_unref: true,
}); });
TablePairs { TablePairs {
@ -349,26 +362,18 @@ where
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.table); lua.push_ref(&self.table);
lua.push_ref(lua.state, &next_key); lua.push_ref(&next_key);
match protect_lua_call(lua.state, 2, ffi::LUA_MULTRET, |state| { match protect_lua_closure(lua.state, 2, ffi::LUA_MULTRET, |state| {
if ffi::lua_next(state, -2) == 0 { ffi::lua_next(state, -2) != 0
0
} else {
1
}
}) { }) {
Ok(0) => { Ok(false) => None,
ffi::lua_pop(lua.state, 1); Ok(true) => {
None
}
Ok(_) => {
ffi::lua_pushvalue(lua.state, -2); ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value(lua.state); let key = lua.pop_value();
let value = lua.pop_value(lua.state); let value = lua.pop_value();
self.next_key = Some(lua.pop_ref(lua.state)); self.next_key = Some(lua.pop_ref());
ffi::lua_pop(lua.state, 1);
Some((|| { Some((|| {
let key = K::from_lua(key, lua)?; let key = K::from_lua(key, lua)?;
@ -411,15 +416,13 @@ where
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 5); check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.table); lua.push_ref(&self.table);
match protect_lua_call(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index)) match protect_lua_closure(lua.state, 1, 1, |state| {
{ ffi::lua_geti(state, -1, index)
Ok(ffi::LUA_TNIL) => { }) {
ffi::lua_pop(lua.state, 1); Ok(ffi::LUA_TNIL) => None,
None
}
Ok(_) => { Ok(_) => {
let value = lua.pop_value(lua.state); let value = lua.pop_value();
self.index = Some(index + 1); self.index = Some(index + 1);
Some(V::from_lua(value, lua)) Some(V::from_lua(value, lua))
} }

View File

@ -744,3 +744,38 @@ fn too_many_binds() {
.is_err() .is_err()
); );
} }
#[test]
fn large_args() {
let lua = Lua::new();
let globals = lua.globals();
globals
.set(
"c",
lua.create_function(|_, args: Variadic<usize>| {
let mut s = 0;
for i in 0..args.len() {
s += i;
assert_eq!(i, args[i]);
}
Ok(s)
}).unwrap(),
)
.unwrap();
let f: Function = lua.eval(
r#"
return function(...)
return c(...)
end
"#,
None,
).unwrap();
assert_eq!(
f.call::<_, usize>((0..100).collect::<Variadic<usize>>())
.unwrap(),
4950
);
}

View File

@ -1,8 +1,8 @@
use std::os::raw::c_int; use std::os::raw::c_int;
use ffi; use ffi;
use error::*; use error::{Error, Result};
use util::*; use util::{check_stack, check_stack_err, error_traceback, pop_error, stack_guard};
use types::LuaRef; use types::LuaRef;
use value::{FromLuaMulti, MultiValue, ToLuaMulti}; use value::{FromLuaMulti, MultiValue, ToLuaMulti};
@ -78,10 +78,10 @@ impl<'lua> Thread<'lua> {
{ {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1); let thread_state = ffi::lua_tothread(lua.state, -1);
let status = ffi::lua_status(thread_state); let status = ffi::lua_status(thread_state);
@ -93,11 +93,13 @@ impl<'lua> Thread<'lua> {
let args = args.to_lua_multi(lua)?; let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int; let nargs = args.len() as c_int;
check_stack_err(lua.state, nargs)?;
check_stack_err(thread_state, nargs + 1)?; check_stack_err(thread_state, nargs + 1)?;
for arg in args { for arg in args {
lua.push_value(thread_state, arg); lua.push_value(arg);
} }
ffi::lua_xmove(lua.state, thread_state, nargs);
let ret = ffi::lua_resume(thread_state, lua.state, nargs); let ret = ffi::lua_resume(thread_state, lua.state, nargs);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
@ -107,9 +109,11 @@ impl<'lua> Thread<'lua> {
let nresults = ffi::lua_gettop(thread_state); let nresults = ffi::lua_gettop(thread_state);
let mut results = MultiValue::new(); let mut results = MultiValue::new();
check_stack(thread_state, 2); ffi::lua_xmove(thread_state, lua.state, nresults);
check_stack(lua.state, 2);
for _ in 0..nresults { for _ in 0..nresults {
results.push_front(lua.pop_value(thread_state)); results.push_front(lua.pop_value());
} }
R::from_lua_multi(results, lua) R::from_lua_multi(results, lua)
}) })
@ -123,7 +127,7 @@ impl<'lua> Thread<'lua> {
stack_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 1); check_stack(lua.state, 1);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1); let thread_state = ffi::lua_tothread(lua.state, -1);
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);

View File

@ -1,4 +1,4 @@
use std::fmt; use std::{fmt, mem, ptr};
use std::os::raw::{c_int, c_void}; use std::os::raw::{c_int, c_void};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -16,6 +16,9 @@ pub type Number = ffi::lua_Number;
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LightUserData(pub *mut c_void); pub struct LightUserData(pub *mut c_void);
pub(crate) type Callback<'lua, 'a> =
Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>;
/// An auto generated key into the Lua registry. /// An auto generated key into the Lua registry.
/// ///
/// This is a handle into a value stored inside the Lua registry, similar to the normal handle types /// This is a handle into a value stored inside the Lua registry, similar to the normal handle types
@ -32,57 +35,54 @@ pub struct LightUserData(pub *mut c_void);
pub struct RegistryKey { pub struct RegistryKey {
pub(crate) registry_id: c_int, pub(crate) registry_id: c_int,
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>, pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
pub(crate) drop_unref: bool,
} }
impl Drop for RegistryKey { impl Drop for RegistryKey {
fn drop(&mut self) { fn drop(&mut self) {
if self.drop_unref { if let Some(list) = self.unref_list.lock().unwrap().as_mut() {
if let Some(list) = self.unref_list.lock().unwrap().as_mut() { list.push(self.registry_id);
list.push(self.registry_id);
}
} }
} }
} }
pub(crate) type Callback<'lua, 'a> = impl RegistryKey {
Box<Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>> + 'a>; // Destroys the RegistryKey without adding to the drop list
pub(crate) fn take(self) -> c_int {
let registry_id = self.registry_id;
unsafe {
ptr::read(&self.unref_list);
mem::forget(self);
}
registry_id
}
}
#[derive(Debug)]
pub(crate) enum RefType {
Nil,
Stack { stack_slot: c_int },
Registry { registry_id: c_int },
}
pub(crate) struct LuaRef<'lua> { pub(crate) struct LuaRef<'lua> {
pub lua: &'lua Lua, pub(crate) lua: &'lua Lua,
pub registry_id: c_int, pub(crate) ref_type: RefType,
pub drop_unref: bool,
} }
impl<'lua> fmt::Debug for LuaRef<'lua> { impl<'lua> fmt::Debug for LuaRef<'lua> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "LuaRef({})", self.registry_id) write!(f, "{:?}", self.ref_type)
} }
} }
impl<'lua> Clone for LuaRef<'lua> { impl<'lua> Clone for LuaRef<'lua> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
if self.drop_unref { self.lua.clone_ref(self)
unsafe {
self.lua.push_ref(self.lua.state, self);
self.lua.pop_ref(self.lua.state)
}
} else {
LuaRef {
lua: self.lua,
registry_id: self.registry_id,
drop_unref: self.drop_unref,
}
}
} }
} }
impl<'lua> Drop for LuaRef<'lua> { impl<'lua> Drop for LuaRef<'lua> {
fn drop(&mut self) { fn drop(&mut self) {
if self.drop_unref { self.lua.drop_ref(self)
unsafe {
ffi::luaL_unref(self.lua.state, ffi::LUA_REGISTRYINDEX, self.registry_id);
}
}
} }
} }

View File

@ -4,8 +4,8 @@ use std::collections::HashMap;
use std::string::String as StdString; use std::string::String as StdString;
use ffi; use ffi;
use error::*; use error::{Error, Result};
use util::*; use util::{check_stack, get_userdata, stack_guard};
use types::{Callback, LuaRef}; use types::{Callback, LuaRef};
use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
use lua::Lua; use lua::Lua;
@ -415,10 +415,10 @@ impl<'lua> AnyUserData<'lua> {
{ {
unsafe { unsafe {
let lua = self.0.lua; let lua = self.0.lua;
stack_err_guard(lua.state, move || { stack_guard(lua.state, move || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
rlua_assert!( rlua_assert!(
ffi::lua_getmetatable(lua.state, -1) != 0, ffi::lua_getmetatable(lua.state, -1) != 0,
@ -432,11 +432,9 @@ impl<'lua> AnyUserData<'lua> {
); );
if ffi::lua_rawequal(lua.state, -1, -2) == 0 { if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
ffi::lua_pop(lua.state, 3);
Err(Error::UserDataTypeMismatch) Err(Error::UserDataTypeMismatch)
} else { } else {
let res = func(&*get_userdata::<RefCell<T>>(lua.state, -3)); let res = func(&*get_userdata::<RefCell<T>>(lua.state, -3));
ffi::lua_pop(lua.state, 3);
res res
} }
}) })
@ -451,12 +449,11 @@ impl<'lua> AnyUserData<'lua> {
pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> { pub fn set_user_value<V: ToLua<'lua>>(&self, v: V) -> Result<()> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 2); check_stack(lua.state, 2);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
lua.push_value(lua.state, v.to_lua(lua)?); lua.push_value(v.to_lua(lua)?);
ffi::lua_setuservalue(lua.state, -2); ffi::lua_setuservalue(lua.state, -2);
ffi::lua_pop(lua.state, 1);
Ok(()) Ok(())
}) })
} }
@ -468,12 +465,11 @@ impl<'lua> AnyUserData<'lua> {
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> { pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, || { stack_guard(lua.state, || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(&self.0);
ffi::lua_getuservalue(lua.state, -1); ffi::lua_getuservalue(lua.state, -1);
let res = V::from_lua(lua.pop_value(lua.state), lua)?; let res = V::from_lua(lua.pop_value(), lua)?;
ffi::lua_pop(lua.state, 1);
Ok(res) Ok(res)
}) })
} }

View File

@ -27,74 +27,71 @@ pub unsafe fn check_stack_err(state: *mut ffi::lua_State, amount: c_int) -> Resu
} }
} }
// Run an operation on a lua_State and ensure that there are no stack leaks and the stack is pub struct StackGuard {
// restored on panic. state: *mut ffi::lua_State,
top: c_int,
}
impl StackGuard {
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
// the beginning, this is considered a fatal logic error and will result in an abort.
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
}
}
}
impl Drop for StackGuard {
fn drop(&mut self) {
unsafe {
let top = ffi::lua_gettop(self.state);
if top > self.top {
ffi::lua_settop(self.state, self.top);
} else if top < self.top {
rlua_panic!("{} too many stack values popped", self.top - top);
}
}
}
}
// Run an operation on a lua_State and restores the stack state at the end, using `StackGuard`.
pub unsafe fn stack_guard<F, R>(state: *mut ffi::lua_State, op: F) -> R pub unsafe fn stack_guard<F, R>(state: *mut ffi::lua_State, op: F) -> R
where where
F: FnOnce() -> R, F: FnOnce() -> R,
{ {
let begin = ffi::lua_gettop(state); let _stack_guard = StackGuard::new(state);
op()
let res = match catch_unwind(AssertUnwindSafe(op)) {
Ok(r) => r,
Err(p) => {
let top = ffi::lua_gettop(state);
if top > begin {
ffi::lua_settop(state, begin);
}
resume_unwind(p);
}
};
let top = ffi::lua_gettop(state);
if top > begin {
ffi::lua_settop(state, begin);
rlua_panic!("expected stack to be {}, got {}", begin, top);
} else if top < begin {
rlua_abort!("{} too many stack values popped", begin - top);
}
res
} }
// Run an operation on a lua_State and automatically clean up the stack on error. Takes the // Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// lua_State and an operation to run. If the operation results in success, then the stack is // Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// inspected to make sure there is not a stack leak, and otherwise this is a logic error and will // limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// panic. If the operation results in an error, or if the operation panics, the stack is shrunk to // always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack.
// the value before the call. // Provided function must *never* panic.
pub unsafe fn stack_err_guard<F, R>(state: *mut ffi::lua_State, op: F) -> Result<R> pub unsafe fn protect_lua(
where state: *mut ffi::lua_State,
F: FnOnce() -> Result<R>, nargs: c_int,
{ f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
let begin = ffi::lua_gettop(state); ) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
let res = match catch_unwind(AssertUnwindSafe(op)) { ffi::lua_pushcfunction(state, error_traceback);
Ok(r) => r, ffi::lua_pushcfunction(state, f);
Err(p) => { if nargs > 0 {
let top = ffi::lua_gettop(state); ffi::lua_rotate(state, stack_start + 1, 2);
if top > begin { }
ffi::lua_settop(state, begin);
} let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
resume_unwind(p); ffi::lua_remove(state, stack_start + 1);
}
}; if ret == ffi::LUA_OK {
Ok(())
let top = ffi::lua_gettop(state); } else {
if res.is_ok() { Err(pop_error(state, ret))
if top > begin {
ffi::lua_settop(state, begin);
rlua_panic!("expected stack to be {}, got {}", begin, top);
} else if top < begin {
rlua_abort!("{} too many stack values popped", begin - top);
}
} else {
if top > begin {
ffi::lua_settop(state, begin);
} else if top < begin {
rlua_abort!("{} too many stack values popped", begin - top);
}
} }
res
} }
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way. // Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
@ -104,7 +101,7 @@ where
// values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does // values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does
// not call checkstack. Provided function must *not* panic, and since it will generally be // not call checkstack. Provided function must *not* panic, and since it will generally be
// lonjmping, should not contain any values that implement Drop. // lonjmping, should not contain any values that implement Drop.
pub unsafe fn protect_lua_call<F, R>( pub unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
nargs: c_int, nargs: c_int,
nresults: c_int, nresults: c_int,
@ -115,7 +112,7 @@ where
R: Copy, R: Copy,
{ {
struct Params<F, R> { struct Params<F, R> {
function: *const F, function: F,
result: R, result: R,
nresults: c_int, nresults: c_int,
} }
@ -127,7 +124,7 @@ where
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>; let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
(*params).result = (*(*params).function)(state); (*params).result = ((*params).function)(state);
if (*params).nresults == ffi::LUA_MULTRET { if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state) ffi::lua_gettop(state)
@ -140,10 +137,12 @@ where
ffi::lua_pushcfunction(state, error_traceback); ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, do_call::<F, R>); ffi::lua_pushcfunction(state, do_call::<F, R>);
ffi::lua_rotate(state, stack_start + 1, 2); if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let mut params = Params { let mut params = Params {
function: &f, function: f,
result: mem::uninitialized(), result: mem::uninitialized(),
nresults, nresults,
}; };
@ -173,8 +172,9 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
"pop_error called with non-error return code" "pop_error called with non-error return code"
); );
if let Some(err) = pop_wrapped_error(state) { if let Some(err) = get_wrapped_error(state, -1).as_ref() {
err ffi::lua_pop(state, 1);
err.clone()
} else if is_wrapped_panic(state, -1) { } else if is_wrapped_panic(state, -1) {
let panic = get_userdata::<WrappedPanic>(state, -1); let panic = get_userdata::<WrappedPanic>(state, -1);
if let Some(p) = (*panic).0.take() { if let Some(p) = (*panic).0.take() {
@ -212,7 +212,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
ffi::LUA_ERRMEM => { ffi::LUA_ERRMEM => {
// This should be impossible, as we set the lua allocator to one that aborts // This should be impossible, as we set the lua allocator to one that aborts
// instead of failing. // instead of failing.
rlua_abort!("impossible Lua allocation error, aborting!") rlua_abort!("impossible Lua allocation error")
} }
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string), ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
_ => rlua_panic!("unrecognized lua error code"), _ => rlua_panic!("unrecognized lua error code"),
@ -222,14 +222,14 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
// Internally uses 4 stack spaces, does not call checkstack // Internally uses 4 stack spaces, does not call checkstack
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> { pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> {
protect_lua_call(state, 0, 1, |state| { protect_lua_closure(state, 0, 1, |state| {
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());
}) })
} }
// Internally uses 4 stack spaces, does not call checkstack // Internally uses 4 stack spaces, does not call checkstack
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> { pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua_call(state, 0, 1, move |state| { let ud = protect_lua_closure(state, 0, 1, move |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?; })?;
ptr::write(ud, t); ptr::write(ud, t);
@ -300,7 +300,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
if ffi::lua_checkstack(state, 2) == 0 { if ffi::lua_checkstack(state, 2) == 0 {
// If we don't have enough stack space to even check the error type, do nothing // If we don't have enough stack space to even check the error type, do nothing
} else if is_wrapped_error(state, 1) { } else if let Some(error) = get_wrapped_error(state, 1).as_ref() {
let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 {
gc_guard(state, || { gc_guard(state, || {
ffi::luaL_traceback(state, state, ptr::null(), 0); ffi::luaL_traceback(state, state, ptr::null(), 0);
@ -314,7 +314,9 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
"not enough stack space for traceback".to_owned() "not enough stack space for traceback".to_owned()
}; };
let error = pop_wrapped_error(state).unwrap(); let error = error.clone();
ffi::lua_pop(state, 1);
push_wrapped_error( push_wrapped_error(
state, state,
Error::CallbackError { Error::CallbackError {
@ -404,14 +406,6 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
} }
} }
// Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
let main_state = ffi::lua_tothread(state, -1);
ffi::lua_pop(state, 1);
main_state
}
// Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call // Pushes a WrappedError::Error to the top of the stack. Uses two stack spaces and does not call
// lua_checkstack. // lua_checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) { pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
@ -424,18 +418,26 @@ pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
} }
// Pops a WrappedError off of the top of the stack, if it is a WrappedError. If it is not a // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
// WrappedError, returns None and does not pop anything. Uses 2 stack spaces and does not call // otherwise returns null. Uses 2 stack spaces and does not call lua_checkstack.
// lua_checkstack. pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<Error> { let userdata = ffi::lua_touserdata(state, index);
if !is_wrapped_error(state, -1) { if userdata.is_null() {
None return ptr::null();
}
if ffi::lua_getmetatable(state, index) == 0 {
return ptr::null();
}
get_error_metatable(state);
let res = ffi::lua_rawequal(state, -1, -2) != 0;
ffi::lua_pop(state, 2);
if res {
&(*get_userdata::<WrappedError>(state, -1)).0
} else { } else {
let err = &*get_userdata::<WrappedError>(state, -1); ptr::null()
// We are assuming here that Error::clone() cannot panic.
let err = err.0.clone();
ffi::lua_pop(state, 1);
Some(err)
} }
} }
@ -466,9 +468,8 @@ pub unsafe fn init_error_metatables(state: *mut ffi::lua_State) {
ffi::luaL_checkstack(state, 2, ptr::null()); ffi::luaL_checkstack(state, 2, ptr::null());
callback_error(state, || { callback_error(state, || {
if is_wrapped_error(state, -1) { if let Some(error) = get_wrapped_error(state, -1).as_ref() {
let error = get_userdata::<WrappedError>(state, -1); let error_str = error.to_string();
let error_str = (*error).0.to_string();
gc_guard(state, || { gc_guard(state, || {
ffi::lua_pushlstring( ffi::lua_pushlstring(
state, state,
@ -587,24 +588,6 @@ unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>)
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
} }
// Checks if the value at the given index is a WrappedError, uses 2 stack spaces and does not call
// lua_checkstack.
unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool {
let userdata = ffi::lua_touserdata(state, index);
if userdata.is_null() {
return false;
}
if ffi::lua_getmetatable(state, index) == 0 {
return false;
}
get_error_metatable(state);
let res = ffi::lua_rawequal(state, -1, -2) != 0;
ffi::lua_pop(state, 2);
res
}
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call // Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
// lua_checkstack. // lua_checkstack.
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool { unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {

View File

@ -1,9 +1,7 @@
use std::str; use std::{slice, str, vec};
use std::ops::{Deref, DerefMut}; use std::iter::{self, FromIterator};
use std::iter::FromIterator;
use std::collections::VecDeque;
use error::*; use error::{Error, Result};
use types::{Integer, LightUserData, Number}; use types::{Integer, LightUserData, Number};
use string::String; use string::String;
use table::Table; use table::Table;
@ -76,41 +74,77 @@ pub trait FromLua<'lua>: Sized {
/// Multiple Lua values used for both argument passing and also for multiple return values. /// Multiple Lua values used for both argument passing and also for multiple return values.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MultiValue<'lua>(VecDeque<Value<'lua>>); pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> { impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values. /// Creates an empty `MultiValue` containing no values.
pub fn new() -> MultiValue<'lua> { pub fn new() -> MultiValue<'lua> {
MultiValue(VecDeque::new()) MultiValue(Vec::new())
} }
} }
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> { impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self { fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue(VecDeque::from_iter(iter)) MultiValue::from_vec(Vec::from_iter(iter))
} }
} }
impl<'lua> IntoIterator for MultiValue<'lua> { impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>; type Item = Value<'lua>;
type IntoIter = <VecDeque<Value<'lua>> as IntoIterator>::IntoIter; type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.0.into_iter() self.0.into_iter().rev()
} }
} }
impl<'lua> Deref for MultiValue<'lua> { impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type Target = VecDeque<Value<'lua>>; type Item = &'a Value<'lua>;
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
fn deref(&self) -> &Self::Target { fn into_iter(self) -> Self::IntoIter {
&self.0 (&self.0).into_iter().rev()
} }
} }
impl<'lua> DerefMut for MultiValue<'lua> { impl<'lua> MultiValue<'lua> {
fn deref_mut(&mut self) -> &mut Self::Target { pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
&mut self.0 v.reverse();
MultiValue(v)
}
pub fn into_vec(self) -> Vec<Value<'lua>> {
let mut v = self.0;
v.reverse();
v
}
pub fn from_vec_rev(v: Vec<Value<'lua>>) -> MultiValue<'lua> {
MultiValue(v)
}
pub fn into_vec_rev(self) -> Vec<Value<'lua>> {
self.0
}
pub fn reserve(&mut self, size: usize) {
self.0.reserve(size);
}
pub fn push_front(&mut self, value: Value<'lua>) {
self.0.push(value);
}
pub fn pop_front(&mut self) -> Option<Value<'lua>> {
self.0.pop()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev()
} }
} }