use std::time::{Duration, Instant};
use crate::backend::Storage;
use crate::error::{Error, Result};
pub(crate) const MAGIC: u32 = 0x53484d52; pub(crate) const FORMAT_VERSION: u32 = 1;
pub(crate) const HEADER_SIZE: u64 = 64;
pub(crate) const OFF_MAGIC: u64 = 0;
pub(crate) const OFF_VERSION: u64 = 4;
pub(crate) const OFF_CAPACITY: u64 = 8;
pub(crate) const OFF_HEAD: u64 = 16;
pub(crate) const OFF_TAIL: u64 = 24;
pub(crate) const OFF_CLOSED: u64 = 32;
pub(crate) const MAX_CAPACITY: u64 = 1 << 31;
pub(crate) fn validate_capacity(capacity: u64) -> Result<()> {
if capacity == 0 || capacity > MAX_CAPACITY || capacity & (capacity - 1) != 0 {
return Err(Error::InvalidCapacity);
}
Ok(())
}
pub(crate) fn init_header<S: Storage>(st: &S, capacity: u64) -> Result<()> {
let mut buf = [0u8; HEADER_SIZE as usize];
buf[OFF_MAGIC as usize..OFF_MAGIC as usize + 4].copy_from_slice(&MAGIC.to_le_bytes());
buf[OFF_VERSION as usize..OFF_VERSION as usize + 4]
.copy_from_slice(&FORMAT_VERSION.to_le_bytes());
buf[OFF_CAPACITY as usize..OFF_CAPACITY as usize + 8].copy_from_slice(&capacity.to_le_bytes());
buf[OFF_HEAD as usize..OFF_HEAD as usize + 4].copy_from_slice(&0u32.to_le_bytes());
buf[OFF_TAIL as usize..OFF_TAIL as usize + 4].copy_from_slice(&0u32.to_le_bytes());
buf[OFF_CLOSED as usize..OFF_CLOSED as usize + 4].copy_from_slice(&0u32.to_le_bytes());
st.write_at(&buf, 0)
}
pub(crate) fn verify_header<S: Storage>(st: &S, capacity: u64) -> Result<()> {
let mut buf = [0u8; HEADER_SIZE as usize];
st.read_at(&mut buf, 0)?;
if u32::from_le_bytes(
buf[OFF_MAGIC as usize..OFF_MAGIC as usize + 4]
.try_into()
.unwrap(),
) != MAGIC
{
return Err(Error::HeaderMismatch);
}
if u32::from_le_bytes(
buf[OFF_VERSION as usize..OFF_VERSION as usize + 4]
.try_into()
.unwrap(),
) != FORMAT_VERSION
{
return Err(Error::HeaderMismatch);
}
if u64::from_le_bytes(
buf[OFF_CAPACITY as usize..OFF_CAPACITY as usize + 8]
.try_into()
.unwrap(),
) != capacity
{
return Err(Error::HeaderMismatch);
}
Ok(())
}
pub(crate) fn read_u32_at<S: Storage>(st: &S, off: u64) -> Result<u32> {
st.load_u32_at(off)
}
pub(crate) fn write_u32_at<S: Storage>(st: &S, off: u64, v: u32) -> Result<()> {
st.store_u32_at(off, v)
}
pub(crate) fn wait_slice(deadline: Option<Instant>) -> Option<Duration> {
deadline.map(|d| d.saturating_duration_since(Instant::now()))
}