#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum FrameKind {
Lua = 0,
Cont = 1,
}
impl FrameKind {
#[inline]
pub fn from_tag(tag: u8) -> Option<Self> {
match tag {
0 => Some(FrameKind::Lua),
1 => Some(FrameKind::Cont),
_ => None,
}
}
#[inline]
pub fn tag(self) -> u8 {
self as u8
}
}
const FRAME_KIND_BITS: u32 = 3;
const FRAME_KIND_MASK: u64 = (1 << FRAME_KIND_BITS) - 1;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct FrameMarker(u64);
impl FrameMarker {
#[inline]
pub fn new_lua(pc: u32) -> Self {
let payload = (pc as u64) << FRAME_KIND_BITS;
FrameMarker(payload | FrameKind::Lua.tag() as u64)
}
#[inline]
pub fn new_cont(delta: u32) -> Self {
let payload = (delta as u64) << FRAME_KIND_BITS;
FrameMarker(payload | FrameKind::Cont.tag() as u64)
}
#[inline]
pub fn kind(self) -> Option<FrameKind> {
FrameKind::from_tag((self.0 & FRAME_KIND_MASK) as u8)
}
#[inline]
pub fn payload(self) -> u32 {
(self.0 >> FRAME_KIND_BITS) as u32
}
#[inline]
pub fn to_raw(self) -> i64 {
self.0 as i64
}
#[inline]
pub fn from_raw(raw: i64) -> Self {
FrameMarker(raw as u64)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lua_marker_roundtrip_basic() {
let m = FrameMarker::new_lua(42);
assert_eq!(m.kind(), Some(FrameKind::Lua));
assert_eq!(m.payload(), 42);
}
#[test]
fn cont_marker_roundtrip_basic() {
let m = FrameMarker::new_cont(7);
assert_eq!(m.kind(), Some(FrameKind::Cont));
assert_eq!(m.payload(), 7);
}
#[test]
fn raw_bits_roundtrip_through_i64() {
for pc in [0u32, 1, 100, u16::MAX as u32, u32::MAX] {
let m = FrameMarker::new_lua(pc);
let raw = m.to_raw();
let m2 = FrameMarker::from_raw(raw);
assert_eq!(m2.kind(), Some(FrameKind::Lua), "kind survives pc={}", pc);
assert_eq!(m2.payload(), pc, "payload survives pc={}", pc);
}
}
#[test]
fn cont_payload_survives_full_u32() {
for delta in [0u32, 1, 100, u32::MAX] {
let m = FrameMarker::new_cont(delta);
let m2 = FrameMarker::from_raw(m.to_raw());
assert_eq!(m2.kind(), Some(FrameKind::Cont));
assert_eq!(m2.payload(), delta);
}
}
#[test]
fn kind_tag_values_match_lj() {
assert_eq!(FrameKind::Lua.tag(), 0);
assert_eq!(FrameKind::Cont.tag(), 1);
assert_eq!(FrameKind::from_tag(0), Some(FrameKind::Lua));
assert_eq!(FrameKind::from_tag(1), Some(FrameKind::Cont));
assert_eq!(FrameKind::from_tag(2), None, "FRAME_CONT_LJ not emitted yet");
assert_eq!(FrameKind::from_tag(7), None, "invalid tag rejected");
}
#[test]
fn kind_decode_safe_on_invalid() {
let garbage = FrameMarker::from_raw(0x_8000_0000_0000_0007u64 as i64);
assert_eq!(garbage.kind(), None);
}
}