use crate::align::DEFAULT_ALIGNMENT;
use crate::record::HEADER_SIZE;
pub(crate) const DWB_CAPACITY: usize = 64;
pub(crate) const DWB_SLOT_RECORD_MAX: usize = 64 * 1024;
const DWB_SLOT_MAGIC: u32 = 0x4457_5342;
pub(crate) const SLOT_PREFIX_SIZE: usize = 20;
const DWB_SLOT_RAW: usize = SLOT_PREFIX_SIZE + HEADER_SIZE + DWB_SLOT_RECORD_MAX;
pub(crate) const DWB_SLOT_STRIDE: usize = round_up_const(DWB_SLOT_RAW, DEFAULT_ALIGNMENT);
pub(crate) const DWB_HEADER_STRIDE: usize = DEFAULT_ALIGNMENT;
pub(crate) const DWB_HEADER_FIELDS: usize = 12;
pub(crate) const DWB_MAGIC: u32 = 0x4457_4246;
pub(crate) const fn round_up_const(value: usize, align: usize) -> usize {
(value + align - 1) & !(align - 1)
}
pub const fn slot_stride() -> usize {
DWB_SLOT_STRIDE
}
pub const fn slot_record_max() -> usize {
DWB_SLOT_RECORD_MAX
}
pub(crate) fn slot_offset(idx: u32) -> u64 {
DWB_HEADER_STRIDE as u64 + (idx as u64 % DWB_CAPACITY as u64) * DWB_SLOT_STRIDE as u64
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct SlotPrefix {
pub seq: u64,
pub total_size: usize,
}
impl SlotPrefix {
pub(crate) fn encode(&self) -> [u8; SLOT_PREFIX_SIZE] {
let mut buf = [0u8; SLOT_PREFIX_SIZE];
buf[0..4].copy_from_slice(&DWB_SLOT_MAGIC.to_le_bytes());
buf[4..12].copy_from_slice(&self.seq.to_le_bytes());
buf[12..16].copy_from_slice(&(self.total_size as u32).to_le_bytes());
let crc = crc32c::crc32c(&buf[0..16]);
buf[16..20].copy_from_slice(&crc.to_le_bytes());
buf
}
pub(crate) fn decode(bytes: &[u8]) -> Option<Self> {
if bytes.len() < SLOT_PREFIX_SIZE {
return None;
}
let mut arr4 = [0u8; 4];
arr4.copy_from_slice(&bytes[0..4]);
if u32::from_le_bytes(arr4) != DWB_SLOT_MAGIC {
return None;
}
arr4.copy_from_slice(&bytes[16..20]);
if u32::from_le_bytes(arr4) != crc32c::crc32c(&bytes[0..16]) {
return None;
}
let mut arr8 = [0u8; 8];
arr8.copy_from_slice(&bytes[4..12]);
let seq = u64::from_le_bytes(arr8);
arr4.copy_from_slice(&bytes[12..16]);
let total_size = u32::from_le_bytes(arr4) as usize;
if !(HEADER_SIZE..=DWB_SLOT_RECORD_MAX).contains(&total_size) {
return None;
}
Some(Self { seq, total_size })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::align::is_aligned;
#[test]
fn slot_stride_is_o_direct_aligned() {
assert!(
is_aligned(DWB_SLOT_STRIDE, DEFAULT_ALIGNMENT),
"DWB slot stride {DWB_SLOT_STRIDE} bytes is not a multiple of {DEFAULT_ALIGNMENT}"
);
assert!(is_aligned(DWB_HEADER_STRIDE, DEFAULT_ALIGNMENT));
for i in 0..DWB_CAPACITY as u32 {
assert!(is_aligned(slot_offset(i) as usize, DEFAULT_ALIGNMENT));
}
}
#[test]
fn prefix_round_trips() {
let prefix = SlotPrefix {
seq: 0x0102_0304_0506_0708,
total_size: HEADER_SIZE + 11,
};
assert_eq!(SlotPrefix::decode(&prefix.encode()), Some(prefix));
}
#[test]
fn torn_prefix_is_rejected() {
let prefix = SlotPrefix {
seq: 9,
total_size: HEADER_SIZE,
};
let mut bytes = prefix.encode();
bytes[6] ^= 0xff;
assert_eq!(SlotPrefix::decode(&bytes), None);
}
#[test]
fn pre_sequence_number_slot_fails_closed() {
let mut bytes = [0u8; SLOT_PREFIX_SIZE];
bytes[0..4].copy_from_slice(&((HEADER_SIZE + 32) as u32).to_le_bytes());
assert_eq!(SlotPrefix::decode(&bytes), None);
}
}