#![cfg(not(loom))]
use tagged_index_stack::{ArrayIndexStack, ArrayLinks, StackHead, TaggedIndex, TAIL};
const _: () = {
const fn assert_send_sync<T: Send + Sync>() {}
const fn _check() {
assert_send_sync::<StackHead<16>>();
assert_send_sync::<ArrayIndexStack<16, 4>>();
assert_send_sync::<ArrayLinks<4>>();
}
_check();
assert!(TaggedIndex::<16>::INDEX_MASK == 0xFFFF);
assert!(TaggedIndex::<16>::INDEX_MASK != TAIL as u64);
};
#[test]
fn pack_rejects_out_of_range_halves_and_accepts_the_full_index_range() {
type T = TaggedIndex<16>;
for &(idx, tag, word, empty) in &[
(0u32, 0u64, 0u64, false),
(1, 1, (1u64 << 16) | 1, false),
(2748, 42, (42u64 << 16) | 2748, false),
(
0xFFFE,
(1u64 << T::TAG_BITS) - 1,
(((1u64 << T::TAG_BITS) - 1) << 16) | 0xFFFE,
false,
),
(T::INDEX_MASK as u32, 0, T::INDEX_MASK, true),
(T::INDEX_MASK as u32, 1, (1u64 << 16) | T::INDEX_MASK, true),
(
T::INDEX_MASK as u32,
42,
(42u64 << 16) | T::INDEX_MASK,
true,
),
(
T::INDEX_MASK as u32,
99,
(99u64 << 16) | T::INDEX_MASK,
true,
),
(T::INDEX_MASK as u32, 7, (7u64 << 16) | T::INDEX_MASK, true),
(
T::INDEX_MASK as u32,
(1u64 << T::TAG_BITS) - 1,
(((1u64 << T::TAG_BITS) - 1) << 16) | T::INDEX_MASK,
true,
),
] {
assert_eq!(
T::pack(idx, tag),
Some(word),
"in-range (index {idx}, tag {tag}) must pack to the exact word"
);
let (unpacked_idx, unpacked_tag) = T::unpack(word);
assert_eq!(unpacked_idx, idx, "index {idx} must round-trip");
assert_eq!(unpacked_tag, tag, "tag {tag} must round-trip");
assert_eq!(
T::is_empty(word),
empty,
"empty classification for index {idx}"
);
}
assert_eq!(T::pack(1u32 << 16, 7), None, "first invalid index");
assert_eq!(T::pack(u32::MAX, 7), None, "far out-of-range index");
assert_eq!(
T::pack(0x1_FFFF, 7),
None,
"over-wide index whose low bits are the empty sentinel must be rejected, not masked into it"
);
assert_eq!(T::pack(9, 1u64 << T::TAG_BITS), None, "first invalid tag");
}
#[test]
fn width_12_partitions() {
type T = TaggedIndex<12>;
assert_eq!(T::INDEX_MASK, 0xFFF);
assert_eq!(T::TAG_BITS, 52);
let w = T::pack(0xABC, 7).expect("0xABC < 0xFFF, 7 < 2^52");
let (v, t) = T::unpack(w);
assert_eq!(v, 0xABC);
assert_eq!(t, 7);
assert!(T::is_empty(
T::pack(T::empty_index(), 0).expect("bootstrap empty halves are in range")
));
assert_ne!(T::empty_index(), TAIL);
}
#[test]
fn array_index_stack_accepts_index_mask_capacity_boundary() {
assert_eq!(TaggedIndex::<4>::INDEX_MASK, 15);
let stack = ArrayIndexStack::<4, 15>::new();
unsafe { stack.push(14) }.expect("fresh head has tag budget");
assert_eq!(stack.pop(), Some(14));
let default_stack: ArrayIndexStack<4, 15> = Default::default();
assert!(default_stack.is_empty());
}
#[test]
#[should_panic(expected = "self-loop, corrupting the free-list into a cycle")]
fn double_push_of_current_head_panics_on_first_pop() {
let stack = ArrayIndexStack::<16, 64>::new();
unsafe { stack.push(1) }.expect("fresh head has tag budget");
unsafe { stack.push(1) }.expect("fresh head has tag budget"); let _ = stack.pop(); }
#[test]
fn fresh_stack_is_empty() {
let stack = ArrayIndexStack::<16, 8>::new();
assert_eq!(stack.pop(), None, "a fresh (lazy-link) stack is empty");
}
#[test]
fn push_pop_is_lifo() {
let stack = ArrayIndexStack::<16, 8>::new();
for i in 0..5u32 {
unsafe { stack.push(i) }.expect("fresh head has tag budget");
}
let mut got = Vec::new();
while let Some(i) = stack.pop() {
got.push(i);
}
assert_eq!(got, vec![4, 3, 2, 1, 0], "LIFO order");
assert_eq!(stack.pop(), None);
}
#[test]
fn width_1_stack_push_pop_round_trips_its_sole_index() {
assert_eq!(TaggedIndex::<1>::INDEX_MASK, 1);
let stack = ArrayIndexStack::<1, 1>::new();
assert!(stack.is_empty(), "a fresh (lazy-link) stack is empty");
unsafe { stack.push(0) }.expect("fresh head has tag budget");
assert!(!stack.is_empty(), "the sole index is on the stack");
assert_eq!(stack.pop(), Some(0));
assert!(stack.is_empty(), "drained back to empty");
assert_eq!(stack.pop(), None, "empty stays empty");
}
#[cfg(tagged_index_stack_test)]
#[test]
fn empty_transition_preserves_running_tag() {
type T = TaggedIndex<16>;
let stack = ArrayIndexStack::<16, 4>::new();
unsafe { stack.push(0) }.expect("fresh head has tag budget"); let (_v, tag_after_push1) = T::unpack(stack.raw_head());
assert_eq!(tag_after_push1, 1);
assert_eq!(stack.pop(), Some(0));
let empty_head = stack.raw_head();
assert!(T::is_empty(empty_head), "stack is now empty");
let (_ev, empty_tag) = T::unpack(empty_head);
assert_eq!(
empty_tag, 1,
"the empty transition preserves the running tag (1), not 0 — \
resetting to 0 would reopen ABA"
);
unsafe { stack.push(0) }.expect("fresh head has tag budget");
let (_v2, tag_after_push2) = T::unpack(stack.raw_head());
assert_eq!(
tag_after_push2, 2,
"the tag keeps climbing across empty->non-empty (1 -> 2), never restarts"
);
}
#[cfg(tagged_index_stack_test)]
#[test]
fn links_are_lazy() {
let stack = ArrayIndexStack::<16, 4>::new();
assert_eq!(
stack.load_next_for_test(3),
0,
"a never-pushed index's link is the zero value straight after construction"
);
unsafe { stack.push(0) }.expect("fresh head has tag budget");
assert_eq!(stack.pop(), Some(0));
assert_eq!(
stack.load_next_for_test(3),
0,
"push/pop of other indices never writes a never-pushed index's link"
);
}
#[test]
fn default_array_links_behaves_like_new() {
let default_links = ArrayLinks::<4>::default();
let new_links = ArrayLinks::<4>::new();
for i in 0..4u32 {
assert_eq!(
default_links.load_next(i),
new_links.load_next(i),
"link {i}: Default and New backings read identically"
);
assert_eq!(
default_links.load_next(i),
0,
"link {i}: a fresh backing's links are the zero value (lazy links)"
);
}
}
#[test]
fn default_array_index_stack_behaves_like_new() {
let stack = ArrayIndexStack::<16, 8>::default();
assert!(stack.is_empty(), "a freshly-defaulted stack is empty");
assert_eq!(
stack.pop(),
None,
"Default == new: the lazy-link stack starts empty"
);
unsafe { stack.push(7) }.expect("fresh head has tag budget");
assert!(!stack.is_empty());
assert_eq!(stack.pop(), Some(7));
}
#[cfg(tagged_index_stack_test)]
#[test]
fn default_stack_head_behaves_like_new() {
let default_head = StackHead::<16>::default();
let new_head = StackHead::<16>::new();
assert_eq!(
default_head.raw_head(),
new_head.raw_head(),
"Default and New heads hold the identical packed word"
);
assert_eq!(
default_head.raw_head(),
TaggedIndex::<16>::pack(TaggedIndex::<16>::empty_index(), 0)
.expect("bootstrap empty halves are in range"),
"a freshly-defaulted head IS the documented bootstrap empty sentinel"
);
assert_eq!(
new_head.raw_head(),
TaggedIndex::<16>::pack(TaggedIndex::<16>::empty_index(), 0)
.expect("bootstrap empty halves are in range"),
"a freshly-newed head IS the documented bootstrap empty sentinel"
);
assert!(
default_head.is_empty(),
"a freshly-defaulted head reads empty"
);
assert!(new_head.is_empty(), "a freshly-newed head reads empty");
}
#[cfg(tagged_index_stack_test)]
#[test]
fn with_tag_for_test_accepts_the_exact_tag_max_boundary() {
let head = StackHead::<16>::with_tag_for_test(TaggedIndex::<16>::TAG_MAX);
let (_index, tag) = TaggedIndex::<16>::unpack(head.raw_head());
assert_eq!(
tag,
TaggedIndex::<16>::TAG_MAX,
"TAG_MAX itself is in-range and must round-trip exactly, not truncate"
);
}
#[cfg(tagged_index_stack_test)]
#[test]
#[should_panic(expected = "with_tag_for_test: tag out of range")]
fn with_tag_for_test_panics_instead_of_silently_truncating_an_out_of_range_tag() {
let _ = StackHead::<16>::with_tag_for_test(TaggedIndex::<16>::TAG_MAX + 1);
}