use std::any::{Any, TypeId};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::mem;
use std::ptr;
use ffi;
use libc;
use AsLua;
use AsMutLua;
use Push;
use PushGuard;
use LuaContext;
use LuaRead;
use InsideCallback;
use LuaTable;
#[inline]
extern "C" fn destructor_wrapper<T>(lua: *mut ffi::lua_State) -> libc::c_int {
unsafe {
let obj = ffi::lua_touserdata(lua, -1);
ptr::drop_in_place(obj as *mut TypeId);
ptr::drop_in_place((obj as *mut u8).offset(mem::size_of::<TypeId>() as isize) as *mut T);
0
}
}
#[inline]
pub fn push_userdata<'lua, L, T, F>(data: T, mut lua: L, metatable: F) -> PushGuard<L>
where F: FnOnce(LuaTable<&mut PushGuard<&mut L>>),
L: AsMutLua<'lua>,
T: Send + 'static + Any
{
unsafe {
let typeid = TypeId::of::<T>();
let lua_data = {
let tot_size = mem::size_of_val(&typeid) + mem::size_of_val(&data);
ffi::lua_newuserdata(lua.as_mut_lua().0, tot_size as libc::size_t)
};
debug_assert_eq!(lua_data as usize % mem::align_of_val(&data), 0);
debug_assert_eq!(mem::size_of_val(&typeid) % mem::align_of_val(&data), 0);
ptr::write(lua_data as *mut TypeId, typeid);
let data_loc = (lua_data as *const u8).offset(mem::size_of_val(&typeid) as isize);
ptr::write(data_loc as *mut _, data);
let lua_raw = lua.as_mut_lua();
ffi::lua_newtable(lua.as_mut_lua().0);
{
match "__gc".push_to_lua(&mut lua) {
Ok(p) => p.forget(),
Err(_) => unreachable!(),
};
ffi::lua_pushcfunction(lua.as_mut_lua().0, destructor_wrapper::<T>);
ffi::lua_settable(lua.as_mut_lua().0, -3);
}
{
let raw_lua = lua.as_lua();
let mut guard = PushGuard {
lua: &mut lua,
size: 1,
raw_lua: raw_lua,
};
metatable(LuaRead::lua_read(&mut guard).ok().unwrap());
guard.forget();
}
ffi::lua_setmetatable(lua_raw.0, -2);
}
let raw_lua = lua.as_lua();
PushGuard {
lua: lua,
size: 1,
raw_lua: raw_lua,
}
}
#[inline]
pub fn read_userdata<'t, 'c, T>(lua: &'c mut InsideCallback,
index: i32)
-> Result<&'t mut T, &'c mut InsideCallback>
where T: 'static + Any
{
unsafe {
let data_ptr = ffi::lua_touserdata(lua.as_lua().0, index);
if data_ptr.is_null() {
return Err(lua);
}
let actual_typeid = data_ptr as *const TypeId;
if *actual_typeid != TypeId::of::<T>() {
return Err(lua);
}
let data = (data_ptr as *const u8).offset(mem::size_of::<TypeId>() as isize);
Ok(&mut *(data as *mut T))
}
}
#[derive(Debug)]
pub struct UserdataOnStack<T, L> {
variable: L,
index: i32,
marker: PhantomData<T>,
}
impl<'lua, T, L> LuaRead<L> for UserdataOnStack<T, L>
where L: AsMutLua<'lua>,
T: 'lua + Any
{
#[inline]
fn lua_read_at_position(lua: L, index: i32) -> Result<UserdataOnStack<T, L>, L> {
unsafe {
let data_ptr = ffi::lua_touserdata(lua.as_lua().0, index);
if data_ptr.is_null() {
return Err(lua);
}
let actual_typeid = data_ptr as *const TypeId;
if *actual_typeid != TypeId::of::<T>() {
return Err(lua);
}
Ok(UserdataOnStack {
variable: lua,
index: index,
marker: PhantomData,
})
}
}
}
unsafe impl<'lua, T, L> AsLua<'lua> for UserdataOnStack<T, L>
where L: AsLua<'lua>,
T: 'lua + Any
{
#[inline]
fn as_lua(&self) -> LuaContext {
self.variable.as_lua()
}
}
unsafe impl<'lua, T, L> AsMutLua<'lua> for UserdataOnStack<T, L>
where L: AsMutLua<'lua>,
T: 'lua + Any
{
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
self.variable.as_mut_lua()
}
}
impl<'lua, T, L> Deref for UserdataOnStack<T, L>
where L: AsLua<'lua>,
T: 'lua + Any
{
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe {
let base = ffi::lua_touserdata(self.variable.as_lua().0, self.index);
let data = (base as *const u8).offset(mem::size_of::<TypeId>() as isize);
&*(data as *const T)
}
}
}
impl<'lua, T, L> DerefMut for UserdataOnStack<T, L>
where L: AsMutLua<'lua>,
T: 'lua + Any
{
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe {
let base = ffi::lua_touserdata(self.variable.as_mut_lua().0, self.index);
let data = (base as *const u8).offset(mem::size_of::<TypeId>() as isize);
&mut *(data as *mut T)
}
}
}