tlua/
cdata.rs

1use crate::ffi;
2use crate::lua_functions::LuaFunction;
3use crate::object::{FromObject, Object};
4use crate::{AsLua, LuaRead, LuaState, Push, PushInto, PushOneInto, ReadResult, WrongType};
5use std::cell::UnsafeCell;
6use std::convert::TryFrom;
7use std::num::NonZeroI32;
8use std::os::raw::{c_char, c_void};
9
10////////////////////////////////////////////////////////////////////////////////
11// CDataOnStack
12////////////////////////////////////////////////////////////////////////////////
13
14/// Represents a reference to the underlying cdata value corresponding to a
15/// given cdata object.
16#[derive(Debug, Clone, Copy)]
17enum CDataRef<'l> {
18    Ptr(*mut c_void),
19    Slice(&'l [u8]),
20}
21
22/// A cdata value stored on lua stack. Can be used to check type of cdata,
23/// access the raw bytes of the data, downcast it to a rust type or passed as
24/// an argument into a lua function.
25/// # Examples:
26/// ```no_run
27/// use tlua::{CDataOnStack, Lua, ffi};
28/// let lua = Lua::new();
29/// let cdata: CDataOnStack<_> = lua.eval("
30///     ffi = require 'ffi';
31///     return ffi.new('uint8_t', 69)
32/// ").unwrap();
33///
34/// // check CTypeID
35/// assert_eq!(cdata.ctypeid(), ffi::CTID_UINT8);
36///
37/// // check raw bytes
38/// assert_eq!(cdata.data(), [69]);
39///
40/// // pass to a lua function
41/// let n: u8 = lua.eval_with("return ... + 1", &cdata).unwrap();
42/// assert_eq!(n, 70);
43/// ```
44#[derive(Debug)]
45pub struct CDataOnStack<'l, L> {
46    inner: Object<L>,
47    data: UnsafeCell<CDataRef<'l>>,
48    ctypeid: ffi::CTypeID,
49}
50
51impl<L> CDataOnStack<'_, L>
52where
53    L: AsLua,
54{
55    /// Return pointer to data. Maybe use [`CDataOnStack::data`] instead.
56    #[inline(always)]
57    pub fn as_ptr(&self) -> *const c_void {
58        match unsafe { *self.data.get() } {
59            CDataRef::Ptr(ptr) => ptr,
60            CDataRef::Slice(slice) => slice.as_ptr().cast(),
61        }
62    }
63
64    /// Updates the CDataRef inplace replacing the pointer with a slice of known
65    /// size. This function is only executed once because it performs an
66    /// expensnive call to luajit runtime to figure out the size of the data.
67    fn update_data(&self, ptr: *const c_void) -> &[u8] {
68        let f = LuaFunction::load(self, "return require('ffi').sizeof(...)").unwrap();
69        let size: usize = f.into_call_with_args(self).unwrap();
70        unsafe {
71            let slice = std::slice::from_raw_parts(ptr.cast(), size);
72            std::ptr::write(self.data.get(), CDataRef::Slice(slice));
73            slice
74        }
75    }
76
77    /// Return a slice of bytes covering the data if the data's size was already
78    /// retrieved before. Otherwise return `None`.
79    ///
80    /// See also [`CDataOnStack::data`].
81    pub fn try_as_bytes(&self) -> Option<&[u8]> {
82        match unsafe { *self.data.get() } {
83            CDataRef::Slice(slice) => Some(slice),
84            CDataRef::Ptr(_) => None,
85        }
86    }
87
88    /// Return a mutable slice of bytes covering the data if the data's size was
89    /// already retrieved before. Otherwise return `None`.
90    ///
91    /// See also [`CDataOnStack::data_mut`].
92    pub fn try_as_bytes_mut(&mut self) -> Option<&mut [u8]> {
93        match unsafe { *self.data.get() } {
94            CDataRef::Slice(slice) => unsafe {
95                Some(std::slice::from_raw_parts_mut(
96                    slice.as_ptr() as *mut _,
97                    slice.len(),
98                ))
99            },
100            CDataRef::Ptr(_) => None,
101        }
102    }
103
104    /// Return a slice of bytes covering the data. Calling this function the
105    /// first time around will perform an expensive operation of retrieving the
106    /// data's size. So if for some reason you just need the pointer to the
107    /// data, use the [`CDataOnStack::as_ptr`]. But if you actually need the
108    /// bytes, use this function.
109    #[inline(always)]
110    pub fn data(&self) -> &[u8] {
111        match unsafe { *self.data.get() } {
112            CDataRef::Ptr(ptr) => self.update_data(ptr),
113            CDataRef::Slice(slice) => slice,
114        }
115    }
116
117    /// Return a mutable slice of bytes covering the data. Calling this function the
118    /// first time around will perform an expensive operation of retrieving the
119    /// data's size.
120    #[inline(always)]
121    pub fn data_mut(&mut self) -> &mut [u8] {
122        let data = self.data();
123        unsafe { std::slice::from_raw_parts_mut(data.as_ptr() as *mut _, data.len()) }
124    }
125
126    /// Return the ctypeid of the cdata.
127    #[inline(always)]
128    pub fn ctypeid(&self) -> ffi::CTypeID {
129        self.ctypeid
130    }
131
132    /// Return a reference to the underlying value if
133    /// `self.`[`ctypeid`]`() ==
134    /// <T as `[`AsCData`]`>::`[`ctypeid`](AsCData::ctypeid)`()`,
135    /// otherwise return `None`.
136    ///
137    /// **This function may panic, if
138    /// `self.`[`data`]`().len() != std::mem::size_of::<T>()`**
139    ///
140    /// [`data`]: CDataOnStack::data
141    /// [`ctypeid`]: CDataOnStack::ctypeid
142    #[inline(always)]
143    pub fn try_downcast<T>(&self) -> Option<&T>
144    where
145        T: AsCData,
146    {
147        self.check_ctypeid::<T>()
148            .then(|| unsafe { &*self.as_ptr().cast::<T>() })
149    }
150
151    /// Return a mutable reference to the underlying value if
152    /// `self.`[`ctypeid`]`() ==
153    /// <T as `[`AsCData`]`>::`[`ctypeid`](AsCData::ctypeid)`()`,
154    /// otherwise return `None`.
155    ///
156    /// **This function may panic, if
157    /// `self.`[`data`]`().len() != std::mem::size_of::<T>()`**
158    ///
159    /// [`data`]: CDataOnStack::data
160    /// [`ctypeid`]: CDataOnStack::ctypeid
161    #[inline(always)]
162    pub fn try_downcast_mut<T>(&self) -> Option<&mut T>
163    where
164        T: AsCData,
165    {
166        self.check_ctypeid::<T>()
167            .then(|| unsafe { &mut *(self.as_ptr().cast::<T>() as *mut _) })
168    }
169
170    /// Return the underlying value consuming `self` if
171    /// `self.`[`ctypeid`]`() ==
172    /// <T as `[`AsCData`]`>::`[`ctypeid`](AsCData::ctypeid)`()`,
173    /// otherwise return `Err(self)`.
174    ///
175    /// **This function may panic, if
176    /// `self.`[`data`]`().len() != std::mem::size_of::<T>()`**
177    ///
178    /// [`data`]: CDataOnStack::data
179    /// [`ctypeid`]: CDataOnStack::ctypeid
180    #[inline(always)]
181    pub fn try_downcast_into<T>(self) -> Result<T, Self>
182    where
183        T: AsCData,
184    {
185        self.check_ctypeid::<T>()
186            .then(|| unsafe { std::ptr::read(self.as_ptr().cast::<T>()) })
187            .ok_or(self)
188    }
189
190    #[inline(always)]
191    #[allow(clippy::nonminimal_bool)]
192    fn check_ctypeid<T: AsCData>(&self) -> bool {
193        self.ctypeid == T::ctypeid() && {
194            if cfg!(debug_assertions) {
195                assert_eq!(self.data().len(), std::mem::size_of::<T>());
196            }
197            true
198        }
199    }
200}
201
202impl<L> FromObject<L> for CDataOnStack<'_, L>
203where
204    L: AsLua,
205{
206    unsafe fn check(lua: impl AsLua, index: NonZeroI32) -> bool {
207        ffi::lua_type(lua.as_lua(), index.into()) == ffi::LUA_TCDATA
208    }
209
210    unsafe fn from_obj(inner: Object<L>) -> Self {
211        let mut ctypeid = 0;
212        let cdata = ffi::luaL_checkcdata(inner.as_lua(), inner.index().into(), &mut ctypeid);
213        Self {
214            inner,
215            data: UnsafeCell::new(CDataRef::Ptr(cdata)),
216            ctypeid,
217        }
218    }
219}
220
221impl<L> TryFrom<Object<L>> for CDataOnStack<'_, L>
222where
223    L: AsLua,
224{
225    type Error = Object<L>;
226
227    #[inline(always)]
228    fn try_from(o: Object<L>) -> Result<Self, Self::Error> {
229        Self::try_from_obj(o)
230    }
231}
232
233impl<'a, L> From<CDataOnStack<'a, L>> for Object<L> {
234    fn from(ud: CDataOnStack<'a, L>) -> Self {
235        ud.inner
236    }
237}
238
239impl<L> LuaRead<L> for CDataOnStack<'_, L>
240where
241    L: AsLua,
242{
243    #[inline]
244    fn lua_read_at_position(lua: L, index: NonZeroI32) -> ReadResult<Self, L> {
245        Self::try_from_obj(Object::new(lua, index)).map_err(|l| {
246            let g = Object::into_guard(l);
247            let e = WrongType::info("reading cdata")
248                .expected_type::<Self>()
249                .actual_single_lua(&g, index);
250            (g, e)
251        })
252    }
253}
254
255impl<L, O> Push<L> for CDataOnStack<'_, O>
256where
257    L: AsLua,
258    O: AsLua,
259{
260    type Err = crate::Void;
261    #[inline]
262    fn push_to_lua(&self, lua: L) -> Result<crate::PushGuard<L>, (Self::Err, L)> {
263        unsafe {
264            crate::ffi::lua_pushvalue(lua.as_lua(), self.inner.index().into());
265            Ok(crate::PushGuard::new(lua, 1))
266        }
267    }
268}
269
270impl<L> AsLua for CDataOnStack<'_, L>
271where
272    L: AsLua,
273{
274    #[inline]
275    fn as_lua(&self) -> LuaState {
276        self.inner.as_lua()
277    }
278}
279
280////////////////////////////////////////////////////////////////////////////////
281// AsCData
282////////////////////////////////////////////////////////////////////////////////
283
284/// Types implementing this trait can be represented as luajit's cdata.
285///
286/// # Safety
287/// The implementor must make sure that the type can actually be used as cdata,
288/// for example the CTypeID must be correct and the size of the type must be the
289/// same as `ffi.sizeof(<typeid>)`
290pub unsafe trait AsCData: Sized + 'static {
291    /// This function will be called every time the ctypeid is needed, so
292    /// implement your own caching if it's required.
293    fn ctypeid() -> ffi::CTypeID;
294}
295
296macro_rules! impl_builtin_as_cdata {
297    ($($t:ty: $ctid:expr),* $(,)?) => {
298        $(
299            unsafe impl AsCData for $t {
300                fn ctypeid() -> ffi::CTypeID {
301                    $ctid
302                }
303            }
304        )*
305    };
306}
307
308impl_builtin_as_cdata! {
309    i8 : ffi::CTID_INT8,
310    i16: ffi::CTID_INT16,
311    i32: ffi::CTID_INT32,
312    i64: ffi::CTID_INT64,
313    u8 : ffi::CTID_UINT8,
314    u16: ffi::CTID_UINT16,
315    u32: ffi::CTID_UINT32,
316    u64: ffi::CTID_UINT64,
317    f32: ffi::CTID_FLOAT,
318    f64: ffi::CTID_DOUBLE,
319
320    bool: ffi::CTID_BOOL,
321
322    *mut   c_void: ffi::CTID_P_VOID,
323    *const c_void: ffi::CTID_P_CVOID,
324    *const c_char: ffi::CTID_P_CCHAR,
325
326    isize:
327        match std::mem::size_of::<isize>() {
328            4 => ffi::CTID_INT32,
329            8 => ffi::CTID_INT64,
330            _ => unimplemented!("only 32 & 64 bit pointers are supported"),
331        },
332    usize:
333        match std::mem::size_of::<usize>() {
334            4 => ffi::CTID_UINT32,
335            8 => ffi::CTID_UINT64,
336            _ => unimplemented!("only 32 & 64 bit pointers are supported"),
337        },
338}
339
340////////////////////////////////////////////////////////////////////////////////
341// CData
342////////////////////////////////////////////////////////////////////////////////
343
344/// A wrapper type for reading/writing rust values as luajit cdata.
345/// ```no_run
346/// use tlua::{Lua, CData};
347/// let lua = Lua::new();
348/// lua.set("cdata", CData(1337_i16));
349/// let ty: String = lua.eval("return require('ffi').typeof(cdata)").unwrap();
350/// assert_eq!(ty, "ctype<short>");
351///
352/// let CData(num): CData<i16> = lua.get("cdata").unwrap();
353/// assert_eq!(num, 1337);
354/// ```
355///
356/// For this to work the type must implement [`AsCData`] which is true for
357/// builtin numbers and some pointers but can also be implemented for user
358/// defined types:
359/// ```no_run
360/// use tlua::{AsCData, CData};
361/// use tlua::{Lua, AsLua, ffi, c_ptr};
362/// # let lua = Lua::new();
363///
364/// #[repr(C)]
365/// #[derive(Debug, PartialEq, Clone, Copy)]
366/// struct S { i: i32, f: f32 }
367///
368/// // let luajit know about our struct
369/// lua.exec("ffi.cdef[[ struct S { int i; float f; }; ]]").unwrap();
370///
371/// // save the CTypeID of our struct
372/// static mut CTID_STRUCT_S: Option<ffi::CTypeID> = None;
373/// let ctid = unsafe { ffi::luaL_ctypeid(lua.as_lua(), c_ptr!("struct S")) };
374/// unsafe { CTID_STRUCT_S = Some(ctid) }
375///
376/// // implement AsCData for our struct so that it can be wrapped with CData
377/// unsafe impl AsCData for S {
378///     fn ctypeid() -> ffi::CTypeID {
379///         unsafe { CTID_STRUCT_S.unwrap() }
380///     }
381/// }
382///
383/// // wirte our struct into a lua variable as cdata
384/// lua.set("tmp", CData(S { i: 69, f: 420.0 }));
385///
386/// // check it's type
387/// let ty: String = lua.eval("return type(tmp)").unwrap();
388/// assert_eq!(ty, "cdata");
389///
390/// // read the value back
391/// let CData(res): CData<S> = lua.get("tmp").unwrap();
392/// assert_eq!(res, S { i: 69, f: 420.0 });
393/// ```
394#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
395pub struct CData<T>(pub T)
396where
397    T: AsCData;
398
399impl<L, T> PushInto<L> for CData<T>
400where
401    L: AsLua,
402    T: AsCData,
403{
404    type Err = crate::Void;
405    fn push_into_lua(self, lua: L) -> Result<crate::PushGuard<L>, (Self::Err, L)> {
406        let Self(value) = self;
407        unsafe {
408            let ptr = ffi::luaL_pushcdata(lua.as_lua(), T::ctypeid());
409            std::ptr::write(ptr.cast::<T>(), value);
410            Ok(crate::PushGuard::new(lua, 1))
411        }
412    }
413}
414impl<L, T> PushOneInto<L> for CData<T>
415where
416    L: AsLua,
417    T: AsCData,
418    T: Copy,
419{
420}
421
422impl<L, T> LuaRead<L> for CData<T>
423where
424    L: AsLua,
425    T: AsCData,
426{
427    fn lua_read_at_position(lua: L, index: NonZeroI32) -> ReadResult<Self, L> {
428        CDataOnStack::lua_read_at_position(lua, index).and_then(|data| {
429            match data.try_downcast_into() {
430                Ok(value) => Ok(CData(value)),
431                Err(data) => {
432                    let g = data.inner.into_guard();
433                    let e = WrongType::info("reading cdata")
434                        .expected_type::<T>()
435                        .actual_single_lua(&g, index);
436                    Err((g, e))
437                }
438            }
439        })
440    }
441}