use std::any::{Any, TypeId};
use std::convert::TryFrom;
use std::mem;
use std::num::NonZeroI32;
use std::ops::{Deref, DerefMut};
use std::ptr;
use crate::{
c_ptr, ffi,
object::{FromObject, Object},
AsLua, InsideCallback, LuaRead, LuaState, LuaTable, Push, PushGuard, ReadResult, WrongType,
};
pub unsafe fn push_some_userdata<T>(lua: *mut ffi::lua_State, value: T)
where
T: 'static,
{
type UDBox<T> = Option<T>;
let ud_ptr = ffi::lua_newuserdata(lua, std::mem::size_of::<UDBox<T>>());
std::ptr::write(ud_ptr as *mut UDBox<T>, Some(value));
if std::mem::needs_drop::<T>() {
ffi::lua_newtable(lua);
ffi::lua_pushstring(lua, c_ptr!("__gc"));
ffi::lua_pushcfunction(lua, wrap_gc::<T>);
ffi::lua_settable(lua, -3);
ffi::lua_setmetatable(lua, -2);
}
unsafe extern "C" fn wrap_gc<T>(lua: *mut ffi::lua_State) -> i32 {
let ud_ptr = ffi::lua_touserdata(lua, 1);
let ud = ud_ptr
.cast::<UDBox<T>>()
.as_mut()
.expect("__gc called with userdata pointing to NULL");
drop(ud.take());
0
}
}
#[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.cast::<u8>().add(mem::size_of::<TypeId>()).cast::<T>());
0
}
}
#[inline]
pub fn push_userdata<L, T, F>(data: T, lua: L, metatable: F) -> PushGuard<L>
where
F: for<'a> FnOnce(LuaTable<&'a PushGuard<L>>),
L: AsLua,
T: '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_lua(), 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.cast::<u8>().add(mem::size_of_val(&typeid));
ptr::write(data_loc as *mut _, data);
ffi::lua_newtable(lua.as_lua());
{
match "__gc".push_to_lua(lua.as_lua()) {
Ok(p) => p.forget(),
Err(_) => unreachable!(),
};
ffi::lua_pushcfunction(lua.as_lua(), destructor_wrapper::<T>);
ffi::lua_settable(lua.as_lua(), -3);
}
let lua_state = lua.as_lua();
let guard = PushGuard::new(lua, 1);
metatable(LuaTable::lua_read(&guard).ok().unwrap());
ffi::lua_setmetatable(lua_state, -2);
guard
}
}
#[inline]
pub fn read_userdata<'t, T>(lua: &InsideCallback, index: i32) -> Result<&'t mut T, &InsideCallback>
where
T: 'static + Any,
{
unsafe {
let data_ptr = ffi::lua_touserdata(lua.as_lua(), 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
.cast::<u8>()
.add(mem::size_of::<TypeId>())
.cast::<T>();
Ok(&mut *data)
}
}
#[derive(Debug)]
pub struct UserdataOnStack<'a, T, L: 'a> {
inner: Object<L>,
data: &'a mut T,
}
impl<T, L> FromObject<L> for UserdataOnStack<'_, T, L>
where
L: AsLua,
T: Any,
{
unsafe fn check(lua: impl AsLua, index: NonZeroI32) -> bool {
ffi::lua_touserdata(lua.as_lua(), index.into())
.cast::<TypeId>()
.as_ref()
.map(|&ti| ti == TypeId::of::<T>())
.unwrap_or(false)
}
unsafe fn from_obj(inner: Object<L>) -> Self {
let data = ffi::lua_touserdata(inner.as_lua(), inner.index().into())
.cast::<u8>()
.add(mem::size_of::<TypeId>())
.cast();
Self {
inner,
data: &mut *data,
}
}
}
impl<T, L> TryFrom<Object<L>> for UserdataOnStack<'_, T, L>
where
L: AsLua,
T: Any,
{
type Error = Object<L>;
#[inline(always)]
fn try_from(o: Object<L>) -> Result<Self, Self::Error> {
Self::try_from_obj(o)
}
}
impl<'a, T, L> From<UserdataOnStack<'a, T, L>> for Object<L> {
fn from(ud: UserdataOnStack<'a, T, L>) -> Self {
ud.inner
}
}
impl<T, L> LuaRead<L> for UserdataOnStack<'_, T, L>
where
L: AsLua,
T: Any,
{
#[inline]
fn lua_read_at_position(lua: L, index: NonZeroI32) -> ReadResult<Self, L> {
Self::try_from_obj(Object::new(lua, index)).map_err(|l| {
let g = Object::into_guard(l);
let e = WrongType::info("reading userdata")
.expected_type::<T>()
.actual_single_lua(&g, index);
(g, e)
})
}
}
impl<T, L> AsLua for UserdataOnStack<'_, T, L>
where
L: AsLua,
{
#[inline]
fn as_lua(&self) -> LuaState {
self.inner.as_lua()
}
}
impl<T, L> Deref for UserdataOnStack<'_, T, L> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.data
}
}
impl<T, L> DerefMut for UserdataOnStack<'_, T, L> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.data
}
}