1use core::mem::MaybeUninit;
2use core::ptr::{self, NonNull};
3
4use crate::Table;
5use crate::VmErrorResult;
6use crate::gc::{GcBarrier, GcObject, GcRuntime};
7use crate::handle::RawHandle;
8use crate::handle::sealed::Sealed;
9use crate::layout::Align8Byte;
10use crate::memory::{LuaPage, MemoryRuntime};
11use crate::native::NativeCallResult;
12use crate::state::{GlobalState, ThreadState};
13use crate::string::TString;
14use crate::table::RawLuaTable;
15use crate::thread::Thread;
16use crate::types::LUA_TUSERDATA;
17use crate::value::{RawTValue, TValue};
18
19mod typed;
20
21pub use typed::UserdataTypeRegistration;
22pub(crate) use typed::UserdataTypeRegistry;
23pub use typed::{
24 TypedUserdata, TypedUserdataAccess, TypedUserdataError, UserdataTypeRegistryAccess,
25};
26
27#[repr(C)]
28pub struct RawUserdata {
29 pub tt: u8,
30 pub marked: u8,
31 pub memcat: u8,
32 pub tag: u8,
33 pub len: i32,
34 pub metatable: *mut RawLuaTable,
35 pub data: [Align8Byte; 1],
36}
37
38#[derive(Clone, Copy, PartialEq, Eq)]
39#[repr(transparent)]
40pub struct Userdata {
41 raw: NonNull<RawUserdata>,
42}
43
44impl Userdata {
45 pub const unsafe fn from_raw(raw: NonNull<RawUserdata>) -> Self {
53 Self { raw }
54 }
55
56 const fn size_userdata(payload_len: usize) -> usize {
57 core::mem::offset_of!(RawUserdata, data)
58 + if payload_len > 16 {
59 (payload_len + 15) & !15
60 } else {
61 payload_len
62 }
63 }
64
65 pub fn allocation_size_for_payload_len(payload_len: usize) -> Option<usize> {
66 let max_len = i32::MAX as usize - core::mem::size_of::<RawUserdata>();
67 (payload_len <= max_len).then(|| Self::size_userdata(payload_len))
68 }
69
70 pub unsafe fn metatable(&self) -> Option<Table> {
77 unsafe {
78 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().metatable)
79 .map(|raw| Table::from_raw(raw))
80 }
81 }
82
83 pub unsafe fn set_metatable(&self, metatable: Option<Table>) {
90 unsafe {
91 self.as_ptr().as_mut().unwrap_unchecked().metatable =
92 metatable.map_or(core::ptr::null_mut(), |table| table.as_ptr())
93 };
94 }
95
96 pub fn total_payload_len(&self) -> usize {
97 unsafe { self.as_ptr().as_ref().unwrap_unchecked().len as usize }
98 }
99
100 pub const fn data_ptr(&self) -> *const u8 {
101 unsafe { (&raw const self.raw.as_ref().data).cast::<u8>() }
102 }
103
104 pub const unsafe fn data_mut_ptr(&self) -> *mut u8 {
111 unsafe { (&raw mut (*self.raw.as_ptr()).data).cast::<u8>() }
112 }
113
114 unsafe fn inline_destructor_slot(&self, requested_payload_len: usize) -> *mut u8 {
115 unsafe { self.data_mut_ptr().add(requested_payload_len).cast::<u8>() }
116 }
117
118 pub unsafe fn set_inline_destructor(
125 &self,
126 requested_payload_len: usize,
127 destructor: LuaInlineDestructor,
128 ) {
129 debug_assert_eq!(
130 unsafe { self.as_ptr().as_ref().unwrap_unchecked().tag as usize },
131 USERDATA_TAG_IDTOR
132 );
133
134 let inline_destructor_size = core::mem::size_of::<LuaInlineDestructor>();
135 debug_assert!(self.total_payload_len() >= inline_destructor_size);
136 debug_assert_eq!(
137 self.total_payload_len() - inline_destructor_size,
138 requested_payload_len
139 );
140
141 unsafe {
142 ptr::copy_nonoverlapping(
143 (&raw const destructor).cast::<u8>(),
144 self.inline_destructor_slot(requested_payload_len),
145 inline_destructor_size,
146 );
147 }
148 }
149
150 pub unsafe fn inline_destructor(&self) -> LuaInlineDestructor {
157 debug_assert_eq!(
158 unsafe { self.as_ptr().as_ref().unwrap_unchecked().tag as usize },
159 USERDATA_TAG_IDTOR
160 );
161
162 let mut destructor = MaybeUninit::<LuaInlineDestructor>::uninit();
163 unsafe {
164 let inline_destructor_size = core::mem::size_of::<LuaInlineDestructor>();
165 debug_assert!(self.total_payload_len() >= inline_destructor_size);
166 ptr::copy_nonoverlapping(
167 self.inline_destructor_slot(self.total_payload_len() - inline_destructor_size),
168 destructor.as_mut_ptr().cast::<u8>(),
169 inline_destructor_size,
170 );
171 destructor.assume_init()
172 }
173 }
174
175 pub unsafe fn allocation_size(&self) -> usize {
182 Self::size_userdata(self.total_payload_len())
183 }
184}
185
186impl crate::handle::sealed::Sealed for Userdata {}
187
188impl RawHandle for Userdata {
189 type Raw = RawUserdata;
190
191 fn as_ptr(&self) -> *mut Self::Raw {
192 self.raw.as_ptr()
193 }
194}
195
196impl AsRef<Userdata> for Userdata {
197 fn as_ref(&self) -> &Userdata {
198 self
199 }
200}
201
202#[repr(C)]
203pub struct LuaUserdataDirectAccessData {
204 pub index_tm: RawTValue,
205 pub new_index_tm: RawTValue,
206 pub name_call_tm: RawTValue,
207 pub index: Option<LuaUserdataDirectAccess>,
208 pub new_index: Option<LuaUserdataDirectAccess>,
209 pub name_call: Option<LuaUserdataDirectNamecall>,
210}
211
212impl GlobalState {
213 pub fn userdata_direct_field(&self, tag: usize) -> Option<Table> {
214 unsafe {
215 Some(Table::from_raw(NonNull::new(
216 self.as_ptr()
217 .as_ref()
218 .unwrap_unchecked()
219 .userdata_direct_fields[tag],
220 )?))
221 }
222 }
223
224 pub fn set_userdata_direct_field(&self, tag: usize, table: Option<Table>) {
225 unsafe {
226 self.as_ptr()
227 .as_mut()
228 .unwrap_unchecked()
229 .userdata_direct_fields[tag] =
230 table.map_or(core::ptr::null_mut(), |table| table.as_ptr());
231 }
232 }
233}
234
235#[allow(
243 clippy::missing_safety_doc,
244 reason = "all methods share the capability-level safety contract"
245)]
246pub trait UserdataRuntime: Sealed {
247 unsafe fn new_userdata_tagged_internal(&self, size: usize, tag: i32) -> VmErrorResult<*mut ()>;
248
249 unsafe fn new_userdata_internal(&self, size: usize, tag: i32) -> VmErrorResult<Userdata>;
251
252 unsafe fn free_userdata(&self, userdata: Userdata, page: LuaPage);
254}
255
256pub const LIGHT_USERDATA_TAG_LIMIT: usize = 128;
258
259pub const USERDATA_TAG_LIMIT: usize = 128;
261
262pub(crate) const USERDATA_TAG_IDTOR: usize = USERDATA_TAG_LIMIT;
263pub(crate) const USERDATA_TAG_PROXY: usize = USERDATA_TAG_IDTOR + 1;
264pub(crate) const USERDATA_INTERNAL_LIMIT: usize = USERDATA_TAG_PROXY + 1;
265
266pub type LuaDestructor = fn(&Thread, *mut ());
267
268pub type LuaUserdataMark = fn(&Thread, *mut ());
269
270pub type LuaInlineDestructor = fn(*mut ());
271
272pub type LuaUserdataDirectAccess = fn(&Thread, *mut (), i32, *mut u16, i32) -> VmErrorResult;
273
274pub type LuaUserdataDirectNamecall = fn(&Thread, *mut (), i32, *mut u16, i32) -> NativeCallResult;
275
276pub type LuaUserdataDirectFieldGet = fn(*mut (), &mut UserdataDirectFieldResult);
277
278impl GlobalState {
279 pub fn userdata_metatable(&self, tag: usize) -> Option<Table> {
280 unsafe {
281 Some(Table::from_raw(NonNull::new(
282 self.as_ptr().as_ref().unwrap_unchecked().userdata_mt[tag],
283 )?))
284 }
285 }
286
287 pub fn set_userdata_metatable(&self, tag: usize, table: Option<Table>) {
288 unsafe {
289 (*self.as_ptr()).userdata_mt[tag] =
290 table.map_or(core::ptr::null_mut(), |table| table.as_ptr());
291 }
292 }
293
294 pub fn userdata_dtor(&self, tag: usize) -> Option<LuaDestructor> {
295 unsafe { (*self.as_ptr()).userdata_gc[tag] }
296 }
297
298 pub fn set_userdata_dtor(&self, tag: usize, destructor: Option<LuaDestructor>) {
299 unsafe {
300 (*self.as_ptr()).userdata_gc[tag] = destructor;
301 }
302 }
303
304 pub fn userdata_mark(&self, tag: usize) -> Option<LuaUserdataMark> {
305 unsafe { (*self.as_ptr()).userdata_mark[tag] }
306 }
307
308 pub fn set_userdata_mark(&self, tag: usize, mark: Option<LuaUserdataMark>) {
309 unsafe {
310 (*self.as_ptr()).userdata_mark[tag] = mark;
311 }
312 }
313
314 pub fn light_userdata_name(&self, tag: usize) -> Option<TString> {
315 unsafe {
316 NonNull::new((*self.as_ptr()).light_userdata_name[tag])
317 .map(|raw| TString::from_raw(raw))
318 }
319 }
320
321 pub fn set_light_userdata_name(&self, tag: usize, name: Option<TString>) {
322 unsafe {
323 (*self.as_ptr()).light_userdata_name[tag] =
324 name.map_or(core::ptr::null_mut(), |name| name.as_ptr());
325 }
326 }
327}
328
329#[repr(transparent)]
330pub struct UserdataDirectFieldResult(pub(crate) TValue);
331
332impl UserdataRuntime for Thread {
333 unsafe fn new_userdata_tagged_internal(&self, size: usize, tag: i32) -> VmErrorResult<*mut ()> {
334 assert!((tag as u32) < USERDATA_INTERNAL_LIMIT as u32);
335 unsafe {
336 self.check_gc()?;
337 self.thread_barrier();
338 self.ensure_stack(self, 1)?;
339
340 let userdata = self.new_userdata_internal(size, tag)?;
341 let data = userdata.data_mut_ptr();
342 let top = self.stack_top();
343 top.value_unchecked().set_userdata_value(userdata);
344 debug_assert!(top < self.current_call_info().top());
345 self.set_stack_top(top.add(1));
346
347 Ok(data.cast())
348 }
349 }
350
351 unsafe fn new_userdata_internal(
353 &self,
354 payload_len: usize,
355 tag: i32,
356 ) -> VmErrorResult<Userdata> {
357 let Some(allocation_size) = Userdata::allocation_size_for_payload_len(payload_len) else {
358 return unsafe { self.too_big() };
359 };
360
361 unsafe {
362 let userdata = self.new_gco::<Userdata>(
363 allocation_size,
364 self.as_ptr().as_ref().unwrap_unchecked().active_memcat,
365 )?;
366 GcObject::from(userdata).init_header(self, LUA_TUSERDATA as u8);
367 userdata.as_ptr().as_mut().unwrap_unchecked().len = payload_len as i32;
368 userdata.set_metatable(None);
369 assert!((tag as u32) < USERDATA_INTERNAL_LIMIT as u32);
370 userdata.as_ptr().as_mut().unwrap_unchecked().tag = tag as u8;
371
372 Ok(userdata)
373 }
374 }
375
376 unsafe fn free_userdata(&self, userdata: Userdata, page: LuaPage) {
378 let userdata_ref = unsafe { userdata.as_ptr().as_ref().unwrap_unchecked() };
379 let tag = userdata_ref.tag;
380 let memcat = userdata_ref.memcat;
381 let data = unsafe { userdata.data_mut_ptr() };
382
383 unsafe {
384 if tag < USERDATA_TAG_LIMIT as u8 {
385 if let Some(dtor) = self.global().userdata_dtor(tag as usize) {
386 dtor(self, data.cast());
387 }
388 } else if tag == USERDATA_TAG_IDTOR as u8 {
389 let dtor = userdata.inline_destructor();
390 dtor(data.cast());
391 }
392
393 self.free_gco(userdata.into(), userdata.allocation_size(), memcat, page);
394 }
395 }
396}
397
398impl UserdataDirectFieldResult {
399 pub fn set_number(&mut self, value: f64) {
401 self.0.set_number(value);
402 }
403
404 pub fn set_vector(&mut self, value: [f32; crate::types::LUA_VECTOR_SIZE]) {
406 self.0.set_vector(value);
407 }
408
409 pub fn set_boolean(&mut self, value: i32) {
411 self.0.set_boolean(value);
412 }
413
414 pub fn set_integer64(&mut self, value: i64) {
416 self.0.set_integer(value);
417 }
418
419 pub fn set_nil(&mut self) {
421 self.0.set_nil();
422 }
423}