use crate::error::{Result, WalError};
pub const SYNC_SEQ_ADVANCE_PAYLOAD_SIZE: usize = 32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SyncSeqAdvancePayload {
pub producer_id: u64,
pub epoch: u64,
pub stream_id: u64,
pub seq: u64,
}
impl SyncSeqAdvancePayload {
pub const fn new(producer_id: u64, epoch: u64, stream_id: u64, seq: u64) -> Self {
Self {
producer_id,
epoch,
stream_id,
seq,
}
}
pub fn to_bytes(&self) -> [u8; SYNC_SEQ_ADVANCE_PAYLOAD_SIZE] {
let mut buf = [0u8; SYNC_SEQ_ADVANCE_PAYLOAD_SIZE];
buf[0..8].copy_from_slice(&self.producer_id.to_le_bytes());
buf[8..16].copy_from_slice(&self.epoch.to_le_bytes());
buf[16..24].copy_from_slice(&self.stream_id.to_le_bytes());
buf[24..32].copy_from_slice(&self.seq.to_le_bytes());
buf
}
pub fn from_bytes(buf: &[u8]) -> Result<Self> {
if buf.len() != SYNC_SEQ_ADVANCE_PAYLOAD_SIZE {
return Err(WalError::InvalidPayload {
detail: format!(
"SyncSeqAdvance payload must be {SYNC_SEQ_ADVANCE_PAYLOAD_SIZE} bytes, got {}",
buf.len()
),
});
}
let producer_id = u64::from_le_bytes(
buf[0..8]
.try_into()
.expect("invariant: bounds-checked above (buf.len() == 32)"),
);
let epoch = u64::from_le_bytes(
buf[8..16]
.try_into()
.expect("invariant: bounds-checked above (buf.len() == 32)"),
);
let stream_id = u64::from_le_bytes(
buf[16..24]
.try_into()
.expect("invariant: bounds-checked above (buf.len() == 32)"),
);
let seq = u64::from_le_bytes(
buf[24..32]
.try_into()
.expect("invariant: bounds-checked above (buf.len() == 32)"),
);
Ok(Self {
producer_id,
epoch,
stream_id,
seq,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sync_seq_advance_roundtrip() {
let p = SyncSeqAdvancePayload::new(0xCAFE_BABE_DEAD_BEEF, 7, 42, 1_000_000);
let bytes = p.to_bytes();
assert_eq!(SyncSeqAdvancePayload::from_bytes(&bytes).unwrap(), p);
}
#[test]
fn sync_seq_advance_zero() {
let p = SyncSeqAdvancePayload::new(0, 0, 0, 0);
assert_eq!(SyncSeqAdvancePayload::from_bytes(&p.to_bytes()).unwrap(), p);
}
#[test]
fn sync_seq_advance_max() {
let p = SyncSeqAdvancePayload::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX);
assert_eq!(SyncSeqAdvancePayload::from_bytes(&p.to_bytes()).unwrap(), p);
}
#[test]
fn sync_seq_advance_wrong_size_rejected() {
assert!(SyncSeqAdvancePayload::from_bytes(&[0u8; 31]).is_err());
assert!(SyncSeqAdvancePayload::from_bytes(&[0u8; 33]).is_err());
}
#[test]
fn sync_seq_advance_payload_size_constant() {
assert_eq!(
core::mem::size_of::<u64>() * 4,
SYNC_SEQ_ADVANCE_PAYLOAD_SIZE
);
}
}