use winnow::error::ParserError;
use winnow::stream::{LocatingSlice, Location, Stateful, Stream};
use winnow::token::take;
use winnow::{error::ErrMode, prelude::*};
use super::data_object::{DataObject, DataSpace, data_object};
use super::super_block::{SuperBlock, super_block};
pub(crate) type Input<'a> = Stateful<LocatingSlice<&'a [u8]>, State>;
#[derive(Debug, Clone)]
pub(crate) struct State {
size_of_lengths: u8,
size_of_offsets: u8,
end_of_file_address: u64,
recursive_counter: u32,
data_space: Option<DataSpace>,
}
impl State {
pub fn new(block: &SuperBlock) -> Self {
Self {
size_of_lengths: block.size_of_lengths,
size_of_offsets: block.size_of_offsets,
end_of_file_address: block.end_of_file_address,
recursive_counter: 0,
data_space: None,
}
}
pub fn size_of_lengths(&self) -> u8 {
self.size_of_lengths
}
pub fn size_of_offsets(&self) -> u8 {
self.size_of_offsets
}
#[allow(dead_code)]
pub fn end_of_file_address(&self) -> u64 {
self.end_of_file_address
}
pub fn is_address_valid(&self, address: u64) -> bool {
address > 0 && address < self.end_of_file_address
}
pub(crate) fn data_space(&self) -> Option<DataSpace> {
self.data_space.clone()
}
pub(crate) fn set_data_space(&mut self, data_space: DataSpace) {
self.data_space = Some(data_space);
}
pub(crate) fn recursive_counter(&self) -> u32 {
self.recursive_counter
}
pub(crate) fn recursive_counter_inc(&mut self) {
self.recursive_counter = self.recursive_counter.saturating_add(1);
}
pub(crate) fn recursive_counter_dec(&mut self) {
self.recursive_counter = self.recursive_counter.saturating_sub(1);
}
}
pub struct ParsedHdf<'a> {
data: &'a [u8],
state: State,
pub root: DataObject,
}
impl<'a> ParsedHdf<'a> {
pub fn parse_child(&self, name: &str, address: u64) -> ModalResult<DataObject> {
if !self.state.is_address_valid(address) {
return Err(ErrMode::assert(&self.data, "Invalid child object address"));
}
let input = LocatingSlice::new(self.data);
let mut stream = Input {
input,
state: self.state.clone(),
};
let _skip = take(address as usize).parse_next(&mut stream)?;
data_object(name).parse_next(&mut stream)
}
pub fn get_child(&self, name: &str) -> Option<ModalResult<DataObject>> {
self.root
.child_directories
.iter()
.find(|d| d.name == name)
.map(|d| self.parse_child(&d.name, d.address))
}
}
pub fn parse_with_children(input: &[u8]) -> ModalResult<ParsedHdf<'_>> {
let mut slice = input;
let cp = slice.checkpoint();
let super_block = super_block.parse_next(&mut slice)?;
slice.reset(&cp);
if super_block.end_of_file_address as usize != slice.eof_offset() {
return Err(ErrMode::assert(&slice, "File size mismatch"));
}
let state = State::new(&super_block);
let locating = LocatingSlice::new(slice);
let mut stream = Input {
input: locating,
state: state.clone(),
};
let _skip =
take(super_block.root_group_object_header_address as usize).parse_next(&mut stream)?;
let root = data_object("root").parse_next(&mut stream)?;
Ok(ParsedHdf {
data: input,
state,
root,
})
}
pub fn parse(mut input: &[u8]) -> ModalResult<DataObject> {
let cp = input.checkpoint();
let super_block = super_block.parse_next(&mut input)?;
input.reset(&cp);
if super_block.end_of_file_address as usize != input.eof_offset() {
log::error!(
"File size mismatch: header says {}, actual {}",
super_block.end_of_file_address,
input.eof_offset()
);
return Err(ErrMode::assert(&input, "File size mismatch"));
}
log::debug!(
"SuperBlock parsed: offsets={}, lengths={}, root_addr={:#x}",
super_block.size_of_offsets,
super_block.size_of_lengths,
super_block.root_group_object_header_address
);
let state = State::new(&super_block);
let input = LocatingSlice::new(input);
let mut stream = Input { input, state };
let _skip =
take(super_block.root_group_object_header_address as usize).parse_next(&mut stream)?;
log::debug!(
"About to parse root data_object at position {:#x}",
stream.input.current_token_start()
);
match data_object("root").parse_next(&mut stream) {
Ok(obj) => {
log::debug!("Root object parsed successfully: {}", obj.name);
Ok(obj)
}
Err(e) => {
log::error!("Failed to parse root object: {:?}", e);
Err(e)
}
}
}