use core::mem::forget;
use layout::{bitpack::PackedArray, Compact, CompactVec, SOA};
#[derive(SOA)]
pub struct Item {
pub flag: Compact<bool>,
pub name: String,
}
fn build_items(n: usize) -> ItemVec {
let mut v = ItemVec::new();
for i in 0..n {
v.push(Item {
flag: Compact::new(i % 2 == 0),
name: format!("s{i}"),
});
}
v
}
#[test]
#[cfg_attr(miri, ignore = "leaks drained Strings by design")]
fn leaked_composite_drain_keeps_columns_consistent() {
let mut v = build_items(4);
forget(v.drain(1..3));
assert_eq!(v.flag.len(), 1);
assert_eq!(v.name.len(), 1);
assert_eq!(v.len(), 1);
assert_eq!(v.get(0).unwrap().name, "s0");
assert!(v.get(1).is_none());
v.push(Item {
flag: Compact::new(false),
name: "new".into(),
});
assert_eq!(v.len(), 2);
assert!(!v.get(1).unwrap().flag.get());
assert_eq!(v.get(1).unwrap().name, "new");
}
#[test]
#[cfg_attr(miri, ignore = "leaks the tail Strings by design")]
fn leaked_empty_range_drain_matches_vec_semantics() {
let mut v = build_items(5);
forget(v.drain(2..2));
assert_eq!(v.len(), 2);
assert_eq!(v.flag.len(), 2);
assert_eq!(v.name.len(), 2);
}
#[test]
fn leaked_compact_drain_shortens_column() {
let mut cv: CompactVec<bool> =
(0..10).map(|i| Compact::new(i % 3 == 0)).collect();
forget(cv.drain(3..7));
assert_eq!(cv.len(), 3);
for i in 0..3 {
assert_eq!(cv.get(i).unwrap().get(), i % 3 == 0, "at {i}");
}
cv.push(Compact::new(false));
cv.push(Compact::new(true));
assert_eq!(cv.len(), 5);
assert!(!cv.get(3).unwrap().get());
assert!(cv.get(4).unwrap().get());
}
#[test]
fn leaked_word_aligned_drain_then_bulk_ops() {
let n = 200;
let mut cv: CompactVec<bool> =
(0..n).map(|i| Compact::new(i % 3 == 0)).collect();
forget(cv.drain(64..n));
assert_eq!(cv.len(), 64);
let mut other: CompactVec<bool> =
(0..130).map(|i| Compact::new(i % 5 == 0)).collect();
cv.append(&mut other);
assert_eq!(cv.len(), 194);
for i in 0..64 {
assert_eq!(cv.get(i).unwrap().get(), i % 3 == 0, "kept at {i}");
}
for i in 0..130 {
assert_eq!(
cv.get(64 + i).unwrap().get(),
i % 5 == 0,
"appended at {i}"
);
}
let mut cv2: CompactVec<bool> =
(0..n).map(|_| Compact::new(true)).collect();
forget(cv2.drain(64..n));
cv2.resize(300, Compact::new(false));
assert_eq!(cv2.len(), 300);
for i in 0..64 {
assert!(cv2.get(i).unwrap().get(), "kept at {i}");
}
for i in 64..300 {
assert!(!cv2.get(i).unwrap().get(), "filled at {i}");
}
}
#[test]
fn completed_drain_still_shifts_and_tightens() {
let mut cv: CompactVec<bool> =
(0..150).map(|i| Compact::new(i % 2 == 0)).collect();
let drained: Vec<bool> = cv.drain(10..100).map(|c| c.get()).collect();
assert_eq!(drained.len(), 90);
for (i, &d) in drained.iter().enumerate() {
assert_eq!(d, (10 + i) % 2 == 0, "drained at {i}");
}
assert_eq!(cv.len(), 60);
for i in 0..10 {
assert_eq!(cv.get(i).unwrap().get(), i % 2 == 0, "head at {i}");
}
for i in 10..60 {
assert_eq!(cv.get(i).unwrap().get(), (i + 90) % 2 == 0, "tail at {i}");
}
let mut other: CompactVec<bool> =
(0..64).map(|_| Compact::new(true)).collect();
let mut base = CompactVec::<bool>::new();
base.append(&mut other);
assert_eq!(base.len(), 64);
assert!(base.iter().all(|c| c.get()));
}
#[test]
#[cfg_attr(miri, ignore = "leaks drained Strings by design")]
fn partially_consumed_leaked_drain_is_consistent() {
let mut v = build_items(6);
let mut d = v.drain(1..5);
assert_eq!(d.next().unwrap().name, "s1");
assert_eq!(d.next_back().unwrap().name, "s4");
forget(d);
assert_eq!(v.len(), 1);
assert_eq!(v.flag.len(), 1);
assert_eq!(v.name.len(), 1);
assert_eq!(v.get(0).unwrap().name, "s0");
}
#[derive(SOA)]
pub struct CopyItem {
pub flag: Compact<bool>,
pub id: u32,
}
fn build_copy_items(n: usize) -> CopyItemVec {
let mut v = CopyItemVec::new();
for i in 0..n {
v.push(CopyItem {
flag: Compact::new(i % 2 == 0),
id: i as u32,
});
}
v
}
#[test]
fn leaked_composite_drain_consistent_copy_columns() {
let mut v = build_copy_items(4);
forget(v.drain(1..3));
assert_eq!(v.flag.len(), 1);
assert_eq!(v.id.len(), 1);
assert_eq!(v.len(), 1);
assert_eq!(*v.get(0).unwrap().id, 0);
assert!(v.get(1).is_none());
v.push(CopyItem {
flag: Compact::new(false),
id: 9,
});
assert_eq!(v.len(), 2);
assert!(!v.get(1).unwrap().flag.get());
assert_eq!(*v.get(1).unwrap().id, 9);
}
#[test]
fn leaked_empty_range_drain_copy_columns() {
let mut v = build_copy_items(5);
forget(v.drain(2..2));
assert_eq!(v.len(), 2);
assert_eq!(v.flag.len(), 2);
assert_eq!(v.id.len(), 2);
}
#[test]
fn partially_consumed_leaked_drain_copy_columns() {
let mut v = build_copy_items(6);
let mut d = v.drain(1..5);
assert_eq!(d.next().unwrap().id, 1);
assert_eq!(d.next_back().unwrap().id, 4);
forget(d);
assert_eq!(v.len(), 1);
assert_eq!(v.flag.len(), 1);
assert_eq!(v.id.len(), 1);
assert_eq!(*v.get(0).unwrap().id, 0);
}
#[test]
fn packed_array_set_len_interplay() {
let mut a = PackedArray::<1>::new();
for i in 0..200 {
a.push(i % 2);
}
unsafe { a.set_len(5) };
assert_eq!(a.len(), 5);
for i in 0..5 {
assert_eq!(a.get(i), i % 2, "kept at {i}");
}
a.push(0);
a.push(0);
assert_eq!(a.get(5), 0);
assert_eq!(a.get(6), 0);
assert_eq!(a.pop(), Some(0));
let mut b = PackedArray::<1>::new();
b.push(1);
a.append(&mut b);
assert_eq!(a.len(), 7);
assert_eq!(a.get(6), 1);
}