use crate::error::{Result, WalError};
pub const ANCHOR_PAYLOAD_SIZE: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LsnMsAnchorPayload {
pub lsn: u64,
pub wall_ms: i64,
}
impl LsnMsAnchorPayload {
pub const fn new(lsn: u64, wall_ms: i64) -> Self {
Self { lsn, wall_ms }
}
pub fn to_bytes(&self) -> [u8; ANCHOR_PAYLOAD_SIZE] {
let mut buf = [0u8; ANCHOR_PAYLOAD_SIZE];
buf[0..8].copy_from_slice(&self.lsn.to_le_bytes());
buf[8..16].copy_from_slice(&self.wall_ms.to_le_bytes());
buf
}
pub fn from_bytes(buf: &[u8]) -> Result<Self> {
if buf.len() != ANCHOR_PAYLOAD_SIZE {
return Err(WalError::InvalidPayload {
detail: format!(
"LsnMsAnchor payload must be {ANCHOR_PAYLOAD_SIZE} bytes, got {}",
buf.len()
),
});
}
let lsn = u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]);
let wall_ms = i64::from_le_bytes([
buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15],
]);
Ok(Self { lsn, wall_ms })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn anchor_roundtrip() {
let anchor = LsnMsAnchorPayload::new(12_345, 1_700_000_000_000);
let bytes = anchor.to_bytes();
assert_eq!(LsnMsAnchorPayload::from_bytes(&bytes).unwrap(), anchor);
}
#[test]
fn anchor_negative_wall_ms() {
let anchor = LsnMsAnchorPayload::new(0, -1);
let bytes = anchor.to_bytes();
assert_eq!(LsnMsAnchorPayload::from_bytes(&bytes).unwrap(), anchor);
}
#[test]
fn anchor_wrong_size_rejected() {
assert!(LsnMsAnchorPayload::from_bytes(&[0u8; 15]).is_err());
assert!(LsnMsAnchorPayload::from_bytes(&[0u8; 17]).is_err());
}
}