#![cfg(feature = "secondary-indexes")]
use core::num::NonZeroU32;
use pipecircuit::storage::warp_table::{
Capacity, OrderedRowLimit, OrderedSelectionFirstCuttingTiesOutcome,
OrderedSelectionFirstKeepingTiesOutcome, WarpTable, WarpTableInsertWithIdOutcome, WarpTableRow,
};
#[global_allocator]
static ALLOCATOR: dhat::Alloc = dhat::Alloc;
#[derive(WarpTableRow)]
#[index(primary = u32)]
struct Unit {
#[index(secondary)]
team: u8,
#[index(range)]
health: u32,
#[index(range)]
x: u32,
}
#[test]
fn fluent_queries_reuse_reserved_storage_from_the_first_operation() -> Result<(), &'static str> {
let mut tables = Vec::new();
for capacity in [65_u32, 4097, 65537, 100000] {
let mut table =
WarpTable::<Unit>::try_new(Capacity::new(NonZeroU32::new(capacity).ok_or("capacity")?))
.map_err(|_| "table")?;
for id in 0..capacity {
assert!(matches!(
table.insert_with_id(
id,
Unit {
team: (id % 3) as u8,
health: id % 17,
x: id
}
),
WarpTableInsertWithIdOutcome::Inserted { .. }
));
}
table.reserve_queries(3).map_err(|_| "reserve")?;
tables.push(table);
}
let ten = OrderedRowLimit::new(NonZeroU32::new(10).ok_or("limit")?);
let one = OrderedRowLimit::new(NonZeroU32::MIN);
let mut unused =
WarpTable::<Unit>::try_new(Capacity::new(NonZeroU32::MIN)).map_err(|_| "unused table")?;
let _profiler = dhat::Profiler::builder().testing().build();
unused.reserve_queries(0).map_err(|_| "zero reservation")?;
assert_eq!(unused.query().find_team(1).len(), 0);
for table in &tables {
for _ in 0..4 {
let held = table.query().find_x_in(1..20).map_err(|_| "held")?;
let result = table
.query()
.find_x_in(..)
.map_err(|_| "range")?
.and(|q| q.find_x_in(3..))
.map_err(|_| "intersection")?
.and_not(|q| q.find_team(2))
.xor(|q| q.find_x_in(20..30))
.map_err(|_| "xor")?
.ordered_by(|q| q.health_largest_first().then(q.x_smallest_first()))
.first_cutting_ties(ten)
.map_err(|_| "rank")?;
let rows = match result {
OrderedSelectionFirstCuttingTiesOutcome::Exact { rows }
| OrderedSelectionFirstCuttingTiesOutcome::TiesCut { rows, .. } => rows,
};
let kept = table
.query()
.ordered_by(|q| q.health_largest_first())
.first_keeping_ties(one)
.map_err(|_| "ties")?;
match kept {
OrderedSelectionFirstKeepingTiesOutcome::Exact { rows } => {
assert_eq!(rows.count(), 1)
}
OrderedSelectionFirstKeepingTiesOutcome::TiesKept { leaders, tied } => {
assert_eq!(leaders.count(), 0);
let replacement = table
.query()
.find_x_in(..3)
.map_err(|_| "small")?
.ordered_by(|q| q.x_largest_first())
.first_cutting_ties(one)
.map_err(|_| "small rank")?;
match replacement {
OrderedSelectionFirstCuttingTiesOutcome::Exact { rows }
| OrderedSelectionFirstCuttingTiesOutcome::TiesCut { rows, .. } => {
assert_eq!(rows.count(), 1)
}
}
assert!(tied.count() > 1);
}
}
assert_eq!(rows.count(), 10);
assert_eq!(
held.rows().fold(0_u32, |count, _| count.saturating_add(1)),
19
);
let compared = table
.query()
.find_team(1)
.ordered_by(|q| q.health_largest_first())
.then_compare_rows(|left, right| left.x.cmp(right.x))
.first_keeping_ties(ten)
.map_err(|_| "compared")?;
match compared {
OrderedSelectionFirstKeepingTiesOutcome::Exact { rows } => {
assert_eq!(rows.count(), 10)
}
OrderedSelectionFirstKeepingTiesOutcome::TiesKept { .. } => {
return Err("unexpected ties");
}
}
assert!(
table
.query()
.find_x_in(..)
.map_err(|_| "range")?
.and(|q| q
.find_x_in((core::ops::Bound::Included(2), core::ops::Bound::Included(1))))
.is_err()
);
}
}
let stats = dhat::HeapStats::get();
assert_eq!(stats.total_blocks, 0);
assert_eq!(stats.total_bytes, 0);
let table = tables.first().ok_or("table missing")?;
for repetition in 0..2 {
let before = dhat::HeapStats::get();
let held = [
table.query().find_x_in(..).map_err(|_| "growth")?,
table.query().find_x_in(..).map_err(|_| "growth")?,
table.query().find_x_in(..).map_err(|_| "growth")?,
table.query().find_x_in(..).map_err(|_| "growth")?,
];
for selection in held {
assert_eq!(selection.len(), 65);
}
let after = dhat::HeapStats::get();
if repetition == 0 {
assert!(after.total_blocks > before.total_blocks);
} else {
assert_eq!(after.total_blocks, before.total_blocks);
assert_eq!(after.total_bytes, before.total_bytes);
}
}
Ok(())
}