use super::*;
#[derive(Copy, Clone)]
struct Level {
price_ticks: u64,
qty: u32,
}
#[test]
fn per_tick_scratch_resets_without_realloc() {
let mut scratch = Bump::with_capacity(4096);
let cap = scratch.capacity();
let updates = [
(9998u64, 5u32, true),
(9997, 8, true),
(10002, 4, false),
(10003, 9, false),
];
let (mut best_bid, mut best_ask) = (0u64, u64::MAX);
let (mut bid_qty, mut ask_qty) = (0u64, 0u64);
for &(price, qty, is_bid) in &updates {
let level = scratch.alloc_copy(Level {
price_ticks: price,
qty,
});
if is_bid {
best_bid = best_bid.max(level.price_ticks);
bid_qty += level.qty as u64;
} else {
best_ask = best_ask.min(level.price_ticks);
ask_qty += level.qty as u64;
}
}
assert_eq!((best_bid + best_ask) / 2, 10_000, "mid of the tick");
assert_eq!(bid_qty as i64 - ask_qty as i64, 0, "balanced book");
assert!(scratch.used() > 0);
scratch.reset();
assert_eq!(scratch.used(), 0, "reset rewinds the cursor");
assert_eq!(scratch.capacity(), cap, "no reallocation across ticks");
}
#[cfg(feature = "typed")]
#[test]
fn typed_snapshot_reads_back_and_recycles() {
use crate::TypedArena;
let mut book = TypedArena::<Level>::with_capacity(64);
let mut live = Vec::new();
for &(price, qty) in &[(9998u64, 5u32), (9997, 8), (10002, 4), (10003, 9)] {
live.push(book.alloc(Level {
price_ticks: price,
qty,
}));
}
assert_eq!(book.len(), 4);
assert_eq!(live.iter().map(|s| book.get(s).qty as u64).sum::<u64>(), 26);
assert_eq!(
live.iter().map(|s| book.get(s).price_ticks).max(),
Some(10_003)
);
let cancelled = live.pop().expect("a level to cancel");
let freed_index = cancelled.index();
book.free(cancelled);
let replacement = book.alloc(Level {
price_ticks: 10_004,
qty: 2,
});
assert_eq!(replacement.index(), freed_index, "freed slot came back");
assert_eq!(book.reuse_hits(), 1);
assert_eq!(book.len(), 4, "cancel + replace is footprint-neutral");
book.reset();
assert!(book.is_empty());
}
#[cfg(feature = "growable")]
#[test]
fn growable_deep_tick_then_grow_free_steady_state() {
use crate::GrowableBump;
let mut scratch = GrowableBump::with_capacity(256);
for i in 0..200u64 {
scratch.alloc_copy(Level {
price_ticks: 10_000 + i,
qty: 1,
});
}
assert!(scratch.chunk_count() > 1, "deep book forced a grow");
scratch.reset();
assert_eq!(scratch.chunk_count(), 1, "reset keeps the largest chunk");
let cap = scratch.total_capacity();
for i in 0..50u64 {
scratch.alloc_copy(Level {
price_ticks: 10_000 + i,
qty: 1,
});
}
assert_eq!(
scratch.total_capacity(),
cap,
"steady-state tick is grow-free"
);
}
#[cfg(feature = "stats")]
#[test]
fn stats_counters_survive_reset() {
use crate::StatsBump;
let mut scratch = StatsBump::with_capacity(4096);
for _ in 0..1_000 {
for i in 0..8u64 {
scratch.alloc_copy(Level {
price_ticks: 10_000 + i,
qty: 1,
});
}
scratch.reset();
}
let s = scratch.stats();
assert_eq!(s.allocations, 8_000);
assert!(s.peak_bytes > 0);
}
#[cfg(feature = "aligned")]
#[test]
fn aligned_scratch_is_cache_line_aligned() {
use crate::AlignedBump;
let mut scratch = AlignedBump::with_capacity(1024);
let region = scratch.alloc_aligned(64, 64);
assert_eq!(region.as_ptr() as usize % 64, 0);
assert_eq!(region.len(), 64);
scratch.reset();
assert_eq!(scratch.used(), 0);
}