use std::fs::File;
use std::path::Path;
use crate::Lsn;
use crate::error::Result;
use crate::record::RECORD_HEADER_SIZE;
use crate::segment::{self, ScanOutcome};
pub struct Reader<'w> {
dir: &'w Path,
segments: &'w [Lsn],
seg_idx: usize,
file: Option<File>,
offset: u64,
expected_lsn: Lsn,
from: Lsn,
segment_size: u64,
max_record_size: u32,
buf: Vec<u8>,
done: bool,
}
impl<'w> Reader<'w> {
pub(crate) fn new(
dir: &'w Path,
segments: &'w [Lsn],
first: File,
from: Lsn,
segment_size: u64,
max_record_size: u32,
) -> Reader<'w> {
debug_assert!(!segments.is_empty(), "a Wal always has ≥1 segment");
Reader {
dir,
segments,
seg_idx: 0,
file: Some(first),
offset: segment::HEADER_SIZE,
expected_lsn: segments[0],
from,
segment_size,
max_record_size,
buf: Vec::new(),
done: false,
}
}
fn advance_segment(&mut self) -> Result<bool> {
self.seg_idx += 1;
if self.seg_idx >= self.segments.len() {
self.file = None;
return Ok(false);
}
let name = segment::filename_for(self.segments[self.seg_idx]);
self.file = Some(File::open(self.dir.join(name))?);
self.offset = segment::HEADER_SIZE;
Ok(true)
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<Result<(Lsn, &[u8])>> {
let segment_size = self.segment_size;
let max_record_size = self.max_record_size;
loop {
if self.done {
return None;
}
let Some(file) = self.file.as_ref() else {
self.done = true;
return None;
};
match segment::read_record_at(
file,
self.offset,
segment_size,
max_record_size,
&mut self.buf,
) {
Err(e) => {
self.done = true;
return Some(Err(e));
}
Ok(ScanOutcome::CleanEnd) | Ok(ScanOutcome::Invalid) => {
match self.advance_segment() {
Ok(true) => continue,
Ok(false) => {
self.done = true;
return None;
}
Err(e) => {
self.done = true;
return Some(Err(e));
}
}
}
Ok(ScanOutcome::Record {
lsn,
payload_len,
framed_len,
}) => {
if lsn != self.expected_lsn {
self.done = true;
return None;
}
self.offset += framed_len as u64;
self.expected_lsn = lsn.next();
if lsn < self.from {
continue;
}
let payload = &self.buf[RECORD_HEADER_SIZE..RECORD_HEADER_SIZE + payload_len];
return Some(Ok((lsn, payload)));
}
}
}
}
}