#[cfg(all(feature = "set", feature = "atomic"))]
use bstack::BStack;
#[cfg(all(feature = "set", feature = "atomic"))]
use std::io;
#[cfg(all(feature = "set", feature = "atomic"))]
use std::sync::{Arc, Barrier};
#[cfg(all(feature = "set", feature = "atomic"))]
use std::thread;
#[cfg(all(feature = "set", feature = "atomic"))]
const BLOCK_SIZE: usize = 64;
#[cfg(all(feature = "set", feature = "atomic"))]
const PAYLOAD_OFFSET: u64 = 8;
#[cfg(all(feature = "set", feature = "atomic"))]
const PAYLOAD_SIZE: usize = 56;
#[cfg(all(feature = "set", feature = "atomic"))]
struct ChecksummedBlock {
stack: Arc<BStack>,
cached_checksum: Option<[u8; 8]>,
cached_payload: [u8; PAYLOAD_SIZE],
}
#[cfg(all(feature = "set", feature = "atomic"))]
impl ChecksummedBlock {
fn open(path: &str) -> io::Result<Arc<BStack>> {
let _ = std::fs::remove_file(path);
let stack = BStack::open(path)?;
stack.push(&[0u8; BLOCK_SIZE])?;
Ok(Arc::new(stack))
}
fn new(stack: Arc<BStack>) -> Self {
Self {
stack,
cached_checksum: None,
cached_payload: [0u8; PAYLOAD_SIZE],
}
}
fn compute_checksum(payload: &[u8]) -> u64 {
payload.iter().fold(0u64, |acc, &byte| acc ^ (byte as u64))
}
fn write(&mut self, new_payload: &[u8]) -> io::Result<bool> {
assert!(new_payload.len() <= PAYLOAD_SIZE);
let new_checksum = Self::compute_checksum(new_payload);
let mut block = [0u8; BLOCK_SIZE];
block[0..8].copy_from_slice(&new_checksum.to_le_bytes());
block[PAYLOAD_OFFSET as usize..PAYLOAD_OFFSET as usize + new_payload.len()]
.copy_from_slice(new_payload);
let expected_checksum = self.cached_checksum.unwrap_or([0u8; 8]);
if self
.stack
.eq_crds(0, &expected_checksum, 0, block)?
.is_some()
{
self.cached_checksum = Some(new_checksum.to_le_bytes());
self.cached_payload[..new_payload.len()].copy_from_slice(new_payload);
self.cached_payload[new_payload.len()..].fill(0);
return Ok(true);
}
let mut disk = [0u8; BLOCK_SIZE];
self.stack.get_into(0, &mut disk)?;
self.cached_checksum = Some(disk[0..8].try_into().unwrap());
self.cached_payload
.copy_from_slice(&disk[PAYLOAD_OFFSET as usize..]);
Ok(false)
}
fn read(&mut self) -> io::Result<&[u8]> {
let mut need_checksum_read = true;
let mut read_checksum = [0u8; 8];
self.stack.get_batched_gen(|| {
if need_checksum_read {
need_checksum_read = false;
Some((
0,
unsafe { std::slice::from_raw_parts_mut(read_checksum.as_mut_ptr(), 8) },
))
} else {
if let Some(old_checksum) = self.cached_checksum
&& read_checksum == old_checksum
{
return None;
}
self.cached_checksum = Some(read_checksum);
Some((
PAYLOAD_OFFSET,
unsafe {
std::slice::from_raw_parts_mut(
self.cached_payload.as_mut_ptr(),
PAYLOAD_SIZE,
)
},
))
}
})?;
Ok(&self.cached_payload)
}
}
#[cfg(all(feature = "set", feature = "atomic"))]
fn trim(payload: &[u8]) -> &str {
let len = payload.iter().rposition(|&b| b != 0).map_or(0, |i| i + 1);
std::str::from_utf8(&payload[..len]).unwrap_or("")
}
#[cfg(all(feature = "set", feature = "atomic"))]
fn main() -> io::Result<()> {
let stack = ChecksummedBlock::open("checksummed_cache_example.bstack")?;
let barrier = Arc::new(Barrier::new(2));
let stack1 = Arc::clone(&stack);
let barrier1 = Arc::clone(&barrier);
let t1 = thread::spawn(move || -> io::Result<()> {
let mut block = ChecksummedBlock::new(stack1);
assert!(block.write(b"Written by thread 1.")?);
println!("[t1] write → committed");
let payload = block.read()?;
println!("[t1] read → hit {:?}", trim(payload));
barrier1.wait(); Ok(())
});
let stack2 = Arc::clone(&stack);
let barrier2 = Arc::clone(&barrier);
let t2 = thread::spawn(move || -> io::Result<()> {
let mut block = ChecksummedBlock::new(stack2);
barrier2.wait();
let committed = block.write(b"Written by thread 2.")?;
println!("[t2] write (stale) → committed={committed}, cache refreshed");
let committed = block.write(b"Written by thread 2.")?;
println!("[t2] write (retry) → committed={committed}");
let payload = block.read()?;
println!("[t2] read → hit {:?}", trim(payload));
Ok(())
});
t1.join().unwrap()?;
t2.join().unwrap()?;
Ok(())
}
#[cfg(not(all(feature = "set", feature = "atomic")))]
fn main() {
eprintln!("This example requires the 'set' and 'atomic' features.");
eprintln!("Run: cargo run --example checksummed_cache --features \"set,atomic\"");
}