use either::Either::{self, Left, Right};
use thiserror::Error;
use crate::sans::{
Decoder,
check::compute_crc,
data::{AnyField, Field, FieldInner},
definition::{Definition, DefinitionAlt, DefinitionFieldAlt},
header::{DocumentHeaderError, RecordHeader, RecordHeaderError},
};
use super::{FromRecord, FromRecords};
#[derive(Debug, Error)]
pub enum Error {
#[error("Unexpectedly reached the end of the slice.")]
EndOfSlice,
#[error("Calculated ({calculated}) and found ({found}) CRC values do not match.")]
CyclicRedundancyCheck { found: u16, calculated: u16 },
#[error("Incorrect file header: {0}.")]
Header(#[from] DocumentHeaderError),
#[error("Found unsupported developer data.")]
Developer,
}
impl From<RecordHeaderError> for Error {
fn from(err: RecordHeaderError) -> Self {
match err {
RecordHeaderError::DeveloperData => Self::Developer,
}
}
}
pub fn decode(r: &[u8], o: &mut impl FromRecords) -> Result<(), Error> {
let i = &mut 0;
let (size, successor) = Decoder::advance(take(r, i)?)?;
let mut record_header = match successor {
Left(state) => state.advance(take(r, i)?),
Right(state) => state,
};
let end = *i + size as usize;
let found = u16::from_le_bytes(r.get(end..).ok_or(Error::EndOfSlice)?.try_into().unwrap());
let calculated = compute_crc(0, r.get(..end).ok_or(Error::EndOfSlice)?);
if found != calculated {
Err(Error::CyclicRedundancyCheck { found, calculated })?;
}
let mut definition_table = [0; 16];
while *i < end {
let (local, successor) = record_header.advance(take(r, i)?)?;
record_header = match successor {
Left(state) => {
definition_table[local as usize] = *i;
decode_definition(state, r, i)?
}
Right((time, state)) => {
let j = definition_table[local as usize];
decode_data(state, time, r, i, j, o)?
}
};
}
Ok(())
}
fn decode_definition(state: Definition, r: &[u8], i: &mut usize) -> Result<RecordHeader, Error> {
Ok(match state.advance(take(r, i)?) {
Left(mut state) => loop {
state = match state.advance(take(r, i)?) {
Left(state) => state,
Right(state) => break state,
};
},
Right(state) => state,
})
}
fn decode_data(
state: DefinitionAlt,
time: Option<u8>,
r: &[u8],
i: &mut usize,
mut j: usize,
o: &mut impl FromRecords,
) -> Result<RecordHeader, Error> {
let (global, successor) = state.advance(take(r, &mut j)?);
let mut o = o.add_record(global);
if let (Some(o), Some(time)) = (&mut o, time) {
o.add_time_offset(time);
}
Ok(match successor {
Left(mut state) => loop {
let (f, inner_state) = state.advance(take(r, &mut j)?);
let o = o.as_deref_mut();
fn decode_field<
T: FieldInner<From = [u8; N]>,
O: FromRecord + ?Sized,
const N: usize,
>(
mut state: Field<T>,
r: &[u8],
i: &mut usize,
f: u8,
mut o: Option<&mut O>,
add: fn(&mut O, u8, T::Into),
) -> Result<Either<DefinitionFieldAlt, RecordHeader>, Error> {
loop {
let (value, successor) = state.advance(take(r, i)?);
if let (Some(o), Some(value)) = (&mut o, value) {
add(o, f, value);
}
state = match successor {
Left(successor) => return Ok(successor),
Right(y) => y,
}
}
}
let successor = match inner_state {
AnyField::U8(s) => decode_field(s, r, i, f, o, FromRecord::add_u8),
AnyField::U8Z(s) => decode_field(s, r, i, f, o, FromRecord::add_u8),
AnyField::U16(s) => decode_field(s, r, i, f, o, FromRecord::add_u16),
AnyField::U16Z(s) => decode_field(s, r, i, f, o, FromRecord::add_u16),
AnyField::U32(s) => decode_field(s, r, i, f, o, FromRecord::add_u32),
AnyField::U32Z(s) => decode_field(s, r, i, f, o, FromRecord::add_u32),
AnyField::U64(s) => decode_field(s, r, i, f, o, FromRecord::add_u64),
AnyField::U64Z(s) => decode_field(s, r, i, f, o, FromRecord::add_u64),
AnyField::I8(s) => decode_field(s, r, i, f, o, FromRecord::add_i8),
AnyField::I16(s) => decode_field(s, r, i, f, o, FromRecord::add_i16),
AnyField::I32(s) => decode_field(s, r, i, f, o, FromRecord::add_i32),
AnyField::I64(s) => decode_field(s, r, i, f, o, FromRecord::add_i64),
AnyField::F32(s) => decode_field(s, r, i, f, o, FromRecord::add_f32),
AnyField::F64(s) => decode_field(s, r, i, f, o, FromRecord::add_f64),
}?;
state = match successor {
Left(state) => state,
Right(state) => break state,
};
},
Right(state) => state,
})
}
fn take<const N: usize>(r: &[u8], i: &mut usize) -> Result<[u8; N], Error> {
let s = *i;
*i += N;
Ok(r.get(s..*i).ok_or(Error::EndOfSlice)?.try_into().unwrap())
}