use std::{
env,
fs::OpenOptions,
io::{self, prelude::*, SeekFrom},
};
pub(crate) fn open_current_exe() -> io::Result<std::fs::File> {
OpenOptions::new().read(true).open(env::current_exe()?)
}
pub struct ConfReaderOptions {
magic_bytes_opt: Vec<u8>,
window_size_opt: i64,
}
impl ConfReaderOptions {
pub fn new(bytes: Vec<u8>) -> Self {
ConfReaderOptions {
magic_bytes_opt: bytes,
window_size_opt: 2048,
}
}
pub fn magic_bytes(&mut self, bytes: Vec<u8>) -> &mut Self {
self.magic_bytes_opt = bytes;
self
}
pub fn window_size(&mut self, size: u32) -> &mut Self {
self.window_size_opt = size as i64;
self
}
pub fn read<F>(&self, input: &mut F) -> io::Result<Vec<u8>>
where
F: Seek + Read,
{
read_from_file(&self.magic_bytes_opt, self.window_size_opt, input)
}
pub fn read_from_exe(&self) -> io::Result<Vec<u8>> {
let mut cur_exe = open_current_exe()?;
self.read(&mut cur_exe)
}
}
pub fn read_from_exe(magic_bytes: &[u8], window_size: i64) -> io::Result<Vec<u8>> {
let mut cur_exe = open_current_exe()?;
read_from_file(magic_bytes, window_size, &mut cur_exe)
}
pub fn read_from_file<F>(magic_bytes: &[u8], window_size: i64, input: &mut F) -> io::Result<Vec<u8>>
where
F: Seek + Read,
{
let buffer_size = window_size * 2;
let mut current_window_index: i64 = 1;
let mut current_read_buffer = vec![0u8; buffer_size as usize];
loop {
input.seek(SeekFrom::End(-((current_window_index + 1) * window_size)))?;
let bytes_read = input.read(&mut current_read_buffer[..])?;
if bytes_read < window_size as usize {
break Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"reached beginning of the file without finding magic bytes",
));
}
if let Some(pos) = current_read_buffer
.windows(magic_bytes.len())
.position(|window| window == magic_bytes)
{
let conf_buffer_size = window_size - pos as i64 - magic_bytes.len() as i64
+ (current_window_index * window_size);
let mut conf_buffer = vec![0; conf_buffer_size as usize];
input.seek(SeekFrom::End(-conf_buffer_size))?;
input.read(&mut conf_buffer[..])?;
break Ok(conf_buffer);
}
current_window_index += 1;
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[test]
fn pulls_basic_data() {
let input_data = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1,
];
let header = [1, 2, 3, 4];
let data = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
let mut buf = Cursor::new(&input_data);
assert_eq!(&read_from_file(&header, 16, &mut buf).unwrap(), &data);
}
#[test]
fn pulls_data_over_boundary() {
let input_data = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1,
];
let header = [1, 2, 3, 4];
let data = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
let mut buf = Cursor::new(&input_data);
assert_eq!(&read_from_file(&header, 15, &mut buf).unwrap(), &data);
}
}