use core::mem;
use core::ptr::{self, NonNull};
mod layout;
mod lookup;
pub use layout::{
LuaNode, LuaNodeCursor, RAW_LUA_NODE_DUMMY, RAW_TKEY_DEAD_KEY, RAW_TKEY_NIL, RawLuaNode,
RawTKey, TKey,
};
use crate::VmErrorResult;
use crate::debug::DebugRuntime;
use crate::gc::GcBarrier;
use crate::gc::{GcObject, RawGcObject};
use crate::handle::RawHandle;
use crate::handle::sealed::Sealed;
use crate::memory::{LuaPage, MemoryRuntime};
use crate::string::TString;
use crate::thread::Thread;
use crate::types::{LUA_TNUMBER, LUA_TTABLE};
use crate::value::{RAW_TVALUE_NIL, RawTValue, TValue, TValueCursor, nil_object};
#[repr(C)]
pub struct RawLuaTable {
pub tt: u8,
pub marked: u8,
pub memcat: u8,
pub tm_cache: u8,
pub readonly: u8,
pub safe_env: u8,
pub lsize_node: u8,
pub node_mask_8: u8,
pub size_array: i32,
pub free: RawLuaTableFree,
pub metatable: *mut RawLuaTable,
pub array: *mut RawTValue,
pub node: *mut RawLuaNode,
pub gc_list: *mut RawGcObject,
}
#[repr(C)]
pub union RawLuaTableFree {
pub last_free: i32,
pub aboundary: i32,
}
const LOG_2: [u8; 256] = [
0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
];
fn log_2(mut value: u32) -> i32 {
let mut log = -1;
while value >= 256 {
log += 8;
value >>= 8;
}
log + i32::from(LOG_2[value as usize])
}
pub(super) fn ceil_log_2(value: u32) -> i32 {
log_2(value - 1) + 1
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct Table {
pub(crate) raw: NonNull<RawLuaTable>,
}
#[allow(
clippy::missing_safety_doc,
reason = "all methods share the capability-level safety contract"
)]
pub trait TableRuntime: Sealed {
unsafe fn set_num(&self, table: Table, key: i32) -> VmErrorResult<TValue>;
unsafe fn set_str(&self, table: Table, key: TString) -> VmErrorResult<LuaNodeCursor>;
unsafe fn setp(&self, table: Table, key: *mut (), tag: i32) -> VmErrorResult<TValue>;
unsafe fn set(&self, table: Table, key: TValue) -> VmErrorResult<TValue>;
unsafe fn set_slot(&self, table: Table, slot: TValue, key: TValue) -> VmErrorResult<TValue>;
unsafe fn new_key(&self, table: Table, key: TValue) -> VmErrorResult<TValue>;
unsafe fn next_internal(&self, table: Table, key: TValueCursor) -> VmErrorResult<i32>;
unsafe fn new_table_internal(&self, n_array: i32, n_hash: i32) -> VmErrorResult<Table>;
unsafe fn resize_array(&self, table: Table, n_array: i32) -> VmErrorResult;
unsafe fn resize_hash(&self, table: Table, n_hash: i32) -> VmErrorResult;
unsafe fn free_table(&self, table: Table, page: LuaPage);
unsafe fn clone_table_internal(&self, table: Table) -> VmErrorResult<Table>;
}
impl crate::handle::sealed::Sealed for Table {}
impl RawHandle for Table {
type Raw = RawLuaTable;
fn as_ptr(&self) -> *mut Self::Raw {
self.raw.as_ptr()
}
}
impl AsRef<Table> for Table {
fn as_ref(&self) -> &Table {
self
}
}
const MAX_BITS: i32 = 26;
const MAX_SIZE: i32 = 1 << MAX_BITS;
fn twoto(value: i32) -> i32 {
1 << value
}
fn count_int(key: f64, nums: &mut [i32; (MAX_BITS + 1) as usize]) -> i32 {
match Table::array_index(key) {
Some(index) if index > 0 && index <= MAX_SIZE => {
nums[ceil_log_2(index as u32) as usize] += 1;
1
}
_ => 0,
}
}
fn compute_sizes(nums: &[i32; (MAX_BITS + 1) as usize], n_array: &mut i32) -> i32 {
let mut a = 0;
let mut na = 0;
let mut n = 0;
let mut twotoi = 1;
let mut index = 0;
while twotoi / 2 < *n_array {
if nums[index] > 0 {
a += nums[index];
if a > twotoi / 2 {
n = twotoi;
na = a;
}
}
if a == *n_array {
break;
}
index += 1;
twotoi *= 2;
}
*n_array = n;
debug_assert!(*n_array / 2 <= na && na <= *n_array);
na
}
#[allow(
clippy::missing_safety_doc,
reason = "Table's shared raw-handle contract is documented on Table"
)]
impl Table {
fn num_use_array(&self, nums: &mut [i32; (MAX_BITS + 1) as usize]) -> i32 {
let mut ause = 0;
let mut index = 1;
let mut ttlg = 1;
let size_array = unsafe { self.as_ptr().as_ref().unwrap_unchecked().size_array };
let array = unsafe { self.array_cursor() };
for lg in 0..=MAX_BITS {
let mut lc = 0;
let mut limit = ttlg;
if limit > size_array {
limit = size_array;
if index > limit {
break;
}
}
while index <= limit {
if unsafe { !array.add((index - 1) as usize).is_nil_unchecked() } {
lc += 1;
}
index += 1;
}
nums[lg as usize] += lc;
ause += lc;
ttlg *= 2;
}
ause
}
fn num_use_hash(&self, nums: &mut [i32; (MAX_BITS + 1) as usize], n_array: &mut i32) -> i32 {
let mut total_use = 0;
let mut ause = 0;
let mut index = unsafe { self.node_count() };
while index > 0 {
index -= 1;
let node = unsafe { self.node(index as i32) };
if !node.value_unchecked().is_nil() {
let key = node.key();
if key.tt() == LUA_TNUMBER {
ause += count_int(key.number_value(), nums);
}
total_use += 1;
}
}
*n_array += ause;
total_use
}
unsafe fn set_array_vector(&self, thread: &Thread, size: i32) -> VmErrorResult {
if size > MAX_SIZE {
return unsafe { crate::run_error!(thread, "table overflow") };
}
unsafe {
let old_size = self.as_ptr().as_ref().unwrap_unchecked().size_array as usize;
let new_size = size as usize;
let memcat = self.as_ptr().as_ref().unwrap_unchecked().memcat;
let array = TValueCursor::from_ptr(thread.realloc_array(
self.as_ptr().as_ref().unwrap_unchecked().array,
old_size,
new_size,
memcat,
)?);
for index in old_size..new_size {
array.add(index).value_unchecked().set_nil();
}
self.set_array(array);
self.as_ptr().as_mut().unwrap_unchecked().size_array = size;
}
Ok(())
}
unsafe fn set_node_vector(&self, thread: &Thread, size: i32) -> VmErrorResult {
unsafe {
let (node, lsize, count) = if size == 0 {
(Table::dummy_node_cursor(), 0, 0usize)
} else {
let lsize = ceil_log_2(size as u32);
if lsize > MAX_BITS {
return crate::run_error!(thread, "table overflow");
}
let count = twoto(lsize) as usize;
let node = LuaNodeCursor::from_ptr(thread.new_array::<RawLuaNode>(
count,
self.as_ptr().as_ref().unwrap_unchecked().memcat,
)?);
for index in 0..count {
let current_cursor = node.add(index);
let current_node = current_cursor.node_unchecked();
current_node.key().set_nil();
current_node.value_unchecked().set_nil();
}
(node, lsize, count)
};
self.set_node(node);
self.as_ptr().as_mut().unwrap_unchecked().lsize_node = lsize as u8;
self.as_ptr().as_mut().unwrap_unchecked().node_mask_8 = ((1 << lsize) - 1) as u8;
self.as_ptr().as_mut().unwrap_unchecked().free = RawLuaTableFree {
last_free: count as i32,
};
}
Ok(())
}
unsafe fn array_or_new_key(&self, thread: &Thread, key: TValue) -> VmErrorResult<TValue> {
unsafe {
if key.is_number() {
let Some(index) = Table::array_index(key.number_value()) else {
return self.new_key(thread, key);
};
if let Some(slot) = self.array_slot_for_key(index) {
return Ok(slot);
}
}
self.new_key(thread, key)
}
}
unsafe fn resize(&self, thread: &Thread, n_array: i32, n_hash: i32) -> VmErrorResult {
unsafe {
if n_array > MAX_SIZE || n_hash > MAX_SIZE {
return crate::run_error!(thread, "table overflow");
}
let old_array_size = self.as_ptr().as_ref().unwrap_unchecked().size_array;
let old_hash_lsize = self.as_ptr().as_ref().unwrap_unchecked().lsize_node as i32;
let old_nodes = self.node_cursor();
if n_array > old_array_size {
self.set_array_vector(thread, n_array)?;
}
self.set_node_vector(thread, n_hash)?;
let new_nodes = self.node_cursor();
if n_array < old_array_size {
self.as_ptr().as_mut().unwrap_unchecked().size_array = n_array;
for index in n_array..old_array_size {
let value = self.array_slot(index as usize);
if !value.is_nil() {
let mut key_storage = RawTValue::number((index + 1) as f64);
let key = TValue::from_mut(&mut key_storage);
self.array_or_new_key(thread, key)?.set_obj(value);
}
}
let array = TValueCursor::from_ptr(thread.realloc_array(
self.as_ptr().as_ref().unwrap_unchecked().array,
old_array_size as usize,
n_array as usize,
self.as_ptr().as_ref().unwrap_unchecked().memcat,
)?);
self.set_array(array);
}
let new_array = self.array_cursor();
for index in (0..twoto(old_hash_lsize)).rev() {
let old_cursor = old_nodes.add(index as usize);
let old_node = old_cursor.node_unchecked();
if !old_node.value_unchecked().is_nil() {
let mut key_storage = RawTValue::nil();
let key = TValue::from_mut(&mut key_storage);
old_node.write_key_to_value(key);
self.array_or_new_key(thread, key)?
.set_obj(old_node.value_unchecked());
}
}
debug_assert!(new_nodes == self.node_cursor());
debug_assert!(new_array == self.array_cursor());
if old_nodes != Table::dummy_node_cursor() {
thread.free_array(
old_nodes.as_ptr(),
twoto(old_hash_lsize) as usize,
self.as_ptr().as_ref().unwrap_unchecked().memcat,
);
}
}
Ok(())
}
unsafe fn adjust_array_size(&self, mut size: i32, extra_key: Option<TValue>) -> i32 {
let table_bound = unsafe {
!self.has_dummy_node() || size < self.as_ptr().as_ref().unwrap_unchecked().size_array
};
let extra_key_index = extra_key
.and_then(|key| {
if key.is_number() {
Table::array_index(key.number_value())
} else {
None
}
})
.unwrap_or(-1);
while size + 1 == extra_key_index
|| (table_bound && !unsafe { self.get_num(size + 1) }.is_nil())
{
size += 1;
}
size
}
unsafe fn rehash(&self, thread: &Thread, extra_key: TValue) -> VmErrorResult {
let mut nums = [0; (MAX_BITS + 1) as usize];
let mut n_array = self.num_use_array(&mut nums);
let mut total_use = n_array;
total_use += self.num_use_hash(&mut nums, &mut n_array);
if extra_key.is_number() {
n_array += count_int(extra_key.number_value(), &mut nums);
}
total_use += 1;
let na = compute_sizes(&nums, &mut n_array);
let mut n_hash = total_use - na;
unsafe {
let mut adjusted = self.adjust_array_size(n_array, Some(extra_key));
let extra_array = adjusted - n_array;
if extra_array != 0 {
n_hash -= extra_array;
n_array = adjusted + extra_array;
adjusted = self.adjust_array_size(n_array, Some(extra_key));
}
self.resize(thread, adjusted, n_hash)?;
}
Ok(())
}
unsafe fn free_position(&self) -> Option<LuaNodeCursor> {
let mut last_free = unsafe { self.as_ptr().as_ref().unwrap_unchecked().free.last_free };
while last_free > 0 {
last_free -= 1;
unsafe {
self.as_ptr().as_mut().unwrap_unchecked().free = RawLuaTableFree { last_free };
let node_cursor = self.node_cursor().add(last_free as usize);
if node_cursor.node_unchecked().key().is_nil() {
return Some(node_cursor);
}
}
}
None
}
unsafe fn new_hash_key(&self, thread: &Thread, key: TValue) -> VmErrorResult<TValue> {
unsafe {
let mut main_cursor = self.main_position(key);
if !main_cursor.node_unchecked().value_unchecked().is_nil()
|| main_cursor == Table::dummy_node_cursor()
{
let Some(free_cursor) = self.free_position() else {
self.rehash(thread, key)?;
return self.array_or_new_key(thread, key);
};
debug_assert!(free_cursor != Table::dummy_node_cursor());
let mut main_key_storage = RAW_TVALUE_NIL;
let main_key = TValue::from_mut(&mut main_key_storage);
main_cursor.node_unchecked().write_key_to_value(main_key);
let mut other_cursor = self.main_position(main_key);
if other_cursor != main_cursor {
let mut next_cursor =
other_cursor.offset(other_cursor.node_unchecked().next() as isize);
while next_cursor != main_cursor {
other_cursor =
other_cursor.offset(other_cursor.node_unchecked().next() as isize);
next_cursor =
other_cursor.offset(other_cursor.node_unchecked().next() as isize);
}
other_cursor
.node_unchecked()
.key()
.set_next(free_cursor.offset_from(other_cursor) as i32);
ptr::copy_nonoverlapping(main_cursor.as_ptr(), free_cursor.as_ptr(), 1);
let main_next = main_cursor.node_unchecked().next();
if main_next != 0 {
free_cursor.node_unchecked().key().set_next(
free_cursor.node_unchecked().next()
+ main_cursor.offset_from(free_cursor) as i32,
);
main_cursor.node_unchecked().key().set_next(0);
}
main_cursor.node_unchecked().value_unchecked().set_nil();
} else {
let main_next = main_cursor.node_unchecked().next();
if main_next != 0 {
free_cursor.node_unchecked().key().set_next(
main_cursor
.offset(main_next as isize)
.offset_from(free_cursor) as i32,
);
} else {
debug_assert!(free_cursor.node_unchecked().next() == 0);
}
main_cursor
.node_unchecked()
.key()
.set_next(free_cursor.offset_from(main_cursor) as i32);
main_cursor = free_cursor;
}
}
let main_node = main_cursor.node_unchecked();
main_node.set_key_from_value(key);
if key.is_collectable() {
let object = key.gc_value();
let table_object: GcObject = (*self).into();
if table_object.is_black() && object.is_white() {
thread.barrier_table(*self, object);
}
}
debug_assert!(main_node.value_unchecked().is_nil());
Ok(main_node.value())
}
}
unsafe fn new_key(&self, thread: &Thread, key: TValue) -> VmErrorResult<TValue> {
unsafe {
let size_array = self.as_ptr().as_ref().unwrap_unchecked().size_array;
if key.is_number() && key.number_value() == (size_array + 1) as f64 {
self.rehash(thread, key)?;
return self.array_or_new_key(thread, key);
}
self.new_hash_key(thread, key)
}
}
unsafe fn find_index(&self, thread: &Thread, key: TValue) -> VmErrorResult<i32> {
if key.is_nil() {
return Ok(-1);
}
unsafe {
if key.is_number()
&& matches!(
Table::array_index(key.number_value()),
Some(index) if index > 0 && index <= self.as_ptr().as_ref().unwrap_unchecked().size_array
)
{
return Ok(Table::array_index(key.number_value()).unwrap_unchecked() - 1);
}
let mut node_cursor = self.main_position(key);
loop {
let node_key = node_cursor.node_unchecked().key();
if node_key.raw_equal_value(key)
|| (node_key.is_dead_key()
&& key.is_collectable()
&& node_key.gc_value() == key.gc_value())
{
let index = self.node_index(node_cursor);
return Ok(index + self.as_ptr().as_ref().unwrap_unchecked().size_array);
}
let next = node_key.next();
if next == 0 {
break;
}
node_cursor = node_cursor.offset(next as isize);
}
crate::run_error!(thread, "invalid key to 'next'")
}
}
}
impl TableRuntime for Thread {
#[inline(always)]
unsafe fn set_num(&self, table: Table, key: i32) -> VmErrorResult<TValue> {
unsafe {
if let Some(slot) = table.array_slot_for_key(key) {
Ok(slot)
} else {
let slot = table.get_num(key);
if slot != nil_object() {
Ok(slot)
} else {
let mut value_storage = RawTValue::number(key as f64);
let value = TValue::from_mut(&mut value_storage);
table.new_key(self, value)
}
}
}
}
#[inline(always)]
unsafe fn set_str(&self, table: Table, key: TString) -> VmErrorResult<LuaNodeCursor> {
unsafe {
table.invalidate_tm_cache();
if let Some(node_cursor) = table.get_str_node(key) {
Ok(node_cursor)
} else {
let mut value_storage = RawTValue::string(key);
let value = TValue::from_mut(&mut value_storage);
table.new_hash_key(self, value)?;
Ok(table.get_str_node(key).unwrap_unchecked())
}
}
}
unsafe fn setp(&self, table: Table, key: *mut (), tag: i32) -> VmErrorResult<TValue> {
unsafe {
let slot = table.getp(key, tag);
if slot != nil_object() {
Ok(slot)
} else {
let mut value_storage = RawTValue::light_userdata(key, tag);
let value = TValue::from_mut(&mut value_storage);
table.new_key(self, value)
}
}
}
unsafe fn set(&self, table: Table, key: TValue) -> VmErrorResult<TValue> {
unsafe {
let slot = table.get(key);
self.set_slot(table, slot, key)
}
}
unsafe fn set_slot(&self, table: Table, slot: TValue, key: TValue) -> VmErrorResult<TValue> {
unsafe {
table.invalidate_tm_cache();
if slot != nil_object() {
Ok(slot)
} else {
self.new_key(table, key)
}
}
}
unsafe fn new_key(&self, table: Table, key: TValue) -> VmErrorResult<TValue> {
unsafe {
if key.is_nil() {
return crate::run_error!(self, "table index is nil");
} else if key.is_number() && key.number_value().is_nan() {
return crate::run_error!(self, "table index is NaN");
} else if key.is_vector() && crate::number::vec_is_nan(&key.vector_value()) {
return crate::run_error!(self, "table index contains NaN");
}
table.new_key(self, key)
}
}
unsafe fn next_internal(&self, table: Table, key: TValueCursor) -> VmErrorResult<i32> {
unsafe {
let mut index = table.find_index(self, key.value_unchecked())?;
index += 1;
while index < table.as_ptr().as_ref().unwrap_unchecked().size_array {
let value = table.array_slot(index as usize);
if !value.is_nil() {
key.value_unchecked().set_number((index + 1) as f64);
key.add(1).value_unchecked().set_obj(value);
return Ok(1);
}
index += 1;
}
index -= table.as_ptr().as_ref().unwrap_unchecked().size_array;
while index < table.node_count() as i32 {
let node = table.node(index);
if !node.value_unchecked().is_nil() {
node.write_key_to_value(key.value_unchecked());
key.add(1).value_unchecked().set_obj(node.value_unchecked());
return Ok(1);
}
index += 1;
}
Ok(0)
}
}
unsafe fn new_table_internal(&self, n_array: i32, n_hash: i32) -> VmErrorResult<Table> {
let table = unsafe {
let table = self.new_gco::<Table>(
mem::size_of::<RawLuaTable>(),
self.as_ptr().as_ref().unwrap_unchecked().active_memcat,
)?;
GcObject::from(table).init_header(self, LUA_TTABLE as u8);
let table_ref = table.as_ptr().as_mut().unwrap_unchecked();
table_ref.metatable = ptr::null_mut();
table_ref.tm_cache = !0;
table.init_empty_storage();
if n_array > 0 {
table.set_array_vector(self, n_array)?;
}
if n_hash > 0 {
table.set_node_vector(self, n_hash)?;
}
table
};
Ok(table)
}
unsafe fn resize_array(&self, table: Table, n_array: i32) -> VmErrorResult {
unsafe {
let n_hash = if table.has_dummy_node() {
0
} else {
table.node_count() as i32
};
let adjusted = table.adjust_array_size(n_array, None);
table.resize(self, adjusted, n_hash)
}
}
unsafe fn resize_hash(&self, table: Table, n_hash: i32) -> VmErrorResult {
unsafe {
table.resize(
self,
table.as_ptr().as_ref().unwrap_unchecked().size_array,
n_hash,
)
}
}
unsafe fn free_table(&self, table: Table, page: LuaPage) {
unsafe {
let table_ref = table.as_ptr().as_ref().unwrap_unchecked();
let memcat = table_ref.memcat;
table.free_storage(self);
self.free_gco(
table.into(),
core::mem::size_of::<RawLuaTable>(),
memcat,
page,
);
}
}
unsafe fn clone_table_internal(&self, table: Table) -> VmErrorResult<Table> {
unsafe {
let active_memcat = self.as_ptr().as_ref().unwrap_unchecked().active_memcat;
let cloned = self.new_gco::<Table>(mem::size_of::<RawLuaTable>(), active_memcat)?;
GcObject::from(cloned).init_header(self, LUA_TTABLE as u8);
let cloned_ref = cloned.as_ptr().as_mut().unwrap_unchecked();
cloned_ref.metatable = table
.metatable()
.map_or(ptr::null_mut(), |table| table.as_ptr());
cloned_ref.tm_cache = table.as_ptr().as_ref().unwrap_unchecked().tm_cache;
cloned.init_empty_storage();
let table_ref = table.as_ptr().as_ref().unwrap_unchecked();
let cloned_memcat = cloned.as_ptr().as_ref().unwrap_unchecked().memcat;
if table_ref.size_array > 0 {
let size_array = table_ref.size_array as usize;
let array =
TValueCursor::from_ptr(self.new_array::<RawTValue>(size_array, cloned_memcat)?);
cloned.set_array(array);
cloned.as_ptr().as_mut().unwrap_unchecked().size_array = table_ref.size_array;
cloned.maybe_set_aboundary(table.get_aboundary());
ptr::copy_nonoverlapping(table.array_cursor().as_ptr(), array.as_ptr(), size_array);
}
if !table.has_dummy_node() {
let size = table.node_count();
let node = self.new_array::<RawLuaNode>(size, cloned_memcat)?;
cloned.set_node(LuaNodeCursor::from_ptr(node));
cloned.as_ptr().as_mut().unwrap_unchecked().lsize_node = table_ref.lsize_node;
cloned.as_ptr().as_mut().unwrap_unchecked().node_mask_8 = table_ref.node_mask_8;
cloned.as_ptr().as_mut().unwrap_unchecked().free = RawLuaTableFree {
last_free: table_ref.free.last_free,
};
ptr::copy_nonoverlapping(table_ref.node, node, size);
}
Ok(cloned)
}
}
}
impl Table {
pub unsafe fn clear(&self) {
unsafe {
for index in 0..self.as_ptr().as_ref().unwrap_unchecked().size_array as usize {
self.array_slot(index).set_nil();
}
self.maybe_set_aboundary(0);
if !self.has_dummy_node() {
let size = self.node_count();
self.as_ptr().as_mut().unwrap_unchecked().free = RawLuaTableFree {
last_free: size as i32,
};
for index in 0..size {
let node = self.node(index as i32);
node.set_key_from_value(nil_object());
node.value_unchecked().set_nil();
node.key().set_next(0);
}
}
self.as_ptr().as_mut().unwrap_unchecked().tm_cache = !0;
}
}
}