use crate::ReadByte;
use std::io;
pub(crate) trait SeekOver {
fn seek_over_string<const N: usize>(&mut self, needle: &[u8; N]) -> io::Result<()>;
}
impl<T: io::Seek + io::Read> SeekOver for T {
fn seek_over_string<const N: usize>(&mut self, needle: &[u8; N]) -> io::Result<()> {
let mut window = [0u8; N];
let mut window_ptr = 0;
self.read_exact(&mut window)?;
if window.contains(&0) {
return Err(io::Error::other("Not Found"));
}
if window[0..N] == needle[0..N] {
return Ok(());
}
loop {
if window[window_ptr..N] == needle[0..(N - window_ptr)]
&& window[0..window_ptr] == needle[(N - window_ptr)..N]
{
return Ok(());
}
window[window_ptr] = match self.read_byte()? {
0 => return Err(io::Error::other("Not Found")),
v => v,
};
window_ptr = (window_ptr + 1) % N;
}
}
}
#[cfg(test)]
mod test {
use std::fs;
use std::io;
use std::io::Seek;
use super::SeekOver;
#[test]
fn search_success() {
let mut file = fs::File::open("./sample-file.hqx").unwrap();
let needle = b"text";
file.seek_over_string(needle).unwrap();
assert_eq!(file.stream_position().unwrap(), 42);
file.seek(io::SeekFrom::Start(0)).unwrap();
let needle = b"(This file must be converted with BinHex 4.0)";
file.seek_over_string(needle).unwrap();
assert_eq!(file.stream_position().unwrap(), 133);
}
#[test]
fn search_fail() {
let mut file = fs::File::open("./sample-file.hqx").unwrap();
file.seek(io::SeekFrom::Start(0)).unwrap();
let needle = b"nowhere";
assert!(file.seek_over_string(needle).is_err());
}
#[test]
fn early_match() {
let mut file = fs::File::open("./sample-file.hqx").unwrap();
file.seek_over_string(b"Subject").unwrap();
assert_eq!(file.stream_position().unwrap(), b"Subject".len() as u64);
file.seek(io::SeekFrom::Start(0)).unwrap();
file.seek_over_string(b"ubject").unwrap();
assert_eq!(file.stream_position().unwrap(), b"ubject".len() as u64 + 1);
file.seek(io::SeekFrom::Start(0)).unwrap();
file.seek_over_string(b"bject").unwrap();
assert_eq!(file.stream_position().unwrap(), b"bject".len() as u64 + 2);
file.seek(io::SeekFrom::Start(0)).unwrap();
file.seek_over_string(b"ject").unwrap();
assert_eq!(file.stream_position().unwrap(), b"ject".len() as u64 + 3);
file.seek(io::SeekFrom::Start(0)).unwrap();
file.seek_over_string(b"4").unwrap();
assert_eq!(file.stream_position().unwrap(), 17);
}
#[test]
fn late_match() {
let mut file = fs::File::open("./sample-file.hqx").unwrap();
file.seek_over_string(b"!!!:").unwrap();
assert_eq!(
file.stream_position().unwrap(),
file.seek(io::SeekFrom::End(0)).unwrap()
);
}
}