use byteorder::{BigEndian, ReadBytesExt};
use bytes::{BufMut, BytesMut};
use noxu_util::lsn::Lsn;
use std::io::{self, Cursor};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BinDeltaLogEntryError {
#[error("I/O error: {0}")]
Io(#[from] io::Error),
}
#[derive(Debug, Clone)]
pub struct BinDeltaLogEntry {
pub db_id: u64,
pub prev_full_lsn: Lsn,
pub prev_delta_lsn: Lsn,
pub delta_data: Vec<u8>,
}
impl BinDeltaLogEntry {
pub fn new(
db_id: u64,
prev_full_lsn: Lsn,
prev_delta_lsn: Lsn,
delta_data: Vec<u8>,
) -> Self {
Self { db_id, prev_full_lsn, prev_delta_lsn, delta_data }
}
pub fn is_bin_delta(&self) -> bool {
true
}
pub fn log_size(&self) -> usize {
8 + 8 + 8 + 4 + self.delta_data.len() }
pub fn write_to_log(&self, buf: &mut BytesMut) {
buf.put_u64(self.db_id);
buf.put_u64(self.prev_full_lsn.as_u64());
buf.put_u64(self.prev_delta_lsn.as_u64());
buf.put_u32(self.delta_data.len() as u32);
buf.extend_from_slice(&self.delta_data);
}
pub fn read_from_log(buf: &[u8]) -> Result<Self, BinDeltaLogEntryError> {
let mut cursor = Cursor::new(buf);
let db_id = cursor.read_u64::<BigEndian>()?;
let prev_full_lsn = Lsn::from_u64(cursor.read_u64::<BigEndian>()?);
let prev_delta_lsn = Lsn::from_u64(cursor.read_u64::<BigEndian>()?);
let delta_len = cursor.read_u32::<BigEndian>()? as usize;
let mut delta_data = vec![0u8; delta_len];
io::Read::read_exact(&mut cursor, &mut delta_data)?;
Ok(Self { db_id, prev_full_lsn, prev_delta_lsn, delta_data })
}
}
#[cfg(test)]
mod tests {
use super::*;
use noxu_util::NULL_LSN;
#[test]
fn test_bin_delta_log_entry_roundtrip() {
let delta_data = b"fake_serialized_BIN_delta".to_vec();
let entry = BinDeltaLogEntry::new(
99,
Lsn::new(5, 2000),
Lsn::new(6, 3000),
delta_data,
);
let mut buf = BytesMut::new();
entry.write_to_log(&mut buf);
let decoded = BinDeltaLogEntry::read_from_log(&buf).unwrap();
assert_eq!(entry.db_id, decoded.db_id);
assert_eq!(entry.prev_full_lsn, decoded.prev_full_lsn);
assert_eq!(entry.prev_delta_lsn, decoded.prev_delta_lsn);
assert_eq!(entry.delta_data, decoded.delta_data);
}
#[test]
fn test_is_bin_delta() {
let entry = BinDeltaLogEntry::new(1, NULL_LSN, NULL_LSN, vec![]);
assert!(entry.is_bin_delta());
}
#[test]
fn test_log_size() {
let entry = BinDeltaLogEntry::new(1, NULL_LSN, NULL_LSN, vec![1, 2, 3]);
assert_eq!(entry.log_size(), 8 + 8 + 8 + 4 + 3);
}
}