use crate::value::LuaValue;
use crate::StackIdx;
use std::cell::Cell;
#[derive(Debug, Clone, Copy)]
enum UpValState {
Open { thread_id: u64, idx: u32 },
Closed(LuaValue),
}
#[derive(Debug)]
pub struct UpVal {
state: Cell<UpValState>,
}
#[cfg(target_pointer_width = "64")]
const _: () = assert!(std::mem::size_of::<UpVal>() == 24);
#[cfg(target_pointer_width = "64")]
const _: () = assert!(std::mem::size_of::<lua_gc::GcBox<UpVal>>() == 48);
impl UpVal {
pub fn open(thread_id: u64, idx: StackIdx) -> Self {
UpVal {
state: Cell::new(UpValState::Open {
thread_id,
idx: idx.0,
}),
}
}
pub fn closed(v: LuaValue) -> Self {
UpVal {
state: Cell::new(UpValState::Closed(v)),
}
}
pub fn is_open(&self) -> bool {
matches!(self.state.get(), UpValState::Open { .. })
}
pub fn is_closed(&self) -> bool {
matches!(self.state.get(), UpValState::Closed(_))
}
#[inline(always)]
pub fn try_open_payload(&self) -> Option<(u64, StackIdx)> {
match self.state.get() {
UpValState::Open { thread_id, idx } => Some((thread_id, StackIdx(idx))),
UpValState::Closed(_) => None,
}
}
#[inline(always)]
pub fn closed_value(&self) -> LuaValue {
match self.state.get() {
UpValState::Closed(v) => v,
UpValState::Open { .. } => LuaValue::Nil,
}
}
pub fn close_with(&self, v: LuaValue) {
self.state.set(UpValState::Closed(v));
}
pub fn set_closed_value(&self, v: LuaValue) {
self.state.set(UpValState::Closed(v));
}
pub fn try_closed_value(&self) -> Option<LuaValue> {
match self.state.get() {
UpValState::Closed(v) => Some(v),
UpValState::Open { .. } => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn closed_scalar_write_updates_canonical_value() {
let uv = UpVal::closed(LuaValue::Int(1));
uv.set_closed_value(LuaValue::Int(2));
assert_eq!(uv.closed_value(), LuaValue::Int(2));
assert_eq!(uv.try_closed_value(), Some(LuaValue::Int(2)));
assert!(uv.is_closed());
assert_eq!(uv.try_open_payload(), None);
}
#[test]
fn close_with_sets_cell_closed_state() {
let uv = UpVal::open(7, StackIdx(3));
assert_eq!(uv.try_open_payload(), Some((7, StackIdx(3))));
uv.close_with(LuaValue::Bool(true));
assert_eq!(uv.closed_value(), LuaValue::Bool(true));
assert_eq!(uv.try_closed_value(), Some(LuaValue::Bool(true)));
assert!(uv.is_closed());
assert_eq!(uv.try_open_payload(), None);
}
#[test]
fn open_upvalue_preserves_full_u64_thread_id() {
const BIG_TID: u64 = 0xFEDC_BA98_7654_3210;
assert!(BIG_TID > i64::MAX as u64);
let uv = UpVal::open(BIG_TID, StackIdx(9));
assert_eq!(uv.try_open_payload(), Some((BIG_TID, StackIdx(9))));
assert!(uv.is_open());
assert_eq!(uv.try_closed_value(), None);
}
#[test]
fn open_upvalue_closed_value_reports_nil() {
let uv = UpVal::open(3, StackIdx(1));
assert!(uv.is_open());
assert_eq!(uv.closed_value(), LuaValue::Nil);
}
}