#![feature(offset_of_enum)]
use nearest::{
Flat, Near, NearList, Ref, Region, ValidateError, array, empty, list, maybe, near, none,
};
#[derive(Flat, Copy, Clone, Debug, PartialEq, Eq)]
struct Type(u8);
#[derive(Flat, Copy, Clone, Debug, PartialEq, Eq)]
struct Symbol(u32);
#[derive(Flat, Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C, u8)]
enum Value {
Const(u32),
Type(Type),
}
#[derive(Flat, Debug)]
struct Inst {
op: u16,
typ: Type,
args: NearList<Value>,
}
#[derive(Flat, Debug)]
struct Jmp {
args: NearList<Value>,
target: Near<Block>,
}
#[derive(Flat, Debug)]
#[repr(C, u8)]
#[expect(dead_code, reason = "variants constructed via raw byte emission, not Rust constructors")]
enum Term {
Ret { values: NearList<Value> },
Jmp(Jmp),
}
#[derive(Flat, Debug)]
struct Block {
name: Symbol,
params: NearList<(Symbol, Type)>,
insts: NearList<Inst>,
term: Term,
}
#[derive(Flat, Debug)]
struct Func {
name: Symbol,
entry: Near<Block>,
}
#[derive(Flat, Debug)]
struct Signature<CT: Flat> {
params: NearList<Type>,
returns: NearList<Type>,
custom: NearList<CT>,
}
#[derive(Flat, Debug)]
#[repr(C, u8)]
#[expect(dead_code, reason = "variants constructed via emitters")]
enum Wrapper<CT: Flat> {
Empty,
WithSig { sig: Near<Signature<CT>> },
Pair(CT, u32),
}
#[derive(Flat, Debug)]
struct PrimArray {
tag: u32,
values: [u32; 3],
}
#[derive(Flat, Debug)]
struct Brif {
cond: u32,
targets: [Near<Block>; 2],
}
fn build_simple_func(name: u32) -> Region<Func> {
Region::new(Func::make(
Symbol(name),
near(Block::make(
Symbol(0),
empty(),
list([Inst::make(1, Type(0), list([Value::Const(42)]))]),
Term::make_jmp(Jmp::make(
list([Value::Const(1)]),
near(Block::make(
Symbol(0),
empty(),
list([Inst::make(1, Type(0), list([Value::Const(42)]))]),
Term::make_ret(list([Value::Const(42)])),
)),
)),
)),
))
}
#[test]
fn emit_build_and_traverse() {
let region = build_simple_func(1);
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(1));
let block = func.entry.get();
assert_eq!(block.name, Symbol(0));
assert!(block.params.is_empty());
assert_eq!(block.insts.len(), 1);
let inst = &block.insts[0];
assert_eq!(inst.op, 1);
assert_eq!(inst.typ, Type(0));
assert_eq!(inst.args.len(), 1);
assert_eq!(inst.args[0], Value::Const(42));
match &block.term {
Term::Jmp(jmp) => {
assert_eq!(jmp.args.len(), 1);
assert_eq!(jmp.args[0], Value::Const(1));
let exit = jmp.target.get();
match &exit.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(42));
}
Term::Jmp(_) => panic!("expected Ret in exit block"),
}
}
Term::Ret { .. } => panic!("expected Jmp, got Ret"),
}
}
#[test]
fn emit_multi_block() {
let region: Region<Func> = Region::new(Func::make(
Symbol(10),
near(Block::make(
Symbol(0),
empty(),
empty(),
Term::make_jmp(Jmp::make(
empty(),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(list([Value::Const(99)])))),
)),
)),
));
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(10));
let entry = func.entry.get();
assert_eq!(entry.name, Symbol(0));
assert!(entry.insts.is_empty());
match &entry.term {
Term::Jmp(jmp) => {
let exit = jmp.target.get();
assert_eq!(exit.name, Symbol(1));
assert!(jmp.args.is_empty());
match &exit.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(99));
}
Term::Jmp(_) => panic!("exit block should have Ret"),
}
}
Term::Ret { .. } => panic!("entry block should have Jmp"),
}
}
#[test]
fn clone_preserves_offsets() {
let region = build_simple_func(2);
let cloned = region.clone();
let func1: &Func = ®ion;
let func2: &Func = &cloned;
assert_eq!(func1.name, func2.name);
let block1 = func1.entry.get();
let block2 = func2.entry.get();
assert_eq!(block1.name, block2.name);
assert_eq!(block1.insts.len(), block2.insts.len());
assert_eq!(block1.insts[0].op, block2.insts[0].op);
assert_eq!(block1.insts[0].args[0], block2.insts[0].args[0]);
drop(cloned);
assert_eq!(func1.name, Symbol(2));
}
#[test]
fn near_list_iteration() {
let region = build_simple_func(4);
let func: &Func = ®ion;
let block = func.entry.get();
let args: Vec<&Value> = block.insts[0].args.iter().collect();
assert_eq!(args.len(), 1);
assert_eq!(*args[0], Value::Const(42));
let mut count = 0;
for val in &block.insts[0].args {
assert_eq!(*val, Value::Const(42));
count += 1;
}
assert_eq!(count, 1);
assert_eq!(block.insts[0].args.len(), 1);
assert_eq!(block.insts[0].args[0], Value::Const(42));
}
#[test]
fn region_debug() {
let region = build_simple_func(42);
let debug_str = format!("{region:?}");
assert!(debug_str.contains("Region"));
}
#[test]
fn empty_slices() {
let region: Region<Func> = Region::new(Func::make(
Symbol(99),
near(Block::make(Symbol(0), empty(), empty(), Term::make_ret(empty()))),
));
let func: &Func = ®ion;
let block = func.entry.get();
assert!(block.params.is_empty());
assert!(block.insts.is_empty());
match &block.term {
Term::Ret { values } => assert!(values.is_empty()),
Term::Jmp(_) => panic!("expected Ret"),
}
}
#[test]
fn multiple_instructions() {
let region: Region<Func> = Region::new(Func::make(
Symbol(5),
near(Block::make(
Symbol(0),
list([(Symbol(1), Type(0)), (Symbol(2), Type(1))]),
list([
Inst::make(1, Type(0), list([Value::Const(10), Value::Const(11)])),
Inst::make(2, Type(1), list([Value::Const(20), Value::Type(Type(0))])),
Inst::make(3, Type(0), list([Value::Const(30), Value::Const(31)])),
]),
Term::make_ret(list([Value::Const(10), Value::Const(20)])),
)),
));
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(5));
let block = func.entry.get();
assert_eq!(block.params.len(), 2);
assert_eq!(block.params[0], (Symbol(1), Type(0)));
assert_eq!(block.params[1], (Symbol(2), Type(1)));
assert_eq!(block.insts.len(), 3);
assert_eq!(block.insts[0].op, 1);
assert_eq!(block.insts[0].args.len(), 2);
assert_eq!(block.insts[0].args[0], Value::Const(10));
assert_eq!(block.insts[1].op, 2);
assert_eq!(block.insts[1].args.len(), 2);
assert_eq!(block.insts[1].args[1], Value::Type(Type(0)));
assert_eq!(block.insts[2].op, 3);
assert_eq!(block.insts[2].args.len(), 2);
}
#[test]
fn session_roundtrip() {
let mut region = build_simple_func(7);
region.session(|_s| {
});
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(7));
let block = func.entry.get();
assert_eq!(block.insts[0].op, 1);
assert_eq!(block.insts[0].args[0], Value::Const(42));
}
#[test]
fn session_splice_near_replaces_jmp_target() {
let mut region = build_simple_func(1);
region.session(|s| {
let jmp_target = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.splice(
jmp_target,
Block::make(
Symbol(99),
empty(),
list([Inst::make(7, Type(1), list([Value::Const(77)]))]),
Term::make_ret(list([Value::Const(99)])),
),
);
});
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(1));
let entry = func.entry.get();
match &entry.term {
Term::Jmp(jmp) => {
assert_eq!(jmp.args.len(), 1);
assert_eq!(jmp.args[0], Value::Const(1));
let exit = jmp.target.get();
assert_eq!(exit.name, Symbol(99));
assert_eq!(exit.insts.len(), 1);
assert_eq!(exit.insts[0].op, 7);
assert_eq!(exit.insts[0].typ, Type(1));
assert_eq!(exit.insts[0].args[0], Value::Const(77));
match &exit.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(99));
}
Term::Jmp(_) => panic!("expected Ret in new exit block"),
}
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn session_splice_list_replaces_insts() {
let mut region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
empty(),
list([Inst::make(1, Type(0), list([Value::Const(42)]))]),
Term::make_ret(list([Value::Const(42)])),
)),
));
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.entry.insts);
s.splice_list(
insts,
[
Inst::make(10, Type(0), list(vec![Value::Const(1)])),
Inst::make(20, Type(1), list(vec![Value::Const(2), Value::Const(3)])),
Inst::make(30, Type(0), list(vec![])),
],
);
});
let func: &Func = ®ion;
let block = func.entry.get();
assert_eq!(block.insts.len(), 3);
assert_eq!(block.insts[0].op, 10);
assert_eq!(block.insts[0].args.len(), 1);
assert_eq!(block.insts[0].args[0], Value::Const(1));
assert_eq!(block.insts[1].op, 20);
assert_eq!(block.insts[1].args.len(), 2);
assert_eq!(block.insts[1].args[0], Value::Const(2));
assert_eq!(block.insts[1].args[1], Value::Const(3));
assert_eq!(block.insts[2].op, 30);
assert!(block.insts[2].args.is_empty());
match &block.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(42));
}
Term::Jmp(_) => panic!("expected Ret"),
}
}
#[test]
fn session_splice_list_to_empty() {
let mut region = build_simple_func(1);
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.entry.insts);
s.splice_list(insts, core::iter::empty::<Inst>());
});
let func: &Func = ®ion;
assert!(func.entry.get().insts.is_empty());
}
#[test]
fn session_splice_list_params() {
let mut region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
list([(Symbol(1), Type(0)), (Symbol(2), Type(1))]),
empty(),
Term::make_ret(empty()),
)),
));
region.session(|s| {
let params = s.nav(s.root(), |f| &f.entry.params);
s.splice_list(params, [(Symbol(10), Type(0)), (Symbol(11), Type(1)), (Symbol(12), Type(2))]);
});
let func: &Func = ®ion;
let block = func.entry.get();
assert_eq!(block.params.len(), 3);
assert_eq!(block.params[0], (Symbol(10), Type(0)));
assert_eq!(block.params[1], (Symbol(11), Type(1)));
assert_eq!(block.params[2], (Symbol(12), Type(2)));
}
#[test]
fn session_inline_callee_block() {
let mut region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
empty(),
empty(),
Term::make_jmp(Jmp::make(
empty(),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(list([Value::Const(0)])))),
)),
)),
));
region.session(|s| {
let jmp_target = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.splice(
jmp_target,
Block::make(
Symbol(100),
list([(Symbol(10), Type(0))]),
list([
Inst::make(1, Type(0), list(vec![Value::Const(10)])),
Inst::make(2, Type(0), list(vec![Value::Const(20), Value::Type(Type(1))])),
]),
Term::make_ret(list([Value::Const(42)])),
),
);
});
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(1));
let entry = func.entry.get();
assert_eq!(entry.name, Symbol(0));
assert!(entry.insts.is_empty());
match &entry.term {
Term::Jmp(jmp) => {
let inlined = jmp.target.get();
assert_eq!(inlined.name, Symbol(100));
assert_eq!(inlined.params.len(), 1);
assert_eq!(inlined.params[0], (Symbol(10), Type(0)));
assert_eq!(inlined.insts.len(), 2);
assert_eq!(inlined.insts[0].op, 1);
assert_eq!(inlined.insts[0].args[0], Value::Const(10));
assert_eq!(inlined.insts[1].op, 2);
assert_eq!(inlined.insts[1].args.len(), 2);
assert_eq!(inlined.insts[1].args[0], Value::Const(20));
assert_eq!(inlined.insts[1].args[1], Value::Type(Type(1)));
match &inlined.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(42));
}
Term::Jmp(_) => panic!("expected Ret in inlined block"),
}
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn session_splice_then_set() {
let mut region = build_simple_func(1);
region.session(|s| {
let jmp_target = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.splice(
jmp_target,
Block::make(Symbol(50), empty(), empty(), Term::make_ret(list([Value::Const(0)]))),
);
});
region.session(|s| {
let name = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target.name,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.set(name, Symbol(77));
});
let func: &Func = ®ion;
match &func.entry.get().term {
Term::Jmp(jmp) => {
assert_eq!(jmp.target.get().name, Symbol(77));
match &jmp.target.get().term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(0));
}
Term::Jmp(_) => panic!("expected Ret"),
}
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn cursor_chain_set() {
let mut region = build_simple_func(1);
region.session(|s| {
s.cursor().at(|f| &f.name).set(Symbol(42));
});
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(42));
}
#[test]
fn cursor_chain_splice() {
let mut region = build_simple_func(1);
region.session(|s| {
s.cursor()
.at(|f| &f.entry)
.follow()
.at(|b| match &b.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
})
.splice(Block::make(Symbol(99), empty(), empty(), Term::make_ret(list([Value::Const(99)]))));
});
let func: &Func = ®ion;
let entry = func.entry.get();
match &entry.term {
Term::Jmp(jmp) => {
assert_eq!(jmp.target.get().name, Symbol(99));
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn generic_struct_empty_custom() {
let region: Region<Signature<u8>> =
Region::new(Signature::<u8>::make(list([Type(1), Type(2)]), list([Type(3)]), empty()));
let sig: &Signature<u8> = ®ion;
assert_eq!(sig.params.len(), 2);
assert_eq!(sig.params[0], Type(1));
assert_eq!(sig.params[1], Type(2));
assert_eq!(sig.returns.len(), 1);
assert_eq!(sig.returns[0], Type(3));
assert!(sig.custom.is_empty());
}
#[test]
fn generic_struct_concrete_param() {
let region: Region<Signature<u32>> =
Region::new(Signature::<u32>::make(list([Type(0)]), empty(), list([10u32, 20, 30])));
let sig: &Signature<u32> = ®ion;
assert_eq!(sig.params.len(), 1);
assert_eq!(sig.params[0], Type(0));
assert!(sig.returns.is_empty());
assert_eq!(sig.custom.len(), 3);
assert_eq!(sig.custom[0], 10);
assert_eq!(sig.custom[1], 20);
assert_eq!(sig.custom[2], 30);
}
#[test]
fn generic_enum_unit_variant() {
let region: Region<Wrapper<u8>> = Region::new(Wrapper::<u8>::make_empty());
let w: &Wrapper<u8> = ®ion;
assert!(matches!(w, Wrapper::Empty));
}
#[test]
fn generic_enum_named_variant() {
let region: Region<Wrapper<u8>> = Region::new(Wrapper::<u8>::make_with_sig(near(
Signature::<u8>::make(list([Type(1)]), list([Type(2)]), empty()),
)));
let w: &Wrapper<u8> = ®ion;
match w {
Wrapper::WithSig { sig } => {
let s = sig.get();
assert_eq!(s.params.len(), 1);
assert_eq!(s.params[0], Type(1));
assert_eq!(s.returns.len(), 1);
assert_eq!(s.returns[0], Type(2));
}
_ => panic!("expected WithSig"),
}
}
#[test]
fn generic_enum_unnamed_variant() {
let region: Region<Wrapper<u32>> = Region::new(Wrapper::<u32>::make_pair(42u32, 7));
let w: &Wrapper<u32> = ®ion;
match w {
Wrapper::Pair(a, b) => {
assert_eq!(*a, 42);
assert_eq!(*b, 7);
}
_ => panic!("expected Pair"),
}
}
#[test]
fn generic_struct_clone() {
let region: Region<Signature<u32>> =
Region::new(Signature::<u32>::make(list([Type(5)]), list([Type(6), Type(7)]), list([100u32])));
let cloned = region.clone();
let s1: &Signature<u32> = ®ion;
let s2: &Signature<u32> = &cloned;
assert_eq!(s1.params[0], s2.params[0]);
assert_eq!(s1.returns.len(), s2.returns.len());
assert_eq!(s1.custom[0], s2.custom[0]);
}
#[test]
fn trim_after_splice_shrinks_region() {
let mut region = build_simple_func(1);
let before = region.byte_len();
region.session(|s| {
let jmp_target = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.splice(
jmp_target,
Block::make(Symbol(50), empty(), empty(), Term::make_ret(list([Value::Const(0)]))),
);
});
let after_splice = region.byte_len();
assert!(after_splice > before, "splice should grow the buffer");
region.trim();
let after_trim = region.byte_len();
assert!(after_trim < after_splice, "trim should shrink: {after_trim} < {after_splice}");
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(1));
let entry = func.entry.get();
assert_eq!(entry.insts.len(), 1);
assert_eq!(entry.insts[0].op, 1);
match &entry.term {
Term::Jmp(jmp) => {
assert_eq!(jmp.target.get().name, Symbol(50));
match &jmp.target.get().term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(0));
}
Term::Jmp(_) => panic!("expected Ret"),
}
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn trim_preserves_fresh_region() {
let mut region = build_simple_func(42);
let before = region.byte_len();
region.trim();
assert_eq!(region.byte_len(), before);
let func: &Func = ®ion;
assert_eq!(func.name, Symbol(42));
let block = func.entry.get();
assert_eq!(block.insts.len(), 1);
assert_eq!(block.insts[0].args[0], Value::Const(42));
match &block.term {
Term::Jmp(jmp) => {
let exit = jmp.target.get();
match &exit.term {
Term::Ret { values } => assert_eq!(values[0], Value::Const(42)),
Term::Jmp(_) => panic!("expected Ret"),
}
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn trim_after_splice_list() {
let mut region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
empty(),
list([
Inst::make(1, Type(0), list(vec![Value::Const(10), Value::Const(11)])),
Inst::make(2, Type(1), list(vec![Value::Const(20)])),
]),
Term::make_ret(list([Value::Const(42)])),
)),
));
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.entry.insts);
s.splice_list(insts, [Inst::make(9, Type(0), list(vec![Value::Const(99)]))]);
});
region.trim();
let func: &Func = ®ion;
let block = func.entry.get();
assert_eq!(block.insts.len(), 1);
assert_eq!(block.insts[0].op, 9);
assert_eq!(block.insts[0].args[0], Value::Const(99));
match &block.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(42));
}
Term::Jmp(_) => panic!("expected Ret"),
}
}
#[test]
fn trim_generic_type() {
let mut region: Region<Signature<u32>> = Region::new(Signature::<u32>::make(
list([Type(1), Type(2)]),
list([Type(3)]),
list([100u32, 200]),
));
let before = region.byte_len();
region.trim();
assert_eq!(region.byte_len(), before);
let sig: &Signature<u32> = ®ion;
assert_eq!(sig.params.len(), 2);
assert_eq!(sig.params[0], Type(1));
assert_eq!(sig.returns[0], Type(3));
assert_eq!(sig.custom[0], 100);
assert_eq!(sig.custom[1], 200);
}
#[derive(Flat, Debug)]
struct ListBlock {
name: Symbol,
items: NearList<Value>,
}
#[derive(Flat, Debug)]
struct ListFunc {
name: Symbol,
insts: NearList<Inst>,
}
#[test]
fn near_list_empty() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
let block: &ListBlock = ®ion;
assert_eq!(block.name, Symbol(1));
assert!(block.items.is_empty());
assert_eq!(block.items.len(), 0);
assert!(block.items.first().is_none());
assert_eq!(block.items.iter().count(), 0);
}
#[test]
fn near_list_single_element() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(2), list([Value::Const(42)])));
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 1);
assert_eq!(*block.items.first().unwrap(), Value::Const(42));
let collected: Vec<&Value> = block.items.iter().collect();
assert_eq!(collected.len(), 1);
assert_eq!(*collected[0], Value::Const(42));
}
#[test]
fn near_list_multiple_elements() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(3),
list([Value::Const(10), Value::Type(Type(1)), Value::Const(30)]),
));
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
let vals: Vec<&Value> = block.items.iter().collect();
assert_eq!(*vals[0], Value::Const(10));
assert_eq!(*vals[1], Value::Type(Type(1)));
assert_eq!(*vals[2], Value::Const(30));
}
#[test]
fn near_list_into_iter() {
let region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(4), list([Value::Const(1), Value::Const(2)])));
let block: &ListBlock = ®ion;
let mut count = 0;
for val in &block.items {
assert!(matches!(val, Value::Const(1 | 2)));
count += 1;
}
assert_eq!(count, 2);
}
#[test]
fn near_list_with_pointer_fields() {
let region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(5),
list([
Inst::make(1, Type(0), list([Value::Const(10), Value::Const(11)].as_slice())),
Inst::make(2, Type(1), list([Value::Const(20)].as_slice())),
Inst::make(3, Type(0), list([].as_slice())),
]),
));
let func: &ListFunc = ®ion;
assert_eq!(func.name, Symbol(5));
assert_eq!(func.insts.len(), 3);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].op, 1);
assert_eq!(insts[0].args.len(), 2);
assert_eq!(insts[0].args[0], Value::Const(10));
assert_eq!(insts[0].args[1], Value::Const(11));
assert_eq!(insts[1].op, 2);
assert_eq!(insts[1].args.len(), 1);
assert_eq!(insts[1].args[0], Value::Const(20));
assert_eq!(insts[2].op, 3);
assert!(insts[2].args.is_empty());
}
#[test]
fn near_list_clone_preserves() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(6),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
let cloned = region.clone();
let b1: &ListBlock = ®ion;
let b2: &ListBlock = &cloned;
assert_eq!(b1.name, b2.name);
assert_eq!(b1.items.len(), b2.items.len());
let v1: Vec<&Value> = b1.items.iter().collect();
let v2: Vec<&Value> = b2.items.iter().collect();
for (a, b) in v1.iter().zip(v2.iter()) {
assert_eq!(**a, **b);
}
}
#[test]
fn near_list_debug() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(7), list([Value::Const(42)])));
let debug_str = format!("{:?}", &*region);
assert!(debug_str.contains("ListBlock"));
}
#[test]
fn near_list_splice_list() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.splice_list(items, [Value::Const(10), Value::Const(20), Value::Const(30)]);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
let vals: Vec<&Value> = block.items.iter().collect();
assert_eq!(*vals[0], Value::Const(10));
assert_eq!(*vals[1], Value::Const(20));
assert_eq!(*vals[2], Value::Const(30));
}
#[test]
fn near_list_splice_list_to_empty() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1), Value::Const(2)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.splice_list(items, core::iter::empty::<Value>());
});
let block: &ListBlock = ®ion;
assert!(block.items.is_empty());
}
#[test]
fn near_list_push_front() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
let vals: Vec<&Value> = block.items.iter().collect();
assert_eq!(*vals[0], Value::Const(1));
assert_eq!(*vals[1], Value::Const(2));
assert_eq!(*vals[2], Value::Const(3));
}
#[test]
fn near_list_push_front_empty() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(42));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 1);
assert_eq!(*block.items.first().unwrap(), Value::Const(42));
}
#[test]
fn near_list_trim() {
let mut region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(1),
list([
Inst::make(1, Type(0), list([Value::Const(10)].as_slice())),
Inst::make(2, Type(1), list([Value::Const(20), Value::Const(21)].as_slice())),
]),
));
let before = region.byte_len();
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.insts);
s.splice_list(insts, [Inst::make(9, Type(0), list([Value::Const(99)]))]);
});
let after_splice = region.byte_len();
assert!(after_splice >= before, "splice should not shrink");
region.trim();
let after_trim = region.byte_len();
assert!(after_trim <= after_splice, "trim should not grow");
let func: &ListFunc = ®ion;
assert_eq!(func.name, Symbol(1));
assert_eq!(func.insts.len(), 1);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].op, 9);
assert_eq!(insts[0].args.len(), 1);
assert_eq!(insts[0].args[0], Value::Const(99));
}
#[test]
fn near_list_trim_preserves_fresh() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
let before = region.byte_len();
region.trim();
assert_eq!(region.byte_len(), before);
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
let vals: Vec<&Value> = block.items.iter().collect();
assert_eq!(*vals[0], Value::Const(1));
assert_eq!(*vals[1], Value::Const(2));
assert_eq!(*vals[2], Value::Const(3));
}
#[test]
fn near_list_exact_size_iterator() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
let block: &ListBlock = ®ion;
let iter = block.items.iter();
assert_eq!(iter.len(), 3);
}
#[test]
fn near_list_fused_iterator() {
let region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1), Value::Const(2)])));
let block: &ListBlock = ®ion;
let mut iter = block.items.iter();
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn near_list_fused_iterator_empty() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
let block: &ListBlock = ®ion;
let mut iter = block.items.iter();
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn near_list_last_single_segment() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
let block: &ListBlock = ®ion;
assert_eq!(block.items.last(), Some(&Value::Const(30)));
}
#[test]
fn near_list_last_empty() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
let block: &ListBlock = ®ion;
assert_eq!(block.items.last(), None);
}
#[test]
fn near_list_last_single_element() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), list([Value::Const(42)])));
let block: &ListBlock = ®ion;
assert_eq!(block.items.last(), Some(&Value::Const(42)));
assert_eq!(block.items.first(), block.items.last());
}
#[test]
fn near_list_last_multi_segment() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 2);
assert_eq!(block.items.last(), Some(&Value::Const(3)));
}
#[test]
fn near_list_last_multi_segment_after_trim() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
region.trim();
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 1);
assert_eq!(block.items.last(), Some(&Value::Const(3)));
}
#[test]
fn near_list_fused_iterator_multi_segment() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 2);
let mut iter = block.items.iter();
assert_eq!(iter.len(), 3);
assert_eq!(iter.next(), Some(&Value::Const(1)));
assert_eq!(iter.next(), Some(&Value::Const(2)));
assert_eq!(iter.next(), Some(&Value::Const(3)));
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn extend_list_appends_to_existing() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1), Value::Const(2)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, [Value::Const(3), Value::Const(4)]);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 4);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
assert_eq!(block.items[3], Value::Const(4));
}
#[test]
fn extend_list_on_empty() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, [Value::Const(10), Value::Const(20)]);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 2);
assert_eq!(block.items[0], Value::Const(10));
assert_eq!(block.items[1], Value::Const(20));
}
#[test]
fn extend_list_with_empty_extra() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, core::iter::empty::<Value>());
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 1);
assert_eq!(block.items[0], Value::Const(1));
}
#[test]
fn extend_list_single_element() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, [Value::Const(4)]);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 4);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
assert_eq!(block.items[3], Value::Const(4));
}
#[test]
fn extend_list_with_iterator() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, (0..3).map(|k| Value::Const(10 + k)));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 4);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(10));
assert_eq!(block.items[2], Value::Const(11));
assert_eq!(block.items[3], Value::Const(12));
}
#[test]
fn extend_list_preserves_after_trim() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, [Value::Const(2), Value::Const(3)]);
});
region.trim();
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
}
#[test]
fn push_front_with_ref_deep_copies() {
let mut region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(1),
list([Inst::make(1, Type(0), list([Value::Const(42)].as_slice()))]),
));
region.session(|s| {
let first_inst = s.nav(s.root(), |f| &f.insts[0]);
let insts = s.nav(s.root(), |f| &f.insts);
s.push_front(insts, first_inst);
});
let func: &ListFunc = ®ion;
assert_eq!(func.insts.len(), 2);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].op, 1);
assert_eq!(insts[0].typ, Type(0));
assert_eq!(insts[0].args.len(), 1);
assert_eq!(insts[0].args[0], Value::Const(42));
assert_eq!(insts[1].op, 1);
assert_eq!(insts[1].args[0], Value::Const(42));
}
#[test]
fn splice_list_with_refs_deep_copies() {
let mut region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(1),
list([
Inst::make(1, Type(0), list([Value::Const(10)].as_slice())),
Inst::make(2, Type(1), list([Value::Const(20), Value::Const(21)].as_slice())),
]),
));
region.session(|s| {
let ref0 = s.nav(s.root(), |f| &f.insts[0]);
let ref1 = s.nav(s.root(), |f| &f.insts[1]);
let insts = s.nav(s.root(), |f| &f.insts);
s.splice_list(insts, [ref1, ref0]);
});
let func: &ListFunc = ®ion;
assert_eq!(func.insts.len(), 2);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].op, 2);
assert_eq!(insts[0].args.len(), 2);
assert_eq!(insts[0].args[0], Value::Const(20));
assert_eq!(insts[0].args[1], Value::Const(21));
assert_eq!(insts[1].op, 1);
assert_eq!(insts[1].args.len(), 1);
assert_eq!(insts[1].args[0], Value::Const(10));
}
#[test]
fn push_front_ref_primitive_list() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(10), Value::Const(20)])));
region.session(|s| {
let first_val = s.nav(s.root(), |b| &b.items[0]);
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, first_val);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(10));
assert_eq!(block.items[1], Value::Const(10));
assert_eq!(block.items[2], Value::Const(20));
}
#[derive(Flat, Debug)]
struct OptBlock {
name: Symbol,
next: Option<Near<Block>>,
}
#[test]
fn option_near_some_roundtrip() {
let region: Region<OptBlock> = Region::new(OptBlock::make(
Symbol(1),
maybe(Some(Block::make(Symbol(2), empty(), empty(), Term::make_ret(list([Value::Const(42)]))))),
));
let ob: &OptBlock = ®ion;
assert_eq!(ob.name, Symbol(1));
let next = ob.next.as_ref().expect("should be Some");
let block = next.get();
assert_eq!(block.name, Symbol(2));
match &block.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(42));
}
Term::Jmp(_) => panic!("expected Ret"),
}
}
#[test]
fn option_near_none_roundtrip() {
let region: Region<OptBlock> = Region::new(OptBlock::make(Symbol(1), none::<Block>()));
let ob: &OptBlock = ®ion;
assert_eq!(ob.name, Symbol(1));
assert!(ob.next.is_none());
}
#[test]
fn option_near_trim() {
let mut region: Region<OptBlock> = Region::new(OptBlock::make(
Symbol(1),
maybe(Some(Block::make(
Symbol(2),
empty(),
list([Inst::make(1, Type(0), list([Value::Const(10)]))]),
Term::make_ret(list([Value::Const(42)])),
))),
));
let before = region.byte_len();
region.trim();
assert_eq!(region.byte_len(), before);
let ob: &OptBlock = ®ion;
let next = ob.next.as_ref().expect("should be Some");
let block = next.get();
assert_eq!(block.name, Symbol(2));
assert_eq!(block.insts.len(), 1);
assert_eq!(block.insts[0].args[0], Value::Const(10));
}
#[test]
fn extend_list_nested_pointer_fields() {
let mut region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(1),
list([Inst::make(1, Type(0), list([Value::Const(10)].as_slice()))]),
));
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.insts);
s.extend_list(
insts,
[
Inst::make(2, Type(1), list(vec![Value::Const(20), Value::Const(21)])),
Inst::make(3, Type(0), list(vec![])),
],
);
});
let func: &ListFunc = ®ion;
assert_eq!(func.insts.len(), 3);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].op, 1);
assert_eq!(insts[0].args.len(), 1);
assert_eq!(insts[0].args[0], Value::Const(10));
assert_eq!(insts[1].op, 2);
assert_eq!(insts[1].args.len(), 2);
assert_eq!(insts[1].args[0], Value::Const(20));
assert_eq!(insts[1].args[1], Value::Const(21));
assert_eq!(insts[2].op, 3);
assert!(insts[2].args.is_empty());
}
#[test]
fn session_map_list_identity() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.map_list(items, |v| v);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(10));
assert_eq!(block.items[1], Value::Const(20));
assert_eq!(block.items[2], Value::Const(30));
}
#[test]
fn session_map_list_transform() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.map_list(items, |v| match v {
Value::Const(n) => Value::Const(n * 10),
other @ Value::Type(_) => other,
});
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(10));
assert_eq!(block.items[1], Value::Const(20));
assert_eq!(block.items[2], Value::Const(30));
}
#[test]
fn session_map_list_empty() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.map_list(items, |v| v);
});
let block: &ListBlock = ®ion;
assert!(block.items.is_empty());
}
#[test]
fn session_graft_copies_region() {
let grafted_block: Region<Block> = Region::new(Block::make(
Symbol(99),
empty(),
empty(),
Term::make_ret(list([Value::Const(77)])),
));
let mut caller: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
empty(),
empty(),
Term::make_jmp(Jmp::make(
empty(),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(empty()))),
)),
)),
));
caller.session(|s| {
let grafted: Ref<'_, Block> = s.graft(&grafted_block);
let jmp_target = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.splice(jmp_target, grafted);
});
let func: &Func = &caller;
match &func.entry.term {
Term::Jmp(jmp) => {
let target = jmp.target.get();
assert_eq!(target.name, Symbol(99));
match &target.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(77));
}
Term::Jmp(_) => panic!("expected Ret in grafted block"),
}
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn session_list_refs_collects() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
let refs = s.list_refs(items);
assert_eq!(refs.len(), 3);
assert_eq!(*s.at(refs[0]), Value::Const(10));
assert_eq!(*s.at(refs[1]), Value::Const(20));
assert_eq!(*s.at(refs[2]), Value::Const(30));
});
}
#[test]
fn session_list_refs_empty() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
let refs = s.list_refs(items);
assert!(refs.is_empty());
});
}
#[test]
fn cursor_pin_extracts_ref() {
let mut region = build_simple_func(1);
region.session(|s| {
let r = s.cursor().at(|f| &f.name).pin();
assert_eq!(*s.at(r), Symbol(1));
});
}
#[test]
fn cursor_write_with_builder() {
let mut region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(Symbol(0), empty(), empty(), Term::make_ret(list([Value::Const(42)])))),
));
region.session(|s| {
s.cursor()
.at(|f| &f.entry)
.follow()
.at(|b| &b.term)
.write_with(Term::make_ret(list([Value::Const(99)])));
});
let func: &Func = ®ion;
match &func.entry.term {
Term::Ret { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(99));
}
Term::Jmp(_) => panic!("expected Ret"),
}
}
#[derive(Flat, Copy, Clone, Debug, PartialEq, Eq)]
struct Marker;
#[derive(Flat, Debug)]
struct TaggedList {
tag: Marker,
items: NearList<u32>,
}
#[test]
fn zst_struct_in_region() {
let region: Region<Marker> = Region::new(Marker);
let _m: &Marker = ®ion;
}
#[test]
fn zst_field_in_struct() {
let region: Region<TaggedList> = Region::new(TaggedList::make(Marker, list([1u32, 2, 3])));
let tl: &TaggedList = ®ion;
assert_eq!(tl.tag, Marker);
assert_eq!(tl.items.len(), 3);
assert_eq!(tl.items[0], 1);
assert_eq!(tl.items[1], 2);
assert_eq!(tl.items[2], 3);
}
#[derive(Flat, Debug)]
struct MarkerList {
items: NearList<Marker>,
}
#[test]
fn zst_list_elements() {
let region: Region<MarkerList> = Region::new(MarkerList::make(list([Marker, Marker, Marker])));
let ml: &MarkerList = ®ion;
assert_eq!(ml.items.len(), 3);
for m in &ml.items {
assert_eq!(*m, Marker);
}
}
#[test]
fn fresh_build_single_segment() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 1);
}
#[test]
fn splice_list_produces_single_segment() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.splice_list(items, [Value::Const(10), Value::Const(20), Value::Const(30)]);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 1);
assert_eq!(block.items.len(), 3);
}
#[test]
fn push_front_creates_multi_segment() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 2);
assert_eq!(block.items.len(), 3);
}
#[test]
fn trim_compacts_to_single_segment() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 2);
region.trim();
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 1);
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
}
#[test]
fn extend_then_trim_single_segment() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.extend_list(items, [Value::Const(2), Value::Const(3)]);
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 2);
region.trim();
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 1);
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
}
#[test]
fn empty_list_zero_segments() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 0);
}
#[test]
fn nested_list_trim_all_single_segment() {
let mut region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(1),
list([Inst::make(1, Type(0), list([Value::Const(10)].as_slice()))]),
));
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.insts);
s.extend_list(insts, [Inst::make(2, Type(1), list(vec![Value::Const(20), Value::Const(21)]))]);
});
let func: &ListFunc = ®ion;
assert_eq!(func.insts.segment_count(), 2);
region.trim();
let func: &ListFunc = ®ion;
assert_eq!(func.insts.segment_count(), 1);
assert_eq!(func.insts.len(), 2);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].args.segment_count(), 1);
assert_eq!(insts[1].args.segment_count(), 1);
}
#[test]
fn near_list_get_returns_some() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
let block: &ListBlock = ®ion;
assert_eq!(*block.items.get(0).unwrap(), Value::Const(10));
assert_eq!(*block.items.get(1).unwrap(), Value::Const(20));
assert_eq!(*block.items.get(2).unwrap(), Value::Const(30));
}
#[test]
fn near_list_get_returns_none_oob() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), list([Value::Const(10)])));
let block: &ListBlock = ®ion;
assert!(block.items.get(1).is_none());
assert!(block.items.get(100).is_none());
}
#[test]
fn near_list_get_empty_returns_none() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
let block: &ListBlock = ®ion;
assert!(block.items.get(0).is_none());
}
#[test]
fn index_single_segment_o1() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 1);
assert_eq!(block.items[0], Value::Const(10));
assert_eq!(block.items[1], Value::Const(20));
assert_eq!(block.items[2], Value::Const(30));
}
#[test]
fn index_multi_segment_fallback() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(2), Value::Const(3)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.push_front(items, Value::Const(1));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.segment_count(), 2);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
}
#[test]
fn filter_list_keeps_matching() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3), Value::Const(4)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.filter_list(items, |v| matches!(v, Value::Const(n) if *n % 2 == 0));
});
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 2);
assert_eq!(block.items[0], Value::Const(2));
assert_eq!(block.items[1], Value::Const(4));
}
#[test]
fn filter_list_removes_all() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(1), Value::Const(2)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.filter_list(items, |_| false);
});
let block: &ListBlock = ®ion;
assert!(block.items.is_empty());
}
#[test]
fn filter_list_keeps_all_noop() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.filter_list(items, |_| true);
});
assert_eq!(region.byte_len(), before);
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
}
#[test]
fn filter_list_empty() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.filter_list(items, |_| true);
});
let block: &ListBlock = ®ion;
assert!(block.items.is_empty());
}
#[test]
fn filter_list_nested_pointers() {
let mut region: Region<ListFunc> = Region::new(ListFunc::make(
Symbol(1),
list([
Inst::make(1, Type(0), list([Value::Const(10)].as_slice())),
Inst::make(2, Type(1), list([Value::Const(20), Value::Const(21)].as_slice())),
Inst::make(3, Type(0), list([Value::Const(30)].as_slice())),
]),
));
region.session(|s| {
let insts = s.nav(s.root(), |f| &f.insts);
s.filter_list(insts, |inst| inst.op != 2);
});
let func: &ListFunc = ®ion;
assert_eq!(func.insts.len(), 2);
let insts: Vec<&Inst> = func.insts.iter().collect();
assert_eq!(insts[0].op, 1);
assert_eq!(insts[0].args.len(), 1);
assert_eq!(insts[0].args[0], Value::Const(10));
assert_eq!(insts[1].op, 3);
assert_eq!(insts[1].args.len(), 1);
assert_eq!(insts[1].args[0], Value::Const(30));
}
#[test]
fn filter_list_then_trim() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3), Value::Const(4), Value::Const(5)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.filter_list(items, |v| matches!(v, Value::Const(n) if *n <= 3));
});
region.trim();
let block: &ListBlock = ®ion;
assert_eq!(block.items.len(), 3);
assert_eq!(block.items.segment_count(), 1);
assert_eq!(block.items[0], Value::Const(1));
assert_eq!(block.items[1], Value::Const(2));
assert_eq!(block.items[2], Value::Const(3));
}
#[test]
fn list_item_returns_correct_ref() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
let r0 = s.list_item(items, 0);
let r1 = s.list_item(items, 1);
let r2 = s.list_item(items, 2);
assert_eq!(*s.at(r0), Value::Const(10));
assert_eq!(*s.at(r1), Value::Const(20));
assert_eq!(*s.at(r2), Value::Const(30));
});
}
#[test]
#[should_panic(expected = "NearList index out of bounds")]
fn list_item_oob_panics() {
let mut region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(10)])));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
let _ = s.list_item(items, 5);
});
}
#[test]
fn near_list_contains_present() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
assert!(region.items.contains(&Value::Const(2)));
}
#[test]
fn near_list_contains_absent() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(2), Value::Const(3)]),
));
assert!(!region.items.contains(&Value::Const(99)));
}
#[test]
fn near_list_contains_empty() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
assert!(!region.items.contains(&Value::Const(1)));
}
#[test]
fn near_list_contains_first_and_last() {
let region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(10), Value::Const(20), Value::Const(30)]),
));
assert!(region.items.contains(&Value::Const(10)));
assert!(region.items.contains(&Value::Const(30)));
}
#[derive(Flat, Debug)]
struct U32List {
items: NearList<u32>,
}
#[test]
fn reverse_list_basic() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 2, 3, 4, 5])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.reverse_list(items);
});
assert_eq!(region.items.len(), 5);
assert_eq!(region.items[0], 5);
assert_eq!(region.items[1], 4);
assert_eq!(region.items[2], 3);
assert_eq!(region.items[3], 2);
assert_eq!(region.items[4], 1);
}
#[test]
fn reverse_list_single() {
let mut region: Region<U32List> = Region::new(U32List::make(list([42u32])));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.reverse_list(items);
});
assert_eq!(region.byte_len(), before);
assert_eq!(region.items[0], 42);
}
#[test]
fn reverse_list_empty() {
let mut region: Region<U32List> = Region::new(U32List::make(empty()));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.reverse_list(items);
});
assert_eq!(region.byte_len(), before);
assert!(region.items.is_empty());
}
#[test]
fn reverse_list_two_elements() {
let mut region: Region<U32List> = Region::new(U32List::make(list([10u32, 20])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.reverse_list(items);
});
assert_eq!(region.items[0], 20);
assert_eq!(region.items[1], 10);
}
#[test]
fn reverse_list_then_trim() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 2, 3])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.reverse_list(items);
});
region.trim();
assert_eq!(region.items.len(), 3);
assert_eq!(region.items.segment_count(), 1);
assert_eq!(region.items[0], 3);
assert_eq!(region.items[1], 2);
assert_eq!(region.items[2], 1);
}
#[test]
fn sort_list_basic() {
let mut region: Region<U32List> = Region::new(U32List::make(list([3u32, 1, 4, 1, 5, 9, 2, 6])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, u32::cmp);
});
assert_eq!(region.items.len(), 8);
assert_eq!(region.items[0], 1);
assert_eq!(region.items[1], 1);
assert_eq!(region.items[2], 2);
assert_eq!(region.items[3], 3);
assert_eq!(region.items[4], 4);
assert_eq!(region.items[5], 5);
assert_eq!(region.items[6], 6);
assert_eq!(region.items[7], 9);
}
#[test]
fn sort_list_descending() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 2, 3, 4, 5])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, |a, b| b.cmp(a));
});
assert_eq!(region.items[0], 5);
assert_eq!(region.items[1], 4);
assert_eq!(region.items[2], 3);
assert_eq!(region.items[3], 2);
assert_eq!(region.items[4], 1);
}
#[test]
fn sort_list_already_sorted() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 2, 3])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, u32::cmp);
});
assert_eq!(region.items[0], 1);
assert_eq!(region.items[1], 2);
assert_eq!(region.items[2], 3);
}
#[test]
fn sort_list_single() {
let mut region: Region<U32List> = Region::new(U32List::make(list([42u32])));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, u32::cmp);
});
assert_eq!(region.byte_len(), before);
assert_eq!(region.items[0], 42);
}
#[test]
fn sort_list_empty() {
let mut region: Region<U32List> = Region::new(U32List::make(empty()));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, u32::cmp);
});
assert_eq!(region.byte_len(), before);
assert!(region.items.is_empty());
}
#[test]
fn sort_list_then_trim() {
let mut region: Region<U32List> = Region::new(U32List::make(list([5u32, 3, 1, 4, 2])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, u32::cmp);
});
region.trim();
assert_eq!(region.items.len(), 5);
assert_eq!(region.items.segment_count(), 1);
let vals: Vec<u32> = region.items.iter().copied().collect();
assert_eq!(vals, vec![1, 2, 3, 4, 5]);
}
#[test]
fn sort_list_with_value_enum() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(30), Value::Const(10), Value::Const(20)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.sort_list(items, |a, b| {
let Value::Const(a_val) = a else { return core::cmp::Ordering::Equal };
let Value::Const(b_val) = b else { return core::cmp::Ordering::Equal };
a_val.cmp(b_val)
});
});
assert_eq!(region.items[0], Value::Const(10));
assert_eq!(region.items[1], Value::Const(20));
assert_eq!(region.items[2], Value::Const(30));
}
#[test]
fn dedup_list_basic() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 1, 2, 3, 3, 3, 2])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.items.len(), 4);
assert_eq!(region.items[0], 1);
assert_eq!(region.items[1], 2);
assert_eq!(region.items[2], 3);
assert_eq!(region.items[3], 2);
}
#[test]
fn dedup_list_no_duplicates() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 2, 3, 4])));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.byte_len(), before);
assert_eq!(region.items.len(), 4);
}
#[test]
fn dedup_list_all_same() {
let mut region: Region<U32List> = Region::new(U32List::make(list([5u32, 5, 5, 5])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.items.len(), 1);
assert_eq!(region.items[0], 5);
}
#[test]
fn dedup_list_empty() {
let mut region: Region<U32List> = Region::new(U32List::make(empty()));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.byte_len(), before);
assert!(region.items.is_empty());
}
#[test]
fn dedup_list_single() {
let mut region: Region<U32List> = Region::new(U32List::make(list([42u32])));
let before = region.byte_len();
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.byte_len(), before);
assert_eq!(region.items[0], 42);
}
#[test]
fn dedup_list_then_trim() {
let mut region: Region<U32List> = Region::new(U32List::make(list([1u32, 1, 2, 2, 3, 3])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.dedup_list(items, |a, b| a == b);
});
region.trim();
assert_eq!(region.items.len(), 3);
assert_eq!(region.items.segment_count(), 1);
assert_eq!(region.items[0], 1);
assert_eq!(region.items[1], 2);
assert_eq!(region.items[2], 3);
}
#[test]
fn dedup_list_with_value_enum() {
let mut region: Region<ListBlock> = Region::new(ListBlock::make(
Symbol(1),
list([Value::Const(1), Value::Const(1), Value::Const(2), Value::Const(2), Value::Const(3)]),
));
region.session(|s| {
let items = s.nav(s.root(), |b| &b.items);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.items.len(), 3);
assert_eq!(region.items[0], Value::Const(1));
assert_eq!(region.items[1], Value::Const(2));
assert_eq!(region.items[2], Value::Const(3));
}
#[test]
fn sort_then_dedup() {
let mut region: Region<U32List> = Region::new(U32List::make(list([3u32, 1, 2, 1, 3, 2])));
region.session(|s| {
let items = s.nav(s.root(), |r| &r.items);
s.sort_list(items, u32::cmp);
s.dedup_list(items, |a, b| a == b);
});
assert_eq!(region.items.len(), 3);
assert_eq!(region.items[0], 1);
assert_eq!(region.items[1], 2);
assert_eq!(region.items[2], 3);
}
#[test]
#[should_panic(expected = "NearList index out of bounds")]
fn near_list_index_out_of_bounds_panics() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), list([Value::Const(10)])));
let block: &ListBlock = ®ion;
let _ = block.items[5]; }
#[test]
fn validate_primitive_region() {
let region: Region<u32> = Region::new(42u32);
let bytes = region.as_bytes();
u32::validate(0, bytes).unwrap();
}
#[test]
fn validate_struct_with_near() {
let region = build_simple_func(1);
let bytes = region.as_bytes();
Func::validate(0, bytes).unwrap();
}
#[test]
fn validate_struct_with_near_list() {
let region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(10), Value::Const(20)])));
let bytes = region.as_bytes();
ListBlock::validate(0, bytes).unwrap();
}
#[test]
fn validate_struct_with_option_near_some() {
let region: Region<OptBlock> = Region::new(OptBlock::make(
Symbol(1),
maybe(Some(Block::make(Symbol(2), empty(), empty(), Term::make_ret(list([Value::Const(42)]))))),
));
let bytes = region.as_bytes();
OptBlock::validate(0, bytes).unwrap();
}
#[test]
fn validate_struct_with_option_near_none() {
let region: Region<OptBlock> = Region::new(OptBlock::make(Symbol(1), none::<Block>()));
let bytes = region.as_bytes();
OptBlock::validate(0, bytes).unwrap();
}
#[test]
fn validate_nested_structs() {
let region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
list([(Symbol(1), Type(0))]),
list([
Inst::make(1, Type(0), list([Value::Const(10), Value::Const(11)])),
Inst::make(2, Type(1), list([Value::Const(20), Value::Const(21)])),
]),
Term::make_jmp(Jmp::make(
list([Value::Const(1)]),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(list([Value::Const(42)])))),
)),
)),
));
let bytes = region.as_bytes();
Func::validate(0, bytes).unwrap();
}
#[test]
fn validate_enum_all_variants() {
let region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(Symbol(0), empty(), empty(), Term::make_ret(list([Value::Const(1)])))),
));
Func::validate(0, region.as_bytes()).unwrap();
let region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(
Symbol(0),
empty(),
empty(),
Term::make_jmp(Jmp::make(
empty(),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(empty()))),
)),
)),
));
Func::validate(0, region.as_bytes()).unwrap();
}
#[test]
fn validate_generic_struct() {
let region: Region<Signature<u32>> =
Region::new(Signature::<u32>::make(list([Type(1)]), list([Type(2)]), list([10u32, 20])));
Signature::<u32>::validate(0, region.as_bytes()).unwrap();
}
#[test]
fn validate_after_mutation() {
let mut region = build_simple_func(1);
region.session(|s| {
let name = s.nav(s.root(), |f| &f.name);
s.set(name, Symbol(99));
});
Func::validate(0, region.as_bytes()).unwrap();
}
#[test]
fn validate_after_trim() {
let mut region = build_simple_func(1);
region.session(|s| {
let jmp_target = s.nav(s.root(), |f| match &f.entry.term {
Term::Jmp(jmp) => &jmp.target,
Term::Ret { .. } => panic!("expected Jmp"),
});
s.splice(
jmp_target,
Block::make(Symbol(50), empty(), empty(), Term::make_ret(list([Value::Const(0)]))),
);
});
region.trim();
Func::validate(0, region.as_bytes()).unwrap();
}
#[test]
fn validate_empty_list() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), empty()));
ListBlock::validate(0, region.as_bytes()).unwrap();
}
#[test]
fn validate_empty_buffer() {
let result = u32::validate(0, &[]);
assert!(matches!(result, Err(ValidateError::OutOfBounds { .. })));
}
#[test]
fn validate_truncated_buffer() {
let region = build_simple_func(1);
let bytes = region.as_bytes();
let result = Func::validate(0, &bytes[..2]);
assert!(matches!(result, Err(ValidateError::OutOfBounds { .. })));
}
#[test]
fn validate_bad_near_offset_oob() {
let region = build_simple_func(1);
let mut bytes = region.as_bytes().to_vec();
let near_offset = core::mem::offset_of!(Func, entry);
let bad_off: i32 = i32::try_from(bytes.len()).unwrap() * 2;
bytes[near_offset..near_offset + 4].copy_from_slice(&bad_off.to_ne_bytes());
let result = Func::validate(0, &bytes);
assert!(matches!(result, Err(ValidateError::OutOfBounds { .. })));
}
#[test]
fn validate_bad_near_offset_zero() {
let region = build_simple_func(1);
let mut bytes = region.as_bytes().to_vec();
let near_offset = core::mem::offset_of!(Func, entry);
bytes[near_offset..near_offset + 4].copy_from_slice(&0i32.to_ne_bytes());
let result = Func::validate(0, &bytes);
assert!(matches!(result, Err(ValidateError::NullNear { .. })));
}
#[test]
fn validate_bad_near_alignment() {
let region = build_simple_func(1);
let mut bytes = region.as_bytes().to_vec();
let near_offset = core::mem::offset_of!(Func, entry);
let bad_off: i32 = 1; bytes[near_offset..near_offset + 4].copy_from_slice(&bad_off.to_ne_bytes());
let result = Func::validate(0, &bytes);
assert!(result.is_err());
}
#[test]
fn validate_bad_list_header_inconsistent() {
let region: Region<ListBlock> = Region::new(ListBlock::make(Symbol(1), list([Value::Const(10)])));
let mut bytes = region.as_bytes().to_vec();
let list_offset = core::mem::offset_of!(ListBlock, items);
bytes[list_offset..list_offset + 4].copy_from_slice(&42i32.to_ne_bytes());
bytes[list_offset + 4..list_offset + 8].copy_from_slice(&0u32.to_ne_bytes());
let result = ListBlock::validate(0, &bytes);
assert!(matches!(result, Err(ValidateError::InvalidListHeader { .. })));
}
#[test]
fn validate_bad_list_len_mismatch() {
let region: Region<ListBlock> =
Region::new(ListBlock::make(Symbol(1), list([Value::Const(10), Value::Const(20)])));
let mut bytes = region.as_bytes().to_vec();
let list_offset = core::mem::offset_of!(ListBlock, items);
bytes[list_offset + 4..list_offset + 8].copy_from_slice(&99u32.to_ne_bytes());
let result = ListBlock::validate(0, &bytes);
assert!(matches!(result, Err(ValidateError::ListLenMismatch { .. })));
}
#[test]
fn validate_bad_enum_discriminant() {
let region: Region<Func> = Region::new(Func::make(
Symbol(1),
near(Block::make(Symbol(0), empty(), empty(), Term::make_ret(list([Value::Const(42)])))),
));
let mut bytes = region.as_bytes().to_vec();
let near_offset = core::mem::offset_of!(Func, entry);
let near_off = i32::from_ne_bytes(bytes[near_offset..near_offset + 4].try_into().unwrap());
let block_addr = near_offset.cast_signed().wrapping_add(near_off as isize).cast_unsigned();
let term_offset = core::mem::offset_of!(Block, term);
let disc_addr = block_addr + term_offset;
bytes[disc_addr] = 255;
let result = Func::validate(0, &bytes);
assert!(matches!(result, Err(ValidateError::InvalidDiscriminant { .. })));
}
#[derive(Flat, Debug)]
struct BoolStruct {
flag: bool,
id: u32,
}
#[test]
fn validate_bad_bool() {
let region: Region<BoolStruct> = Region::new(BoolStruct { flag: true, id: 42 });
let mut bytes = region.as_bytes().to_vec();
let bool_offset = core::mem::offset_of!(BoolStruct, flag);
bytes[bool_offset] = 2;
let result = BoolStruct::validate(0, &bytes);
assert!(matches!(result, Err(ValidateError::InvalidBool { value: 2, .. })));
}
#[test]
fn region_as_bytes_from_bytes_roundtrip() {
let region = build_simple_func(42);
let bytes = region.as_bytes();
let restored: Region<Func> = Region::from_bytes(bytes).unwrap();
assert_eq!(restored.name, Symbol(42));
let block = restored.entry.get();
assert_eq!(block.insts.len(), 1);
assert_eq!(block.insts[0].args[0], Value::Const(42));
}
#[test]
fn region_from_bytes_unchecked_roundtrip() {
let region = build_simple_func(7);
let bytes = region.as_bytes();
let restored: Region<Func> = unsafe { Region::from_bytes_unchecked(bytes) };
assert_eq!(restored.name, Symbol(7));
let block = restored.entry.get();
assert_eq!(block.insts[0].op, 1);
}
#[test]
fn region_from_bytes_complex() {
let region: Region<Func> = Region::new(Func::make(
Symbol(100),
near(Block::make(
Symbol(0),
list([(Symbol(1), Type(0)), (Symbol(2), Type(1))]),
list([
Inst::make(1, Type(0), list([Value::Const(10), Value::Const(11)])),
Inst::make(2, Type(1), list([Value::Const(20), Value::Const(21)])),
]),
Term::make_jmp(Jmp::make(
list([Value::Const(1)]),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(list([Value::Const(42)])))),
)),
)),
));
let bytes = region.as_bytes();
let restored: Region<Func> = Region::from_bytes(bytes).unwrap();
assert_eq!(restored.name, Symbol(100));
let block = restored.entry.get();
assert_eq!(block.params.len(), 2);
assert_eq!(block.insts.len(), 2);
assert_eq!(block.insts[0].args[0], Value::Const(10));
match &block.term {
Term::Jmp(jmp) => {
assert_eq!(jmp.target.get().name, Symbol(1));
}
Term::Ret { .. } => panic!("expected Jmp"),
}
}
#[test]
fn region_from_bytes_clone_equivalence() {
let region = build_simple_func(5);
let cloned = region.clone();
let from_bytes: Region<Func> = Region::from_bytes(region.as_bytes()).unwrap();
assert_eq!(cloned.name, from_bytes.name);
assert_eq!(cloned.entry.get().name, from_bytes.entry.get().name);
assert_eq!(cloned.entry.get().insts.len(), from_bytes.entry.get().insts.len());
}
#[test]
fn region_from_bytes_fixed_buf() {
use nearest::FixedBuf;
let region: Region<u32, FixedBuf<64>> = Region::new_in(42u32);
let bytes = region.as_bytes();
let restored: Region<u32, FixedBuf<64>> = Region::from_bytes(bytes).unwrap();
assert_eq!(*restored, 42);
}
#[test]
fn from_bytes_unchecked_matches_checked_simple() {
let region: Region<Func> = build_simple_func(99);
let bytes = region.as_bytes();
let checked: Region<Func> = Region::from_bytes(bytes).unwrap();
let unchecked: Region<Func> = unsafe { Region::from_bytes_unchecked(bytes) };
assert_eq!(checked.name, unchecked.name);
assert_eq!(checked.entry.get().name, unchecked.entry.get().name);
assert_eq!(checked.entry.get().insts.len(), unchecked.entry.get().insts.len());
assert_eq!(checked.entry.get().insts[0].op, unchecked.entry.get().insts[0].op);
assert_eq!(checked.byte_len(), unchecked.byte_len());
}
#[test]
fn from_bytes_unchecked_matches_checked_complex() {
let region: Region<Func> = Region::new(Func::make(
Symbol(200),
near(Block::make(
Symbol(0),
list([(Symbol(1), Type(0)), (Symbol(2), Type(1))]),
list([
Inst::make(1, Type(0), list([Value::Const(10), Value::Const(11)])),
Inst::make(2, Type(1), list([Value::Const(20), Value::Const(21)])),
]),
Term::make_jmp(Jmp::make(
list([Value::Const(1)]),
near(Block::make(Symbol(1), empty(), empty(), Term::make_ret(list([Value::Const(42)])))),
)),
)),
));
let bytes = region.as_bytes();
let checked: Region<Func> = Region::from_bytes(bytes).unwrap();
let unchecked: Region<Func> = unsafe { Region::from_bytes_unchecked(bytes) };
assert_eq!(checked.name, unchecked.name);
let cb = checked.entry.get();
let ub = unchecked.entry.get();
assert_eq!(cb.params.len(), ub.params.len());
assert_eq!(cb.insts.len(), ub.insts.len());
for i in 0..cb.insts.len() {
assert_eq!(cb.insts[i].op, ub.insts[i].op);
assert_eq!(cb.insts[i].typ, ub.insts[i].typ);
assert_eq!(cb.insts[i].args.len(), ub.insts[i].args.len());
}
assert_eq!(checked.byte_len(), unchecked.byte_len());
}
#[test]
fn from_bytes_unchecked_primitive() {
let region: Region<u32> = Region::new(42u32);
let bytes = region.as_bytes();
let checked: Region<u32> = Region::from_bytes(bytes).unwrap();
let unchecked: Region<u32> = unsafe { Region::from_bytes_unchecked(bytes) };
assert_eq!(*checked, *unchecked);
assert_eq!(checked.as_bytes(), unchecked.as_bytes());
}
#[test]
fn from_bytes_unchecked_generic_type() {
let region: Region<Signature<u32>> = Region::new(Signature::<u32>::make(
list([Type(0), Type(1)]),
list([Type(2)]),
list([10u32, 20, 30]),
));
let bytes = region.as_bytes();
let checked: Region<Signature<u32>> = Region::from_bytes(bytes).unwrap();
let unchecked: Region<Signature<u32>> = unsafe { Region::from_bytes_unchecked(bytes) };
assert_eq!(checked.params.len(), unchecked.params.len());
assert_eq!(checked.returns.len(), unchecked.returns.len());
assert_eq!(checked.custom.len(), unchecked.custom.len());
for i in 0..checked.custom.len() {
assert_eq!(checked.custom[i], unchecked.custom[i]);
}
assert_eq!(checked.as_bytes(), unchecked.as_bytes());
}
#[test]
fn from_buf_unchecked_fixed_buf() {
use nearest::{Buf, FixedBuf};
let region: Region<u32, FixedBuf<64>> = Region::new_in(42u32);
let bytes = region.as_bytes();
let mut buf = FixedBuf::<64>::new();
buf.extend_from_slice(bytes);
let restored: Region<u32, FixedBuf<64>> = unsafe { Region::from_buf_unchecked(buf) };
assert_eq!(*restored, 42);
}
#[test]
fn from_buf_unchecked_matches_checked() {
use nearest::Buf;
let region: Region<Func> = build_simple_func(77);
let bytes = region.as_bytes();
let checked: Region<Func> = Region::from_bytes(bytes).unwrap();
let mut buf = nearest::AlignedBuf::<Func>::with_capacity(bytes.len() as u32);
buf.extend_from_slice(bytes);
let unchecked: Region<Func> = unsafe { Region::from_buf_unchecked(buf) };
assert_eq!(checked.name, unchecked.name);
assert_eq!(checked.entry.get().name, unchecked.entry.get().name);
assert_eq!(checked.byte_len(), unchecked.byte_len());
}
#[test]
fn from_bytes_unchecked_after_mutation_and_trim() {
let mut region: Region<Func> = build_simple_func(50);
region.session(|s| {
let root = s.root();
let name = s.nav(root, |f| &f.name);
s.set(name, Symbol(999));
});
region.trim();
let bytes = region.as_bytes();
let checked: Region<Func> = Region::from_bytes(bytes).unwrap();
let unchecked: Region<Func> = unsafe { Region::from_bytes_unchecked(bytes) };
assert_eq!(checked.name, Symbol(999));
assert_eq!(unchecked.name, Symbol(999));
assert_eq!(checked.byte_len(), unchecked.byte_len());
}
#[cfg(feature = "serde")]
mod serde_tests {
use nearest::{Flat, Near, NearList, Region, list, near};
#[derive(Flat, Debug)]
struct SNode {
id: u32,
items: NearList<u32>,
}
#[derive(Flat, Debug)]
struct SFunc {
name: u32,
entry: Near<SNode>,
}
#[test]
fn serde_json_roundtrip_simple() {
let region: Region<SNode> = Region::new(SNode::make(42, list([1u32, 2, 3])));
let json = serde_json::to_string(®ion).unwrap();
let restored: Region<SNode> = serde_json::from_str(&json).unwrap();
assert_eq!(restored.id, 42);
assert_eq!(restored.items.len(), 3);
assert_eq!(restored.items[0], 1);
assert_eq!(restored.items[1], 2);
assert_eq!(restored.items[2], 3);
}
#[test]
fn serde_json_roundtrip_nested() {
let region: Region<SFunc> =
Region::new(SFunc::make(100, near(SNode::make(7, list([10u32, 20, 30])))));
let json = serde_json::to_string(®ion).unwrap();
let restored: Region<SFunc> = serde_json::from_str(&json).unwrap();
assert_eq!(restored.name, 100);
assert_eq!(restored.entry.get().id, 7);
assert_eq!(restored.entry.get().items.len(), 3);
assert_eq!(restored.entry.get().items[0], 10);
assert_eq!(restored.entry.get().items[1], 20);
assert_eq!(restored.entry.get().items[2], 30);
}
#[test]
fn serde_json_roundtrip_fixed_buf() {
use nearest::FixedBuf;
let region: Region<SNode, FixedBuf<256>> = Region::new_in(SNode::make(7, list([10u32, 20])));
let json = serde_json::to_string(®ion).unwrap();
let restored: Region<SNode, FixedBuf<256>> = serde_json::from_str(&json).unwrap();
assert_eq!(restored.id, 7);
assert_eq!(restored.items.len(), 2);
assert_eq!(restored.items[0], 10);
assert_eq!(restored.items[1], 20);
}
#[test]
fn serde_json_invalid_bytes_rejected() {
let bad_json = serde_json::to_string(&[0u8; 2]).unwrap();
let result: Result<Region<SNode>, _> = serde_json::from_str(&bad_json);
assert!(result.is_err());
}
#[test]
fn serde_roundtrip_preserves_byte_equality() {
let region: Region<SNode> = Region::new(SNode::make(99, list([5u32, 6, 7, 8])));
let original_bytes = region.as_bytes().to_vec();
let json = serde_json::to_string(®ion).unwrap();
let restored: Region<SNode> = serde_json::from_str(&json).unwrap();
assert_eq!(restored.as_bytes(), &original_bytes[..]);
}
}
#[derive(Flat, Debug, PartialEq, Eq)]
struct EqNode {
id: u32,
items: NearList<u32>,
}
#[derive(Flat, Debug, PartialEq, Eq)]
struct EqNested {
label: u32,
child: Near<EqNode>,
}
#[test]
fn region_eq_identical_builds() {
let a = Region::new(EqNode::make(1, list([10u32, 20, 30])));
let b = Region::new(EqNode::make(1, list([10u32, 20, 30])));
assert_eq!(a, b);
}
#[test]
fn region_eq_clone() {
let a = Region::new(EqNode::make(42, list([1u32, 2, 3])));
let b = a.clone();
assert_eq!(a, b);
}
#[test]
fn region_ne_different_scalar() {
let a = Region::new(EqNode::make(1, list([10u32, 20])));
let b = Region::new(EqNode::make(2, list([10u32, 20])));
assert_ne!(a, b);
}
#[test]
fn region_ne_different_list_len() {
let a = Region::new(EqNode::make(1, list([10u32, 20])));
let b = Region::new(EqNode::make(1, list([10u32, 20, 30])));
assert_ne!(a, b);
}
#[test]
fn region_ne_different_list_values() {
let a = Region::new(EqNode::make(1, list([10u32, 20, 30])));
let b = Region::new(EqNode::make(1, list([10u32, 20, 99])));
assert_ne!(a, b);
}
#[test]
fn region_eq_empty_lists() {
let a: Region<EqNode> = Region::new(EqNode::make(5, empty()));
let b: Region<EqNode> = Region::new(EqNode::make(5, empty()));
assert_eq!(a, b);
}
#[test]
fn region_eq_after_trim_same_logical_content() {
let a = Region::new(EqNode::make(1, list([100u32])));
let mut b = Region::new(EqNode::make(1, list([10u32, 20, 30])));
b.session(|s| {
let items = s.nav(s.root(), |n| &n.items);
s.splice_list(items, [100u32]);
});
assert!(b.byte_len() > a.byte_len());
b.trim();
assert_eq!(a, b);
}
#[test]
fn region_eq_nested_near() {
let a = Region::new(EqNested::make(1, near(EqNode::make(2, list([3u32, 4])))));
let b = Region::new(EqNested::make(1, near(EqNode::make(2, list([3u32, 4])))));
assert_eq!(a, b);
}
#[test]
fn region_ne_nested_near_different_child() {
let a = Region::new(EqNested::make(1, near(EqNode::make(2, list([3u32, 4])))));
let b = Region::new(EqNested::make(1, near(EqNode::make(9, list([3u32, 4])))));
assert_ne!(a, b);
}
#[test]
fn region_eq_different_buffer_layouts() {
let mut a = Region::new(EqNode::make(7, list([1u32, 2])));
a.session(|s| {
let items = s.nav(s.root(), |n| &n.items);
s.push_front(items, 0u32);
});
let b = Region::new(EqNode::make(7, list([0u32, 1, 2])));
assert_ne!(a.byte_len(), b.byte_len());
assert_eq!(a, b);
}
#[derive(Flat, Debug)]
struct DisplayNode {
value: u32,
child: Near<u32>,
}
#[derive(Flat, Debug)]
struct DisplayList {
items: NearList<u32>,
}
#[test]
fn display_near_delegates_to_target() {
let region = Region::new(DisplayNode::make(1, near(42u32)));
let output = format!("{}", region.child);
assert_eq!(output, "42");
}
#[test]
fn display_near_with_format_spec() {
let region = Region::new(DisplayNode::make(1, near(42u32)));
let output = format!("{:>5}", region.child);
assert_eq!(output, " 42");
}
#[test]
fn display_nearlist_empty() {
let region = Region::new(DisplayList::make(empty()));
let output = format!("{}", region.items);
assert_eq!(output, "[]");
}
#[test]
fn display_nearlist_single() {
let region = Region::new(DisplayList::make(list([7u32])));
let output = format!("{}", region.items);
assert_eq!(output, "[7]");
}
#[test]
fn display_nearlist_multiple() {
let region = Region::new(DisplayList::make(list([1u32, 2, 3])));
let output = format!("{}", region.items);
assert_eq!(output, "[1, 2, 3]");
}
#[test]
fn display_region_delegates_to_root() {
let region: Region<u32> = Region::new(42u32);
let output = format!("{region}");
assert_eq!(output, "42");
}
#[derive(Flat, Debug)]
struct IntoInst {
#[flat(into)]
op: u16,
typ: Type,
args: NearList<Value>,
}
#[derive(Flat, Debug)]
struct IntoOther {
#[flat(into)]
sym: Symbol,
val: u32,
}
#[derive(Flat, Debug)]
#[repr(C, u8)]
#[expect(dead_code, reason = "variants constructed via renamed emitters")]
enum RenamedTerm {
#[flat(rename = "ret")]
Return { values: NearList<Value> },
#[flat(rename = "br")]
Jump(Jmp),
}
#[derive(Flat, Debug)]
#[repr(C, u8)]
#[expect(dead_code, reason = "variants constructed via renamed emitters")]
enum Signal {
#[flat(rename = "on")]
Enabled,
#[flat(rename = "off")]
Disabled,
#[flat(rename = "val")]
WithPayload { data: Near<Type> },
}
#[test]
fn flat_into_primitive_field() {
let region: Region<IntoInst> =
Region::new(IntoInst::make(42u8, Type(1), list([Value::Const(10)])));
let inst: &IntoInst = ®ion;
assert_eq!(inst.op, 42);
assert_eq!(inst.typ, Type(1));
assert_eq!(inst.args.len(), 1);
assert_eq!(inst.args[0], Value::Const(10));
}
#[test]
fn flat_into_primitive_exact_type() {
let region: Region<IntoInst> = Region::new(IntoInst::make(1000u16, Type(0), empty()));
let inst: &IntoInst = ®ion;
assert_eq!(inst.op, 1000);
}
#[test]
fn flat_into_other_field() {
let region: Region<IntoOther> = Region::new(IntoOther::make(Symbol(42), 7));
let other: &IntoOther = ®ion;
assert_eq!(other.sym, Symbol(42));
assert_eq!(other.val, 7);
}
#[test]
fn flat_rename_enum_variant_named() {
let region: Region<RenamedTerm> = Region::new(RenamedTerm::ret(list([Value::Const(42)])));
let term: &RenamedTerm = ®ion;
match term {
RenamedTerm::Return { values } => {
assert_eq!(values.len(), 1);
assert_eq!(values[0], Value::Const(42));
}
RenamedTerm::Jump(_) => panic!("expected Return"),
}
}
#[test]
fn flat_rename_enum_variant_unnamed() {
let region: Region<RenamedTerm> = Region::new(RenamedTerm::br(Jmp::make(
list([Value::Const(1)]),
near(Block::make(Symbol(0), empty(), empty(), Term::make_ret(list([Value::Const(99)])))),
)));
let term: &RenamedTerm = ®ion;
match term {
RenamedTerm::Jump(jmp) => {
assert_eq!(jmp.args.len(), 1);
assert_eq!(jmp.args[0], Value::Const(1));
}
RenamedTerm::Return { .. } => panic!("expected Jump"),
}
}
#[test]
fn flat_rename_enum_unit_variant() {
let region: Region<Signal> = Region::new(Signal::on());
let sig: &Signal = ®ion;
assert!(matches!(sig, Signal::Enabled));
}
#[test]
fn flat_rename_enum_all_variants() {
let r1: Region<Signal> = Region::new(Signal::on());
assert!(matches!(&*r1, Signal::Enabled));
let r2: Region<Signal> = Region::new(Signal::off());
assert!(matches!(&*r2, Signal::Disabled));
let r3: Region<Signal> = Region::new(Signal::val(near(Type(7))));
match &*r3 {
Signal::WithPayload { data } => assert_eq!(*data.get(), Type(7)),
_ => panic!("expected WithPayload"),
}
}
#[test]
fn flat_into_clone_preserves() {
let region: Region<IntoInst> =
Region::new(IntoInst::make(100u8, Type(2), list([Value::Const(5)])));
let cloned = region.clone();
let i1: &IntoInst = ®ion;
let i2: &IntoInst = &cloned;
assert_eq!(i1.op, i2.op);
assert_eq!(i1.typ, i2.typ);
assert_eq!(i1.args[0], i2.args[0]);
}
#[test]
fn flat_into_trim_preserves() {
let mut region: Region<IntoInst> =
Region::new(IntoInst::make(200u8, Type(3), list([Value::Const(7), Value::Const(8)])));
region.trim();
let inst: &IntoInst = ®ion;
assert_eq!(inst.op, 200);
assert_eq!(inst.args.len(), 2);
assert_eq!(inst.args[0], Value::Const(7));
assert_eq!(inst.args[1], Value::Const(8));
}
#[test]
fn into_buf_roundtrip() {
use nearest::{AlignedBuf, Buf};
let region = build_simple_func(7);
let byte_len = region.byte_len();
let buf: AlignedBuf<Func> = region.into_buf();
assert_eq!(buf.len() as usize, byte_len);
let restored: Region<Func> = Region::from_bytes(buf.as_bytes()).unwrap();
assert_eq!(restored.name, Symbol(7));
assert_eq!(restored.entry.insts.len(), 1);
}
#[test]
fn into_vec_returns_correct_length() {
let region = build_simple_func(42);
let expected_len = region.byte_len();
let vec = region.into_vec();
assert_eq!(vec.len(), expected_len);
}
#[test]
fn into_vec_roundtrip() {
let region: Region<Func> = Region::new(Func::make(
Symbol(100),
near(Block::make(
Symbol(0),
list([(Symbol(1), Type(2))]),
list([Inst::make(1, Type(0), list([Value::Const(42)]))]),
Term::make_ret(list([Value::Const(99)])),
)),
));
let vec = region.into_vec();
let restored: Region<Func> = Region::from_bytes(&vec).unwrap();
assert_eq!(restored.name, Symbol(100));
assert_eq!(restored.entry.params.len(), 1);
assert_eq!(restored.entry.insts[0].args[0], Value::Const(42));
}
#[test]
fn into_vec_primitive_region() {
let region: Region<u32> = Region::new(42u32);
let vec = region.into_vec();
let restored: Region<u32> = Region::from_bytes(&vec).unwrap();
assert_eq!(*restored, 42);
assert_eq!(vec, 42u32.to_ne_bytes());
}
#[test]
fn as_ref_u8_slice() {
let region = build_simple_func(5);
let as_ref: &[u8] = region.as_ref();
assert!(core::ptr::eq(as_ref, region.as_bytes()));
assert_eq!(as_ref.len(), region.byte_len());
}
#[test]
fn as_ref_works_with_generic_api() {
fn byte_len(data: &impl AsRef<[u8]>) -> usize {
data.as_ref().len()
}
let region = build_simple_func(1);
let len = byte_len(®ion);
assert_eq!(len, region.byte_len());
}
#[test]
fn from_region_for_vec() {
let region = build_simple_func(3);
let expected_len = region.byte_len();
let vec: Vec<u8> = region.into();
assert_eq!(vec.len(), expected_len);
let restored: Region<Func> = Region::from_bytes(&vec).unwrap();
assert_eq!(restored.name, Symbol(3));
}
#[test]
fn into_vec_after_mutation_and_trim() {
let mut region = build_simple_func(1);
region.session(|s| {
let name = s.nav(s.root(), |f| &f.name);
s.set(name, Symbol(99));
});
region.trim();
let vec = region.into_vec();
let restored: Region<Func> = Region::from_bytes(&vec).unwrap();
assert_eq!(restored.name, Symbol(99));
}
#[test]
fn into_buf_fixed_buf() {
use nearest::{Buf, FixedBuf};
let region: Region<u32, FixedBuf<64>> = Region::new_in(42u32);
let byte_len = region.byte_len();
let buf: FixedBuf<64> = region.into_buf();
assert_eq!(buf.len() as usize, byte_len);
assert_eq!(buf.as_bytes(), &42u32.to_ne_bytes());
}
#[test]
fn array_primitive_field() {
let region: Region<PrimArray> = Region::new(PrimArray::make(42, [10u32, 20, 30]));
assert_eq!(region.tag, 42);
assert_eq!(region.values, [10, 20, 30]);
}
#[test]
fn array_primitive_field_zeros() {
let region: Region<PrimArray> = Region::new(PrimArray::make(0, [0u32, 0, 0]));
assert_eq!(region.tag, 0);
assert_eq!(region.values, [0, 0, 0]);
}
#[test]
fn array_near_field() {
let region: Region<Brif> = Region::new(Brif::make(
1,
array([
near(Block::make(Symbol(10), empty(), empty(), Term::make_ret(empty()))),
near(Block::make(Symbol(20), empty(), empty(), Term::make_ret(empty()))),
]),
));
assert_eq!(region.cond, 1);
assert_eq!(region.targets[0].name, Symbol(10));
assert_eq!(region.targets[1].name, Symbol(20));
}
#[test]
fn array_near_field_with_contents() {
let region: Region<Brif> = Region::new(Brif::make(
7,
array([
near(Block::make(
Symbol(1),
empty(),
list([Inst::make(1, Type(0), list([Value::Const(42)]))]),
Term::make_ret(list([Value::Const(100)])),
)),
near(Block::make(
Symbol(2),
empty(),
list([Inst::make(2, Type(1), list([Value::Const(99)]))]),
Term::make_ret(list([Value::Const(200)])),
)),
]),
));
assert_eq!(region.cond, 7);
let b0 = &*region.targets[0];
assert_eq!(b0.name, Symbol(1));
assert_eq!(b0.insts.len(), 1);
match &b0.term {
Term::Ret { values } => assert_eq!(values[0], Value::Const(100)),
Term::Jmp(_) => panic!("expected Ret"),
}
let b1 = &*region.targets[1];
assert_eq!(b1.name, Symbol(2));
assert_eq!(b1.insts.len(), 1);
match &b1.term {
Term::Ret { values } => assert_eq!(values[0], Value::Const(200)),
Term::Jmp(_) => panic!("expected Ret"),
}
}
#[test]
fn array_clone_region() {
let region: Region<Brif> = Region::new(Brif::make(
3,
array([
near(Block::make(Symbol(10), empty(), empty(), Term::make_ret(empty()))),
near(Block::make(Symbol(20), empty(), empty(), Term::make_ret(empty()))),
]),
));
let cloned = region.clone();
assert_eq!(region.cond, cloned.cond);
assert_eq!(cloned.targets[0].name, Symbol(10));
assert_eq!(cloned.targets[1].name, Symbol(20));
}