use crate::format::SIGNATURE;
use crate::{Error, Result};
use std::io::{Read, Seek, SeekFrom};
pub struct SignatureScanner<'a, R: Read + Seek> {
reader: &'a mut R,
search_limit: usize,
buffer: Vec<u8>,
current_offset: u64,
bytes_read: usize,
}
impl<'a, R: Read + Seek> SignatureScanner<'a, R> {
pub fn new(reader: &'a mut R, search_limit: usize) -> Self {
Self {
reader,
search_limit,
buffer: Vec::new(),
current_offset: 0,
bytes_read: 0,
}
}
pub fn find_next_signature(&mut self) -> Result<Option<u64>> {
self.ensure_buffer_loaded()?;
let mut search_start = 0;
while search_start + 8 <= self.buffer.len() {
if let Some(rel_pos) = self.buffer[search_start..]
.windows(6)
.position(|w| w == SIGNATURE)
{
let pos = search_start + rel_pos;
if pos + 8 <= self.buffer.len() {
let version_major = self.buffer[pos + 6];
let version_minor = self.buffer[pos + 7];
if version_major == 0 && version_minor <= 10 {
return Ok(Some(self.current_offset + pos as u64));
}
}
search_start = pos + 1;
} else {
break;
}
}
Ok(None)
}
pub fn find_all_signatures(&mut self) -> Result<Vec<u64>> {
self.ensure_buffer_loaded()?;
let mut signatures = Vec::new();
let mut search_start = 0;
while search_start + 8 <= self.buffer.len() {
if let Some(rel_pos) = self.buffer[search_start..]
.windows(6)
.position(|w| w == SIGNATURE)
{
let pos = search_start + rel_pos;
if pos + 8 <= self.buffer.len() {
let version_major = self.buffer[pos + 6];
let version_minor = self.buffer[pos + 7];
if version_major == 0 && version_minor <= 10 {
signatures.push(self.current_offset + pos as u64);
}
}
search_start = pos + 1;
} else {
break;
}
}
Ok(signatures)
}
pub fn scan_backwards(&mut self) -> Result<Vec<u64>> {
let end_pos = self.reader.seek(SeekFrom::End(0)).map_err(Error::Io)?;
let read_size = (end_pos as usize).min(self.search_limit);
let start_pos = end_pos - read_size as u64;
self.reader
.seek(SeekFrom::Start(start_pos))
.map_err(Error::Io)?;
self.current_offset = start_pos;
self.buffer.clear();
self.buffer.resize(read_size, 0);
self.reader
.read_exact(&mut self.buffer)
.map_err(Error::Io)?;
self.bytes_read = read_size;
let mut signatures = self.find_all_signatures()?;
signatures.reverse();
Ok(signatures)
}
fn ensure_buffer_loaded(&mut self) -> Result<()> {
if !self.buffer.is_empty() {
return Ok(());
}
self.current_offset = self.reader.stream_position().map_err(Error::Io)?;
self.buffer.resize(self.search_limit, 0);
let bytes_read = self.reader.read(&mut self.buffer).map_err(Error::Io)?;
self.buffer.truncate(bytes_read);
self.bytes_read = bytes_read;
Ok(())
}
pub fn bytes_scanned(&self) -> usize {
self.bytes_read
}
pub fn reset(&mut self) -> Result<()> {
self.buffer.clear();
self.bytes_read = 0;
self.reader.seek(SeekFrom::Start(0)).map_err(Error::Io)?;
self.current_offset = 0;
Ok(())
}
}
#[allow(dead_code)] pub fn validate_start_header(data: &[u8]) -> bool {
if data.len() < 32 {
return false;
}
if &data[0..6] != SIGNATURE {
return false;
}
let version_major = data[6];
let version_minor = data[7];
if version_major != 0 || version_minor > 10 {
return false;
}
let stored_crc = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
let calculated_crc = crc32fast::hash(&data[12..32]);
stored_crc == calculated_crc
}
#[allow(dead_code)] pub fn find_valid_header<R: Read + Seek>(
reader: &mut R,
search_limit: usize,
) -> Result<Option<u64>> {
let start_pos = reader.stream_position().map_err(Error::Io)?;
let mut buffer = vec![0u8; search_limit];
let bytes_read = reader.read(&mut buffer).map_err(Error::Io)?;
buffer.truncate(bytes_read);
let mut search_start = 0;
while search_start + 32 <= buffer.len() {
if let Some(rel_pos) = buffer[search_start..]
.windows(6)
.position(|w| w == SIGNATURE)
{
let pos = search_start + rel_pos;
if pos + 32 <= buffer.len() && validate_start_header(&buffer[pos..pos + 32]) {
return Ok(Some(start_pos + pos as u64));
}
search_start = pos + 1;
} else {
break;
}
}
Ok(None)
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
fn create_valid_header() -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(SIGNATURE);
data.push(0x00);
data.push(0x04);
let header_data = [0u8; 20];
let crc = crc32fast::hash(&header_data);
data.extend_from_slice(&crc.to_le_bytes());
data.extend_from_slice(&header_data);
data
}
#[test]
fn test_scanner_find_at_start() {
let data = create_valid_header();
let mut cursor = Cursor::new(data);
let mut scanner = SignatureScanner::new(&mut cursor, 1024);
let offset = scanner.find_next_signature().unwrap();
assert_eq!(offset, Some(0));
}
#[test]
fn test_scanner_find_with_prefix() {
let mut data = vec![0xFFu8; 100]; data.extend_from_slice(&create_valid_header());
let mut cursor = Cursor::new(data);
let mut scanner = SignatureScanner::new(&mut cursor, 1024);
let offset = scanner.find_next_signature().unwrap();
assert_eq!(offset, Some(100));
}
#[test]
fn test_scanner_no_signature() {
let data = vec![0x00u8; 1024];
let mut cursor = Cursor::new(data);
let mut scanner = SignatureScanner::new(&mut cursor, 1024);
let offset = scanner.find_next_signature().unwrap();
assert_eq!(offset, None);
}
#[test]
fn test_scanner_find_all() {
let header = create_valid_header();
let mut data = Vec::new();
data.extend_from_slice(&header);
data.extend_from_slice(&[0x00u8; 50]); data.extend_from_slice(&header);
let mut cursor = Cursor::new(data);
let mut scanner = SignatureScanner::new(&mut cursor, 1024);
let offsets = scanner.find_all_signatures().unwrap();
assert_eq!(offsets.len(), 2);
assert_eq!(offsets[0], 0);
assert_eq!(offsets[1], header.len() as u64 + 50);
}
#[test]
fn test_scanner_bytes_scanned() {
let data = vec![0x00u8; 500];
let mut cursor = Cursor::new(data);
let mut scanner = SignatureScanner::new(&mut cursor, 1000);
scanner.find_next_signature().unwrap();
assert_eq!(scanner.bytes_scanned(), 500);
}
#[test]
fn test_validate_start_header_valid() {
let header = create_valid_header();
assert!(validate_start_header(&header));
}
#[test]
fn test_validate_start_header_bad_signature() {
let mut header = create_valid_header();
header[0] = 0x00; assert!(!validate_start_header(&header));
}
#[test]
fn test_validate_start_header_bad_version() {
let mut header = create_valid_header();
header[6] = 1; assert!(!validate_start_header(&header));
}
#[test]
fn test_validate_start_header_too_short() {
let header = vec![0u8; 16];
assert!(!validate_start_header(&header));
}
#[test]
fn test_find_valid_header() {
let header = create_valid_header();
let mut data = vec![0xFFu8; 64]; data.extend_from_slice(&header);
let mut cursor = Cursor::new(data);
let offset = find_valid_header(&mut cursor, 1024).unwrap();
assert_eq!(offset, Some(64));
}
#[test]
fn test_scanner_reset() {
let data = create_valid_header();
let mut cursor = Cursor::new(data);
let mut scanner = SignatureScanner::new(&mut cursor, 1024);
let _ = scanner.find_next_signature().unwrap();
assert!(scanner.bytes_scanned() > 0);
scanner.reset().unwrap();
let offset = scanner.find_next_signature().unwrap();
assert_eq!(offset, Some(0));
}
}