use super::Error;
use super::FileHeader;
use super::ReaderAction;
use crate::crypt_file_data;
use crate::crypt_name_bytes;
use crate::crypt_u32;
use crate::DEFAULT_KEY;
use crate::HEADER_LEN;
use crate::MAGIC;
use crate::MAGIC_LEN;
use crate::MAX_FILE_NAME_LEN;
use crate::U32_LEN;
use crate::VERSION;
const DEFAULT_BUFFER_CAPACITY: usize = 10 * 1024;
#[derive(Debug)]
pub struct Reader {
buffer: oval::Buffer,
state: State,
need_seek: bool,
position: u64,
next_file_position: u64,
pub(crate) key: u32,
}
impl Reader {
pub fn new() -> Self {
Self {
buffer: oval::Buffer::with_capacity(DEFAULT_BUFFER_CAPACITY),
state: State::Header,
need_seek: false,
position: 0,
next_file_position: 0,
key: DEFAULT_KEY,
}
}
pub fn space(&mut self) -> &mut [u8] {
self.buffer.space()
}
pub fn fill(&mut self, num: usize) {
self.buffer.fill(num);
}
pub fn available_data(&self) -> usize {
self.buffer.available_data()
}
pub fn finish_seek(&mut self) {
assert!(
self.position != self.next_file_position,
"a seek was not requested"
);
self.position = self.next_file_position;
self.buffer.reset();
}
pub fn step_read_header(&mut self) -> Result<ReaderAction<()>, Error> {
match self.state {
State::Header => {}
State::FileHeader | State::FileData { .. } => {
return Ok(ReaderAction::Done(()));
}
}
let data = self.buffer.data();
let data_len = data.len();
if data_len < HEADER_LEN {
return Ok(ReaderAction::Read(HEADER_LEN - data_len));
}
let magic = *data.first_chunk::<MAGIC_LEN>().unwrap();
if magic != MAGIC {
return Err(Error::InvalidMagic { magic });
}
let version = data[MAGIC_LEN];
if version != VERSION {
return Err(Error::InvalidVersion { version });
}
let header_len_u64 = u64::try_from(HEADER_LEN).unwrap();
self.buffer.consume(HEADER_LEN);
self.position = header_len_u64;
self.next_file_position = header_len_u64;
self.state = State::FileHeader;
Ok(ReaderAction::Done(()))
}
pub fn step_read_file_header(&mut self) -> Result<ReaderAction<FileHeader>, Error> {
loop {
match self.state {
State::Header => {
let action = self.step_read_header()?;
if !action.is_done() {
return Ok(action.map_done(|_| unreachable!()));
}
}
State::FileHeader => break,
State::FileData { .. } => {
if self.position != self.next_file_position {
return Ok(ReaderAction::Seek(self.next_file_position));
}
self.state = State::FileHeader;
}
}
}
let mut key = self.key;
let data = self.buffer.data();
let data_len = data.len();
if data_len < U32_LEN {
return Ok(ReaderAction::Read(U32_LEN - data_len));
}
let file_name_len = {
let bytes = data[..U32_LEN].try_into().unwrap();
let n = u32::from_le_bytes(bytes);
let n = crypt_u32(&mut key, n);
if n > MAX_FILE_NAME_LEN {
return Err(Error::FileNameTooLongU32 { len: n });
}
usize::try_from(n).unwrap()
};
let file_header_size = (U32_LEN * 2) + file_name_len;
if data_len < file_header_size {
return Ok(ReaderAction::Read(file_header_size - data_len));
}
let file_name = {
let mut bytes = data[U32_LEN..U32_LEN + file_name_len].to_vec();
crypt_name_bytes(&mut key, &mut bytes);
String::from_utf8(bytes).map_err(|error| Error::InvalidFileName { error })?
};
let file_data_len = {
let index = U32_LEN + file_name_len;
let range = index..index + U32_LEN;
let bytes = data[range].try_into().unwrap();
let n = u32::from_le_bytes(bytes);
crypt_u32(&mut key, n)
};
let file_header_size_u64 = u64::try_from(file_header_size).unwrap();
self.buffer.consume(file_header_size);
self.position += file_header_size_u64;
self.next_file_position += file_header_size_u64 + u64::from(file_data_len);
self.key = key;
self.need_seek = true;
self.state = State::FileData {
key: self.key,
counter: 0,
remaining: file_data_len,
};
Ok(ReaderAction::Done(FileHeader {
name: file_name,
size: file_data_len,
}))
}
pub fn step_read_file_data(
&mut self,
output_buffer: &mut [u8],
) -> Result<ReaderAction<usize>, Error> {
let (key, counter, remaining) = loop {
match &mut self.state {
State::Header => {
let action = self.step_read_header()?;
if !action.is_done() {
return Ok(action.map_done(|_| unreachable!()));
}
}
State::FileHeader => return Ok(ReaderAction::Done(0)),
State::FileData {
key,
counter,
remaining,
} => break (key, counter, remaining),
}
};
if *remaining == 0 {
return Ok(ReaderAction::Done(0));
}
let data = self.buffer.data();
if data.is_empty() {
return Ok(ReaderAction::Read(self.buffer.available_space()));
}
let remaining_usize =
usize::try_from(*remaining).expect("remaining bytes cannot fit in a `usize`");
let len = std::cmp::min(data.len(), output_buffer.len());
let len = std::cmp::min(len, remaining_usize);
let len_u32 = u32::try_from(len).expect("len cannot fit in a `u32`");
let output_buffer = &mut output_buffer[..len];
output_buffer.copy_from_slice(&data[..len]);
crypt_file_data(key, counter, output_buffer);
*remaining -= len_u32;
self.buffer.consume(len);
self.position += u64::from(len_u32);
Ok(ReaderAction::Done(len))
}
}
impl Default for Reader {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Copy, Clone)]
enum State {
Header,
FileHeader,
FileData {
key: u32,
counter: u8,
remaining: u32,
},
}