use crate::checksum::crc32c_append;
use crate::error::{DbError, FormatError};
use crate::file_format::check_segment_payload_len;
use crate::segments::header::SegmentType;
use crate::segments::header::{decode_segment_header, SegmentHeader, SEGMENT_HEADER_LEN};
use crate::storage::Store;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SegmentMeta {
pub offset: u64,
pub header: SegmentHeader,
}
pub fn read_segment_header_at(
store: &mut impl Store,
offset: u64,
) -> Result<([u8; SEGMENT_HEADER_LEN], SegmentHeader), DbError> {
let mut buf = [0u8; SEGMENT_HEADER_LEN];
store.read_exact_at(offset, &mut buf)?;
let header = decode_segment_header(&buf)?;
Ok((buf, header))
}
pub fn scan_segments(store: &mut impl Store, start: u64) -> Result<Vec<SegmentMeta>, DbError> {
let mut out = Vec::new();
let mut offset = start;
let file_len = store.len()?;
while offset < file_len {
if file_len - offset < SEGMENT_HEADER_LEN as u64 {
return Err(DbError::Format(FormatError::TruncatedSegmentHeader {
got: (file_len - offset) as usize,
expected: SEGMENT_HEADER_LEN,
}));
}
let (_, header) = read_segment_header_at(store, offset)?;
let payload_start = offset + SEGMENT_HEADER_LEN as u64;
let payload_end = payload_start + header.payload_len;
if payload_end > file_len {
return Err(DbError::Format(FormatError::SegmentPayloadPastEof));
}
let mut remaining = header.payload_len;
let mut chunk = [0u8; 8192];
let mut cursor = payload_start;
let mut crc = 0u32;
while remaining > 0 {
let to_read = std::cmp::min(remaining as usize, chunk.len());
store.read_exact_at(cursor, &mut chunk[..to_read])?;
crc = crc32c_append(crc, &chunk[..to_read]);
cursor += to_read as u64;
remaining -= to_read as u64;
}
if header.segment_type != SegmentType::Checkpoint
&& header.segment_type != SegmentType::Temp
&& crc != header.payload_crc32c
{
return Err(DbError::Format(FormatError::BadSegmentPayloadChecksum));
}
out.push(SegmentMeta { offset, header });
offset = payload_end;
}
Ok(out)
}
pub fn read_segment_payload(
store: &mut impl Store,
meta: &SegmentMeta,
) -> Result<Vec<u8>, DbError> {
check_segment_payload_len(meta.header.payload_len)?;
let mut payload = vec![0u8; meta.header.payload_len as usize];
let start = meta.offset + SEGMENT_HEADER_LEN as u64;
store.read_exact_at(start, &mut payload)?;
Ok(payload)
}