use core::ptr::NonNull;
use crate::Table;
use crate::buffer::Buffer;
use crate::function::{Closure, Proto, UpVal};
use crate::handle::RawHandle;
use crate::state::ThreadState;
use crate::string::TString;
use crate::thread::Thread;
use crate::types::{
LUA_EXTRA_SIZE, LUA_TBOOLEAN, LUA_TBUFFER, LUA_TCLASS, LUA_TFUNCTION, LUA_TINTEGER,
LUA_TLIGHTUSERDATA, LUA_TNIL, LUA_TNUMBER, LUA_TOBJECT, LUA_TPROTO, LUA_TSTRING, LUA_TTABLE,
LUA_TTHREAD, LUA_TUPVALUE, LUA_TUSERDATA, LUA_TVECTOR, LUA_VECTOR_SIZE,
};
use crate::userdata::Userdata;
use crate::{Class, Object};
use crate::gc::{GcObject, RawGcObject};
#[derive(Clone, Copy)]
#[repr(C)]
pub union RawValue {
pub gc: *mut RawGcObject,
pub pointer: *mut (),
pub number: f64,
pub boolean: i32,
pub integer: i64,
pub vector: [f32; 2],
}
#[derive(Clone, Copy)]
#[repr(C)]
pub struct RawTValue {
pub value: RawValue,
pub extra: [i32; LUA_EXTRA_SIZE],
pub tt: i32,
}
impl RawTValue {
pub const fn nil() -> Self {
Self {
value: RawValue {
pointer: core::ptr::null_mut(),
},
extra: [0; LUA_EXTRA_SIZE],
tt: LUA_TNIL,
}
}
pub const fn number(value: f64) -> Self {
Self {
value: RawValue { number: value },
extra: [0; LUA_EXTRA_SIZE],
tt: LUA_TNUMBER,
}
}
pub fn string(value: TString) -> Self {
Self {
value: RawValue {
gc: GcObject::from(value).as_ptr(),
},
extra: [0; LUA_EXTRA_SIZE],
tt: LUA_TSTRING,
}
}
pub const fn light_userdata(pointer: *mut (), tag: i32) -> Self {
let mut extra = [0; LUA_EXTRA_SIZE];
extra[0] = tag;
Self {
value: RawValue { pointer },
extra,
tt: LUA_TLIGHTUSERDATA,
}
}
}
pub const RAW_TVALUE_NIL: RawTValue = RawTValue::nil();
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct TValueCursor(*mut RawTValue);
#[allow(
clippy::missing_safety_doc,
reason = "TValueCursor navigation is governed by its documented non-owning cursor contract"
)]
impl TValueCursor {
pub const fn as_ptr(&self) -> *mut RawTValue {
self.0
}
pub const fn from_ptr(raw: *mut RawTValue) -> Self {
Self(raw)
}
pub const fn is_null(&self) -> bool {
self.0.is_null()
}
pub unsafe fn is_nil_unchecked(&self) -> bool {
debug_assert!(!self.is_null());
unsafe { (*self.as_ptr()).tt == LUA_TNIL }
}
pub unsafe fn value_unchecked(&self) -> TValue {
debug_assert!(!self.is_null());
unsafe { TValue::from_raw(NonNull::new_unchecked(self.0)) }
}
pub fn value(&self) -> Option<TValue> {
NonNull::new(self.0).map(|raw| unsafe { TValue::from_raw(raw) })
}
pub unsafe fn add(self, count: usize) -> Self {
unsafe { Self::from_ptr(self.0.add(count)) }
}
pub unsafe fn sub(self, count: usize) -> Self {
unsafe { Self::from_ptr(self.0.sub(count)) }
}
pub unsafe fn offset(self, count: isize) -> Self {
unsafe { Self::from_ptr(self.0.offset(count)) }
}
pub unsafe fn offset_from(self, other: Self) -> isize {
unsafe { self.0.offset_from(other.0) }
}
pub fn addr_offset_from(self, other: Self) -> isize {
let byte_offset = self.0 as isize - other.0 as isize;
debug_assert_eq!(byte_offset % core::mem::size_of::<RawTValue>() as isize, 0);
byte_offset / core::mem::size_of::<RawTValue>() as isize
}
}
impl AsRef<TValueCursor> for TValueCursor {
fn as_ref(&self) -> &TValueCursor {
self
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct TValue {
raw: NonNull<RawTValue>,
}
#[repr(transparent)]
pub struct NilObject(RawTValue);
unsafe impl Sync for NilObject {}
pub static LUA_O_NIL_OBJECT: NilObject = NilObject(RAW_TVALUE_NIL);
#[allow(
clippy::missing_safety_doc,
reason = "TValue's shared raw-view contract is documented on TValue"
)]
impl TValue {
pub const unsafe fn from_raw(raw: NonNull<RawTValue>) -> Self {
Self { raw }
}
pub unsafe fn from_ref(raw: &RawTValue) -> Self {
Self {
raw: NonNull::from(raw),
}
}
pub unsafe fn from_mut(raw: &mut RawTValue) -> Self {
Self {
raw: NonNull::from(raw),
}
}
pub fn is_nil(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TNIL }
}
pub fn is_number(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TNUMBER }
}
pub fn is_integer(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TINTEGER }
}
pub fn is_string(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TSTRING }
}
pub fn is_table(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TTABLE }
}
pub fn is_function(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TFUNCTION }
}
pub fn is_native_function(&self) -> bool {
if !self.is_function() {
return false;
}
let closure = self.closure_value();
unsafe { closure.is_native() }
}
pub fn is_lua_function(&self) -> bool {
if !self.is_function() {
return false;
}
let closure = self.closure_value();
unsafe { closure.is_lua() }
}
pub fn is_boolean(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TBOOLEAN }
}
pub fn is_userdata(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TUSERDATA }
}
pub fn is_thread(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TTHREAD }
}
pub fn is_buffer(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TBUFFER }
}
pub fn is_light_userdata(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TLIGHTUSERDATA }
}
pub fn is_vector(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TVECTOR }
}
pub fn is_class(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TCLASS }
}
pub fn is_object(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TOBJECT }
}
pub fn is_upvalue(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TUPVALUE }
}
pub fn is_proto(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt == LUA_TPROTO }
}
pub fn is_collectable(&self) -> bool {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt >= LUA_TSTRING }
}
pub fn tt(&self) -> i32 {
unsafe { self.as_ptr().as_ref().unwrap_unchecked().tt }
}
pub fn is_false(&self) -> bool {
let value = unsafe { self.as_ptr().as_ref().unwrap_unchecked() };
value.tt == LUA_TNIL || (value.tt == LUA_TBOOLEAN && unsafe { value.value.boolean } == 0)
}
pub fn gc_value(&self) -> GcObject {
debug_assert!(self.is_collectable());
unsafe {
GcObject::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc,
))
}
}
pub fn string_value(&self) -> TString {
debug_assert!(self.is_string());
unsafe {
TString::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn table_value(&self) -> Table {
debug_assert!(self.is_table());
unsafe {
Table::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn userdata_value(&self) -> Userdata {
debug_assert!(self.is_userdata());
unsafe {
Userdata::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn closure_value(&self) -> Closure {
debug_assert!(self.is_function());
unsafe {
Closure::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn pointer_value(&self) -> *mut () {
debug_assert!(self.is_light_userdata());
unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.pointer }
}
pub fn number_value(&self) -> f64 {
debug_assert!(self.is_number());
unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.number }
}
pub fn integer_value(&self) -> i64 {
debug_assert!(self.is_integer());
unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.integer }
}
pub fn boolean_value(&self) -> i32 {
debug_assert!(self.is_boolean());
unsafe { self.as_ptr().as_ref().unwrap_unchecked().value.boolean }
}
pub fn thread_value(&self) -> Thread {
debug_assert!(self.is_thread());
unsafe {
Thread::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn buffer_value(&self) -> Buffer {
debug_assert!(self.is_buffer());
unsafe {
Buffer::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn upvalue_value(&self) -> UpVal {
debug_assert!(self.is_upvalue());
unsafe {
UpVal::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn class_value(&self) -> Class {
debug_assert!(self.is_class());
unsafe {
Class::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn object_value(&self) -> Object {
debug_assert!(self.is_object());
unsafe {
Object::from_raw(NonNull::new_unchecked(
self.as_ptr().as_ref().unwrap_unchecked().value.gc.cast(),
))
}
}
pub fn proto_value(&self) -> Proto {
debug_assert!(self.is_proto());
unsafe { self.gc_value().to_proto() }
}
pub fn light_userdata_tag(&self) -> i32 {
debug_assert!(self.is_light_userdata());
unsafe { self.as_ptr().as_ref().unwrap_unchecked().extra[0] }
}
pub fn vector_value(&self) -> [f32; LUA_VECTOR_SIZE] {
debug_assert!(self.is_vector());
let vector = self.as_ptr().cast::<f32>();
#[cfg(not(feature = "vector4"))]
unsafe {
[*vector, *vector.add(1), *vector.add(2)]
}
#[cfg(feature = "vector4")]
unsafe {
[*vector, *vector.add(1), *vector.add(2), *vector.add(3)]
}
}
pub fn set_nil(&self) {
unsafe {
self.as_ptr().as_mut().unwrap_unchecked().tt = LUA_TNIL;
}
}
pub fn set_number(&self, value: f64) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue { number: value };
raw.tt = LUA_TNUMBER;
}
}
pub fn set_integer(&self, value: i64) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue { integer: value };
raw.tt = LUA_TINTEGER;
}
}
pub fn set_boolean(&self, value: i32) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue { boolean: value };
raw.tt = LUA_TBOOLEAN;
}
}
pub fn set_light_userdata(&self, pointer: *mut (), tag: i32) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue { pointer };
raw.extra[0] = tag;
raw.tt = LUA_TLIGHTUSERDATA;
}
}
pub fn set_string_value(&self, value: TString) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TSTRING;
}
}
pub fn set_vector(&self, value: [f32; LUA_VECTOR_SIZE]) {
let vector = self.as_ptr().cast::<f32>();
unsafe {
*vector = value[0];
*vector.add(1) = value[1];
*vector.add(2) = value[2];
}
#[cfg(feature = "vector4")]
unsafe {
*vector.add(3) = value[3];
}
unsafe {
(*self.as_ptr()).tt = LUA_TVECTOR;
}
}
pub fn set_userdata_value(&self, value: Userdata) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TUSERDATA;
}
}
pub fn set_thread_value(&self, value: &Thread) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TTHREAD;
}
}
pub fn set_buffer_value(&self, value: Buffer) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TBUFFER;
}
}
pub fn set_closure_value(&self, value: Closure) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TFUNCTION;
}
}
pub fn set_table_value(&self, value: Table) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TTABLE;
}
}
pub fn set_proto_value(&self, value: Proto) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TPROTO;
}
}
pub fn set_upvalue_value(&self, value: UpVal) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TUPVALUE;
}
}
pub fn set_class_value(&self, value: Class) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TCLASS;
}
}
pub fn set_object_value(&self, value: Object) {
unsafe {
let raw = self.as_ptr().as_mut().unwrap_unchecked();
raw.value = RawValue {
gc: GcObject::from(value).as_ptr(),
};
raw.tt = LUA_TOBJECT;
}
}
pub fn set_obj(&self, other: impl Into<TValue>) {
let other = other.into();
unsafe {
core::ptr::copy(other.as_ptr(), self.as_ptr(), 1);
}
}
pub fn raw_equal(&self, other: impl Into<TValue>) -> bool {
let other = other.into();
if self.tt() != other.tt() {
return false;
}
match self.tt() {
x if x == LUA_TNIL => true,
x if x == LUA_TNUMBER => self.number_value() == other.number_value(),
x if x == LUA_TINTEGER => self.integer_value() == other.integer_value(),
x if x == LUA_TVECTOR => self.vector_value() == other.vector_value(),
x if x == LUA_TBOOLEAN => self.boolean_value() == other.boolean_value(),
x if x == LUA_TLIGHTUSERDATA => {
self.pointer_value() == other.pointer_value()
&& self.light_userdata_tag() == other.light_userdata_tag()
}
_ => {
debug_assert!(self.is_collectable());
self.gc_value() == other.gc_value()
}
}
}
}
pub fn nil_object() -> TValue {
unsafe { TValue::from_ref(&LUA_O_NIL_OBJECT.0) }
}
impl crate::handle::sealed::Sealed for TValue {}
impl RawHandle for TValue {
type Raw = RawTValue;
fn as_ptr(&self) -> *mut Self::Raw {
self.raw.as_ptr()
}
}
impl AsRef<TValue> for TValue {
fn as_ref(&self) -> &TValue {
self
}
}