1use core::mem::{self, offset_of};
2use core::ptr::{self, NonNull};
3
4use crate::Table;
5use crate::gc::GcObject;
6use crate::handle::RawHandle;
7use crate::memory::MemoryRuntime;
8use crate::string::TString;
9use crate::thread::Thread;
10use crate::types::{
11 LUA_EXTRA_SIZE, LUA_TBOOLEAN, LUA_TDEADKEY, LUA_TINTEGER, LUA_TLIGHTUSERDATA, LUA_TNIL,
12 LUA_TNUMBER, LUA_TSTRING, LUA_TVECTOR, LUA_VECTOR_SIZE,
13};
14use crate::value::{RAW_TVALUE_NIL, RawTValue, RawValue, TValue, TValueCursor};
15
16use super::{RawLuaTable, RawLuaTableFree};
17
18#[derive(Clone, Copy)]
19#[repr(C)]
20pub struct RawTKey {
21 pub value: RawValue,
22 pub extra: [i32; LUA_EXTRA_SIZE],
23 pub tt_next: u32,
24}
25
26pub const RAW_TKEY_NIL: RawTKey = RawTKey {
27 value: RawValue {
28 pointer: core::ptr::null_mut(),
29 },
30 extra: [0; LUA_EXTRA_SIZE],
31 tt_next: LUA_TNIL as u32,
32};
33
34pub const RAW_TKEY_DEAD_KEY: RawTKey = RawTKey {
35 value: RawValue {
36 pointer: core::ptr::null_mut(),
37 },
38 extra: [0; LUA_EXTRA_SIZE],
39 tt_next: LUA_TDEADKEY as u32,
40};
41
42#[derive(Clone, Copy, PartialEq, Eq)]
43#[repr(transparent)]
44pub struct TKey {
49 raw: NonNull<RawTKey>,
50}
51
52#[allow(
53 clippy::missing_safety_doc,
54 reason = "TKey is a non-owning table-key view governed by internal's raw-view contract"
55)]
56impl TKey {
57 const TT_BITS: u32 = 4;
58 const TT_MASK: u32 = (1 << Self::TT_BITS) - 1;
59 const NEXT_SHIFT: u32 = Self::TT_BITS;
60 const NEXT_BITS: u32 = 32 - Self::NEXT_SHIFT;
61 const NEXT_MASK: u32 = !Self::TT_MASK;
62
63 pub const unsafe fn from_raw(raw: NonNull<RawTKey>) -> Self {
64 Self { raw }
65 }
66
67 pub fn set_nil(&self) {
68 unsafe {
69 *self.as_ptr().as_mut().unwrap_unchecked() = RAW_TKEY_NIL;
70 }
71 }
72
73 pub fn tt(&self) -> i32 {
74 (unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt_next } & Self::TT_MASK) as i32
75 }
76
77 pub fn set_tt(&self, tt: i32) {
79 unsafe {
80 let raw = self.as_ptr().as_mut().unwrap_unchecked();
81 raw.tt_next = (raw.tt_next & Self::NEXT_MASK) | (tt as u32 & Self::TT_MASK);
82 }
83 }
84
85 pub fn next(&self) -> i32 {
86 let raw = unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt_next } >> Self::NEXT_SHIFT;
87 let shift = 32 - Self::NEXT_BITS;
88 ((raw << shift) as i32) >> shift
89 }
90
91 pub fn set_next(&self, next: i32) {
93 let next_bits = ((next as u32) << Self::NEXT_SHIFT) & Self::NEXT_MASK;
94 unsafe {
95 let raw = self.as_ptr().as_mut().unwrap_unchecked();
96 raw.tt_next = (raw.tt_next & Self::TT_MASK) | next_bits;
97 }
98 }
99
100 pub fn is_nil(&self) -> bool {
101 self.tt() == LUA_TNIL
102 }
103
104 pub fn is_dead_key(&self) -> bool {
105 self.tt() == LUA_TDEADKEY
106 }
107
108 pub fn is_collectable(&self) -> bool {
110 self.tt() >= LUA_TSTRING
111 }
112
113 pub fn gc_value(&self) -> GcObject {
115 debug_assert!(self.is_collectable());
116 unsafe {
117 GcObject::from_raw(NonNull::new_unchecked(
118 self.as_ptr().as_ref().unwrap_unchecked().value.gc,
119 ))
120 }
121 }
122
123 pub fn pointer_value(&self) -> *mut () {
125 debug_assert!(self.tt() == LUA_TLIGHTUSERDATA);
126 unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.pointer }
127 }
128
129 pub fn number_value(&self) -> f64 {
131 debug_assert!(self.tt() == LUA_TNUMBER);
132 unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.number }
133 }
134
135 pub fn integer_value(&self) -> i64 {
137 debug_assert!(self.tt() == LUA_TINTEGER);
138 unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.integer }
139 }
140
141 pub fn boolean_value(&self) -> i32 {
143 debug_assert!(self.tt() == LUA_TBOOLEAN);
144 unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.boolean }
145 }
146
147 pub fn light_userdata_tag(&self) -> i32 {
149 debug_assert!(self.tt() == LUA_TLIGHTUSERDATA);
150 unsafe { self.as_ptr().as_ref().unwrap_unchecked().extra[0] }
151 }
152
153 pub fn vector_value(&self) -> [f32; LUA_VECTOR_SIZE] {
155 debug_assert!(self.tt() == LUA_TVECTOR);
156 let vector = self.as_ptr().cast::<f32>();
157 #[cfg(not(feature = "vector4"))]
158 unsafe {
159 [*vector, *vector.add(1), *vector.add(2)]
160 }
161 #[cfg(feature = "vector4")]
162 unsafe {
163 [*vector, *vector.add(1), *vector.add(2), *vector.add(3)]
164 }
165 }
166
167 pub fn raw_equal_value(&self, other: impl Into<TValue>) -> bool {
169 let other = other.into();
170 if self.tt() != unsafe { other.as_ptr().as_ref().unwrap_unchecked().tt } {
171 return false;
172 }
173
174 match self.tt() {
175 x if x == LUA_TNIL => true,
176 x if x == LUA_TNUMBER => self.number_value() == other.number_value(),
177 x if x == LUA_TINTEGER => self.integer_value() == other.integer_value(),
178 x if x == LUA_TVECTOR => self.vector_value() == other.vector_value(),
179 x if x == LUA_TBOOLEAN => self.boolean_value() == other.boolean_value(),
180 x if x == LUA_TLIGHTUSERDATA => {
181 self.pointer_value() == other.pointer_value()
182 && self.light_userdata_tag() == other.light_userdata_tag()
183 }
184 _ => {
185 debug_assert!(self.is_collectable());
186 self.gc_value() == other.gc_value()
187 }
188 }
189 }
190}
191impl crate::handle::sealed::Sealed for TKey {}
192impl RawHandle for TKey {
193 type Raw = RawTKey;
194
195 fn as_ptr(&self) -> *mut Self::Raw {
196 self.raw.as_ptr()
197 }
198}
199
200impl AsRef<TKey> for TKey {
201 fn as_ref(&self) -> &TKey {
202 self
203 }
204}
205
206#[repr(C)]
207pub struct RawLuaNode {
208 pub value: RawTValue,
209 pub key: RawTKey,
210}
211
212pub const RAW_LUA_NODE_DUMMY: RawLuaNode = RawLuaNode {
213 value: RAW_TVALUE_NIL,
214 key: RAW_TKEY_NIL,
215};
216
217#[derive(Clone, Copy, PartialEq, Eq)]
218#[repr(transparent)]
219pub struct LuaNode {
224 raw: NonNull<RawLuaNode>,
225}
226
227#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
228#[repr(transparent)]
229pub struct LuaNodeCursor(*mut RawLuaNode);
235
236#[allow(
237 clippy::missing_safety_doc,
238 reason = "LuaNode is a non-owning table-node view governed by Table's contract"
239)]
240impl LuaNode {
241 pub const unsafe fn from_raw(raw: NonNull<RawLuaNode>) -> Self {
242 Self { raw }
243 }
244
245 pub fn value(&self) -> TValue {
246 unsafe { TValue::from_raw(NonNull::new_unchecked(&raw mut (*self.as_ptr()).value)) }
247 }
248
249 pub fn value_unchecked(&self) -> TValue {
250 self.value()
251 }
252
253 pub fn key(&self) -> TKey {
254 unsafe { TKey::from_raw(NonNull::new_unchecked(&raw mut (*self.as_ptr()).key)) }
255 }
256
257 pub fn has_string_key(&self, key: TString) -> bool {
258 unsafe {
259 const TT_MASK: u32 = (1 << 4) - 1;
260
261 let raw = self.as_ptr();
262 let key_raw = &raw const (*raw).key;
263 ((*key_raw).tt_next & TT_MASK) as i32 == LUA_TSTRING
264 && (*key_raw).value.gc.cast() == key.as_ptr()
265 }
266 }
267
268 pub fn value_is_nil(&self) -> bool {
269 unsafe { (*self.as_ptr()).value.tt == LUA_TNIL }
270 }
271
272 pub fn next(&self) -> i32 {
274 self.key().next()
275 }
276
277 pub fn set_key_from_value(&self, value: impl Into<TValue>) {
279 let value = value.into();
280 unsafe {
281 ptr::copy_nonoverlapping(
282 (&raw const (*value.as_ptr()).value).cast::<u8>(),
283 (&raw mut (*self.as_ptr()).key.value).cast::<u8>(),
284 core::mem::size_of::<RawValue>(),
285 );
286 ptr::copy_nonoverlapping(
287 (&raw const (*value.as_ptr()).extra).cast::<u8>(),
288 (&raw mut (*self.as_ptr()).key.extra).cast::<u8>(),
289 core::mem::size_of::<[i32; LUA_EXTRA_SIZE]>(),
290 );
291 self.key().set_tt((*value.as_ptr()).tt);
292 }
293 }
294
295 pub fn write_key_to_value(&self, value: impl Into<TValue>) {
297 let value = value.into();
298 unsafe {
299 ptr::copy_nonoverlapping(
300 (&raw const (*self.as_ptr()).key.value).cast::<u8>(),
301 (&raw mut (*value.as_ptr()).value).cast::<u8>(),
302 core::mem::size_of::<RawValue>(),
303 );
304 ptr::copy_nonoverlapping(
305 (&raw const (*self.as_ptr()).key.extra).cast::<u8>(),
306 (&raw mut (*value.as_ptr()).extra).cast::<u8>(),
307 core::mem::size_of::<[i32; LUA_EXTRA_SIZE]>(),
308 );
309 (*value.as_ptr()).tt = self.key().tt();
310 }
311 }
312}
313
314#[allow(
315 clippy::missing_safety_doc,
316 reason = "LuaNodeCursor navigation is governed by Table's storage contract"
317)]
318impl LuaNodeCursor {
319 pub const fn as_ptr(&self) -> *mut RawLuaNode {
323 self.0
324 }
325
326 pub const fn from_ptr(raw: *mut RawLuaNode) -> Self {
327 Self(raw)
328 }
329
330 pub const fn is_null(&self) -> bool {
331 self.0.is_null()
332 }
333
334 pub unsafe fn node_unchecked(&self) -> LuaNode {
335 debug_assert!(!self.is_null());
336 unsafe { LuaNode::from_raw(NonNull::new_unchecked(self.0)) }
337 }
338
339 pub fn node(&self) -> Option<LuaNode> {
340 NonNull::new(self.0).map(|raw| unsafe { LuaNode::from_raw(raw) })
341 }
342
343 pub unsafe fn add(self, count: usize) -> Self {
344 unsafe { Self::from_ptr(self.0.add(count)) }
345 }
346
347 pub unsafe fn sub(self, count: usize) -> Self {
348 unsafe { Self::from_ptr(self.0.sub(count)) }
349 }
350
351 pub unsafe fn offset(self, count: isize) -> Self {
352 unsafe { Self::from_ptr(self.0.offset(count)) }
353 }
354
355 pub unsafe fn offset_from(self, other: Self) -> isize {
356 unsafe { self.0.offset_from(other.0) }
357 }
358}
359impl crate::handle::sealed::Sealed for LuaNode {}
360impl RawHandle for LuaNode {
361 type Raw = RawLuaNode;
362
363 fn as_ptr(&self) -> *mut Self::Raw {
364 self.raw.as_ptr()
365 }
366}
367
368impl AsRef<LuaNode> for LuaNode {
369 fn as_ref(&self) -> &LuaNode {
370 self
371 }
372}
373
374impl AsRef<LuaNodeCursor> for LuaNodeCursor {
375 fn as_ref(&self) -> &LuaNodeCursor {
376 self
377 }
378}
379
380const _: () = assert!(offset_of!(RawLuaNode, value) == 0);
381
382#[allow(
383 clippy::missing_safety_doc,
384 reason = "Table's shared raw-handle contract is documented on Table"
385)]
386impl Table {
387 pub const unsafe fn from_raw(raw: NonNull<RawLuaTable>) -> Self {
388 Self { raw }
389 }
390
391 pub unsafe fn node(&self, index: i32) -> LuaNode {
392 unsafe { self.node_cursor().add(index as usize).node_unchecked() }
393 }
394
395 pub unsafe fn node_mut(&mut self, index: i32) -> LuaNode {
396 unsafe {
397 LuaNode::from_raw(NonNull::new_unchecked(
398 self.as_ptr()
399 .as_mut()
400 .unwrap_unchecked()
401 .node
402 .add(index as usize),
403 ))
404 }
405 }
406
407 pub unsafe fn metatable(&self) -> Option<Table> {
408 unsafe {
409 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().metatable)
410 .map(|raw| Table::from_raw(raw))
411 }
412 }
413
414 pub unsafe fn set_metatable(&self, metatable: Option<Table>) {
415 unsafe {
416 self.as_ptr().as_mut().unwrap_unchecked().metatable =
417 metatable.map_or(core::ptr::null_mut(), |table| table.as_ptr())
418 };
419 }
420
421 pub unsafe fn gc_list(&self) -> Option<GcObject> {
422 unsafe {
423 NonNull::new(self.as_ptr().as_ref().unwrap_unchecked().gc_list)
424 .map(|raw| GcObject::from_raw(raw))
425 }
426 }
427
428 pub unsafe fn set_gc_list(&self, gc_list: Option<GcObject>) {
429 unsafe {
430 self.as_ptr().as_mut().unwrap_unchecked().gc_list =
431 gc_list.map_or(ptr::null_mut(), |object| object.as_ptr());
432 }
433 }
434
435 pub unsafe fn invalidate_tm_cache(&self) {
436 unsafe { self.as_ptr().as_mut().unwrap_unchecked().tm_cache = 0 };
437 }
438
439 pub unsafe fn set_node(&self, node_cursor: LuaNodeCursor) {
440 unsafe { self.as_ptr().as_mut().unwrap_unchecked().node = node_cursor.as_ptr() };
441 }
442
443 pub unsafe fn set_array(&self, array_cursor: TValueCursor) {
444 unsafe { self.as_ptr().as_mut().unwrap_unchecked().array = array_cursor.as_ptr() };
445 }
446
447 pub unsafe fn has_dummy_node(&self) -> bool {
448 unsafe { self.node_cursor() == Self::dummy_node_cursor() }
449 }
450
451 pub unsafe fn node_index(&self, node_cursor: LuaNodeCursor) -> i32 {
452 unsafe { node_cursor.offset_from(self.node_cursor()) as i32 }
453 }
454
455 pub unsafe fn value_slot_unchecked(&self, value: TValue) -> i32 {
460 unsafe {
461 let value_addr = value.as_ptr() as usize;
462 let node_addr = self.as_ptr().as_ref().unwrap_unchecked().node as usize;
463 (value_addr.wrapping_sub(node_addr) / mem::size_of::<RawLuaNode>()) as i32
464 }
465 }
466
467 pub unsafe fn node_cursor(&self) -> LuaNodeCursor {
468 unsafe { LuaNodeCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().node) }
469 }
470
471 pub unsafe fn array_cursor(&self) -> TValueCursor {
472 unsafe { TValueCursor::from_ptr(self.as_ptr().as_ref().unwrap_unchecked().array) }
473 }
474
475 pub unsafe fn array_slot(&self, index: usize) -> TValue {
476 unsafe { self.array_cursor().add(index).value_unchecked() }
477 }
478
479 pub unsafe fn array_slot_for_key(&self, key: i32) -> Option<TValue> {
480 unsafe {
481 ((key as u32).wrapping_sub(1)
482 < self.as_ptr().as_ref().unwrap_unchecked().size_array as u32)
483 .then(|| self.array_slot((key - 1) as usize))
484 }
485 }
486
487 pub unsafe fn node_count(&self) -> usize {
488 unsafe { 1usize << self.as_ptr().as_ref().unwrap_unchecked().lsize_node }
489 }
490
491 pub unsafe fn hash_mask(&self) -> usize {
492 unsafe { self.node_count() - 1 }
493 }
494
495 pub unsafe fn node_mask_8(&self) -> usize {
496 unsafe { self.as_ptr().as_ref().unwrap_unchecked().node_mask_8 as usize }
497 }
498
499 pub unsafe fn array_storage_size(&self) -> usize {
500 unsafe {
501 self.as_ptr().as_ref().unwrap_unchecked().size_array as usize
502 * mem::size_of::<RawTValue>()
503 }
504 }
505
506 pub unsafe fn node_storage_size(&self) -> usize {
507 unsafe {
508 if self.has_dummy_node() {
509 0
510 } else {
511 self.node_count() * mem::size_of::<RawLuaNode>()
512 }
513 }
514 }
515
516 pub unsafe fn allocation_size(&self) -> usize {
517 unsafe {
518 mem::size_of::<RawLuaTable>() + self.array_storage_size() + self.node_storage_size()
519 }
520 }
521
522 pub unsafe fn gc_work_size(&self, count_dummy_node: bool) -> usize {
523 unsafe {
524 let node_size = if count_dummy_node {
525 self.node_count() * mem::size_of::<RawLuaNode>()
526 } else {
527 self.node_storage_size()
528 };
529 mem::size_of::<RawLuaTable>() + self.array_storage_size() + node_size
530 }
531 }
532
533 pub unsafe fn init_empty_storage(&self) {
534 unsafe {
535 let table_ref = self.as_ptr().as_mut().unwrap_unchecked();
536 table_ref.array = core::ptr::null_mut();
537 table_ref.size_array = 0;
538 table_ref.lsize_node = 0;
539 table_ref.readonly = 0;
540 table_ref.safe_env = 0;
541 table_ref.node_mask_8 = 0;
542 table_ref.node = Self::dummy_node_ptr();
543 table_ref.gc_list = core::ptr::null_mut();
544 table_ref.free = RawLuaTableFree { last_free: 0 };
545 }
546 }
547
548 pub unsafe fn free_storage(&self, thread: &Thread) {
549 unsafe {
550 let table_ref = self.as_ptr().as_ref().unwrap_unchecked();
551 if !self.has_dummy_node() {
552 thread.free_array(
553 self.node_cursor().as_ptr(),
554 self.node_count(),
555 table_ref.memcat,
556 );
557 }
558
559 if !table_ref.array.is_null() {
560 thread.free_array(
561 table_ref.array,
562 table_ref.size_array as usize,
563 table_ref.memcat,
564 );
565 }
566 }
567 }
568
569 pub unsafe fn maybe_set_aboundary(&self, value: i32) {
570 if unsafe { self.as_ptr().as_ref().unwrap_unchecked().free.aboundary } <= 0 {
571 unsafe {
572 self.as_ptr().as_mut().unwrap_unchecked().free =
573 RawLuaTableFree { aboundary: -value }
574 };
575 }
576 }
577
578 pub(super) fn update_aboundary(&self, boundary: i32) -> i32 {
580 let size_array = unsafe { self.as_ptr().as_ref().unwrap_unchecked().size_array };
581 let array = unsafe { self.array_cursor() };
582 let boundary_slot_is_nil = unsafe { array.add((boundary - 1) as usize).is_nil_unchecked() };
583 if boundary < size_array && boundary_slot_is_nil {
584 let previous_slot_is_set = if boundary >= 2 {
585 unsafe { !array.add((boundary - 2) as usize).is_nil_unchecked() }
586 } else {
587 false
588 };
589 if previous_slot_is_set {
590 unsafe {
591 self.maybe_set_aboundary(boundary - 1);
592 }
593 return boundary - 1;
594 }
595 } else {
596 let next_slot_is_set = if boundary + 1 < size_array {
597 unsafe { !array.add(boundary as usize).is_nil_unchecked() }
598 } else {
599 false
600 };
601 let slot_after_next_is_nil = if boundary + 1 < size_array {
602 unsafe { array.add((boundary + 1) as usize).is_nil_unchecked() }
603 } else {
604 false
605 };
606 if next_slot_is_set && slot_after_next_is_nil {
607 unsafe {
608 self.maybe_set_aboundary(boundary + 1);
609 }
610 return boundary + 1;
611 }
612 }
613
614 0
615 }
616
617 pub unsafe fn get_aboundary(&self) -> i32 {
618 let aboundary = unsafe { self.as_ptr().as_ref().unwrap_unchecked().free.aboundary };
619 if aboundary < 0 {
620 -aboundary
621 } else {
622 unsafe { self.as_ptr().as_ref().unwrap_unchecked().size_array }
623 }
624 }
625}