use rudb_common::Result;
use super::{Cursor, invalid, put_u32, put_u64};
pub(crate) const LOG_ANCHOR: &[u8; 8] = b"RUDBWL1\0";
const VERSION: u8 = 1;
const MAX_LANES: usize = 64;
const MAX_VOIDS: usize = 1 << 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct LaneStart {
pub sequence: u64,
pub offset: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LogAnchor {
pub database: u64,
pub durable: u64,
pub lanes: Vec<LaneStart>,
pub voids: Vec<u64>,
}
impl LogAnchor {
#[must_use]
pub fn replays(&self, ts: u64) -> bool {
ts > self.durable && self.voids.binary_search(&ts).is_err()
}
pub(crate) fn encode(&self, out: &mut Vec<u8>) -> Result<()> {
if self.lanes.len() > MAX_LANES || self.voids.len() > MAX_VOIDS {
return Err(invalid("log anchor holds more lanes or voids than it can"));
}
out.extend_from_slice(LOG_ANCHOR);
out.push(VERSION);
put_u64(out, self.database);
put_u64(out, self.durable);
out.push(self.lanes.len() as u8);
for lane in &self.lanes {
put_u64(out, lane.sequence);
put_u64(out, lane.offset);
}
put_u32(out, self.voids.len() as u32);
for &ts in &self.voids {
put_u64(out, ts);
}
Ok(())
}
pub(crate) fn decode(cur: &mut Cursor<'_>) -> Result<Self> {
if cur.u8()? != VERSION {
return Err(invalid("log anchor version differs"));
}
let database = cur.u64()?;
let durable = cur.u64()?;
let count = cur.u8()? as usize;
if count > MAX_LANES {
return Err(invalid("log anchor names more lanes than a log has"));
}
let mut lanes = Vec::with_capacity(count);
for _ in 0..count {
lanes.push(LaneStart { sequence: cur.u64()?, offset: cur.u64()? });
}
let count = cur.u32()? as usize;
if count > MAX_VOIDS {
return Err(invalid("log anchor holds more voids than it can"));
}
let mut voids = Vec::with_capacity(count);
for _ in 0..count {
voids.push(cur.u64()?);
}
if voids.windows(2).any(|pair| pair[0] >= pair[1])
|| voids.first().is_some_and(|&ts| ts <= durable)
{
return Err(invalid("log anchor voids are out of order or under the cut"));
}
Ok(Self { database, durable, lanes, voids })
}
}