use std::io;
use crc64::crc64;
use serde::{Deserialize, Serialize};
pub(super) const WAL_MAGICNUM: &[u8; 8] = b"TMPS_WAL";
pub(super) const WAL_HEADER_SIZE: usize = 16;
pub(super) const WAL_RECORD_PREFIX_SIZE: usize = 12;
#[derive(Debug)]
pub(super) struct WalHeader {
pub(super) filenum: u64,
}
impl WalHeader {
#[inline]
pub(super) const fn new(filenum: u64) -> Self {
Self { filenum }
}
#[inline]
pub(super) fn encode(&self) -> [u8; WAL_HEADER_SIZE] {
let mut buf = [0u8; WAL_HEADER_SIZE];
buf[0..8].copy_from_slice(WAL_MAGICNUM);
buf[8..16].copy_from_slice(&self.filenum.to_le_bytes());
buf
}
pub(super) fn decode(buf: [u8; WAL_HEADER_SIZE]) -> io::Result<Self> {
let magic_bytes = &buf[0..8];
if magic_bytes != WAL_MAGICNUM {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"invalid magic number: not a write-ahead log file. expected {:?} but got {:?}.",
WAL_MAGICNUM, magic_bytes
),
));
}
let filenum = u64::from_le_bytes(buf[8..16].try_into().unwrap());
Ok(Self { filenum })
}
}
#[derive(Debug, Serialize, Deserialize)]
pub(super) struct WalRecordPrefix {
pub(super) checksum: u64,
pub(super) len: u32,
}
impl WalRecordPrefix {
pub(super) fn new(record: &[u8]) -> Self {
let checksum = crc64(0, record);
let len = record.len() as u32;
Self { checksum, len }
}
#[inline]
pub(super) fn is_valid_record(&self, record: &[u8]) -> bool {
assert_eq!(record.len(), self.len as usize);
let computed_checksum = crc64(0, record);
computed_checksum == self.checksum
}
#[inline]
pub(super) fn encode(&self) -> [u8; WAL_RECORD_PREFIX_SIZE] {
let mut buf = [0u8; WAL_RECORD_PREFIX_SIZE];
buf[0..8].copy_from_slice(&self.checksum.to_le_bytes());
buf[8..12].copy_from_slice(&self.len.to_le_bytes());
buf
}
#[inline]
pub(super) fn decode(buf: [u8; WAL_RECORD_PREFIX_SIZE]) -> Self {
let checksum = u64::from_le_bytes(buf[0..8].try_into().unwrap());
let len = u32::from_le_bytes(buf[8..12].try_into().unwrap());
Self { checksum, len }
}
}