use crate::KeyRing;
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::PathBuf;
use std::sync::Arc;
use crate::error::ReadError;
use crate::record::Record;
use crate::storage::format::{
deserialize_record, read_frame_header, FRAME_HEADER_SIZE, MIN_RECORD_SIZE,
};
use super::decode_frame_payload;
const READ_CHUNK: usize = 64 * 1024;
pub struct RecordIter {
offset: u64,
file_size: u64,
from_id: u64,
end_id: u64,
file: Option<File>,
is_compressed: bool,
is_encrypted: bool,
key: Option<Arc<KeyRing>>,
frame_data: Vec<u8>,
frame_pos: usize,
frame_len: usize,
buf: Vec<u8>,
buf_len: usize,
buf_pos: usize,
base_offset: u64,
}
impl RecordIter {
pub fn new(
segment_path: PathBuf,
start_offset: u64,
file_size: u64,
from_id: u64,
end_id: u64,
is_compressed: bool,
is_encrypted: bool,
key: Option<Arc<KeyRing>>,
) -> Result<Self, ReadError> {
let file = File::open(&segment_path)
.map_err(|e| ReadError::Io(format!("open {:?}: {}", segment_path, e)))?;
Ok(Self {
offset: start_offset,
file_size,
from_id,
end_id,
file: Some(file),
is_compressed,
is_encrypted,
key,
frame_data: Vec::new(),
frame_pos: 0,
frame_len: 0,
buf: vec![0u8; READ_CHUNK],
buf_len: 0,
buf_pos: 0,
base_offset: start_offset,
})
}
fn load_next_frame(&mut self) -> bool {
let file = match &mut self.file {
Some(f) => f,
None => return false,
};
if self.offset + FRAME_HEADER_SIZE as u64 > self.file_size {
return false;
}
let mut fh = [0u8; FRAME_HEADER_SIZE];
if file.seek(SeekFrom::Start(self.offset)).is_err() {
return false;
}
if file.read_exact(&mut fh).is_err() {
return false;
}
let (cl, dl) = read_frame_header(&fh);
let cl = cl as usize;
let dl = dl as usize;
if cl == 0 || dl == 0 || self.offset + FRAME_HEADER_SIZE as u64 + cl as u64 > self.file_size
{
return false;
}
let mut payload = vec![0u8; cl];
if file.read_exact(&mut payload).is_err() {
return false;
}
let decoded = match decode_frame_payload(
&payload,
self.is_compressed,
self.is_encrypted,
self.key.as_deref(),
) {
Ok(d) => d,
Err(_) => return false,
};
self.frame_data = decoded;
self.frame_len = dl.min(self.frame_data.len());
self.frame_pos = 0;
self.offset += FRAME_HEADER_SIZE as u64 + cl as u64;
true
}
fn ensure_raw(&mut self, n: usize) -> bool {
if self.buf_pos + n <= self.buf_len {
return true;
}
if self.buf_pos > 0 {
self.buf.copy_within(self.buf_pos..self.buf_len, 0);
self.buf_len -= self.buf_pos;
self.base_offset += self.buf_pos as u64;
self.buf_pos = 0;
}
if n > self.buf.len() {
let new_cap = n.next_power_of_two().max(READ_CHUNK);
self.buf.resize(new_cap, 0);
}
let file = match self.file.as_mut() {
Some(f) => f,
None => return false,
};
while self.buf_len < n {
let remaining_in_file = self
.file_size
.saturating_sub(self.base_offset + self.buf_len as u64);
if remaining_in_file == 0 {
return false; }
let read_start = self.buf_len;
let space = self.buf.len() - read_start;
let want = (space as u64).min(remaining_in_file) as usize;
if want == 0 {
return false; }
let file_pos = self.base_offset + read_start as u64;
if file.seek(SeekFrom::Start(file_pos)).is_err() {
self.file = None;
return false;
}
match file.read(&mut self.buf[read_start..read_start + want]) {
Ok(0) => return false,
Ok(k) => self.buf_len += k,
Err(_) => {
self.file = None;
return false;
}
}
}
true
}
}
impl Iterator for RecordIter {
type Item = Result<Record, ReadError>;
fn next(&mut self) -> Option<Self::Item> {
if self.file.is_none() {
return None;
}
if self.is_compressed || self.is_encrypted {
loop {
while self.frame_pos + MIN_RECORD_SIZE <= self.frame_len
&& self.frame_pos + MIN_RECORD_SIZE <= self.frame_data.len()
{
let total = u32::from_le_bytes([
self.frame_data[self.frame_pos],
self.frame_data[self.frame_pos + 1],
self.frame_data[self.frame_pos + 2],
self.frame_data[self.frame_pos + 3],
]) as usize;
if total < MIN_RECORD_SIZE
|| self.frame_pos + total > self.frame_len
|| self.frame_pos + total > self.frame_data.len()
{
break;
}
let rec = deserialize_record(
&self.frame_data[self.frame_pos..self.frame_pos + total],
);
self.frame_pos += total;
match rec {
Ok((record, _)) => {
if record.id.sequence < self.from_id {
continue;
}
if record.id.sequence >= self.end_id {
self.file = None;
return None;
}
return Some(Ok(record));
}
Err(_) => continue, }
}
if !self.load_next_frame() {
self.file = None;
return None;
}
}
}
loop {
if !self.ensure_raw(4) {
self.file = None;
return None;
}
let p = self.buf_pos;
let total = u32::from_le_bytes([
self.buf[p],
self.buf[p + 1],
self.buf[p + 2],
self.buf[p + 3],
]) as usize;
if total < MIN_RECORD_SIZE {
self.buf_pos += 1;
continue;
}
if !self.ensure_raw(total) {
self.file = None;
return None; }
let p = self.buf_pos;
let rec_bytes = &self.buf[p..p + total];
self.buf_pos += total;
match deserialize_record(rec_bytes) {
Ok((record, _)) => {
if record.id.sequence < self.from_id {
continue;
}
if record.id.sequence >= self.end_id {
self.file = None;
return None;
}
return Some(Ok(record));
}
Err(_) => continue,
}
}
}
}