use alloc::boxed::Box;
use alloc::vec::Vec;
use fearless_simd::Level;
use super::core::frame::{self, OutputSpec};
use super::core::reader::Parse;
use super::error::{DecodeError, Unsupported};
use crate::dict::BundleId;
use crate::{Backend, DataType, FormatVersion};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct OutputInfo {
data_type: DataType,
size: u64,
strings: u64,
}
impl OutputInfo {
#[must_use]
pub const fn data_type(&self) -> DataType {
self.data_type
}
#[must_use]
pub const fn size(&self) -> u64 {
self.size
}
#[must_use]
pub const fn num_elts(&self) -> Option<u64> {
match self.data_type {
DataType::Serial => Some(self.size),
DataType::String => Some(self.strings),
DataType::Struct | DataType::Numeric => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FrameInfo {
version: FormatVersion,
outputs: Box<[OutputInfo]>,
content_checksum: bool,
compressed_checksum: bool,
bundle: Option<BundleId>,
comment: Option<Box<[u8]>>,
header_len: usize,
}
impl FrameInfo {
pub(crate) fn parse(src: &[u8], simd: Level, verify: bool) -> Result<Self, DecodeError> {
let mut specs: Vec<OutputSpec> = Vec::new();
let info =
frame::parse_with(src, simd, verify, |spec| specs.push(spec)).map_err(|error| {
match error {
Parse::Truncated => DecodeError::UnexpectedEndOfInput,
Parse::Failed(error) => error,
}
})?;
let Some(version) = FormatVersion::new(info.version) else {
return Err(DecodeError::unsupported(Unsupported::FormatVersion {
version: info.version,
}));
};
Ok(Self {
version,
outputs: specs
.into_iter()
.map(|spec| OutputInfo {
data_type: spec.ty,
size: spec.size,
strings: spec.strings,
})
.collect(),
content_checksum: info.content_checksum,
compressed_checksum: info.compressed_checksum,
bundle: info.bundle,
comment: info
.comment
.and_then(|(start, len)| src.get(start..start + len))
.map(Box::from),
header_len: info.header_len,
})
}
#[must_use]
pub const fn version(&self) -> FormatVersion {
self.version
}
#[must_use]
pub fn outputs(&self) -> &[OutputInfo] {
&self.outputs
}
#[must_use]
pub fn decompressed_size(&self) -> u64 {
self.outputs
.iter()
.fold(0, |total, output| total.saturating_add(output.size))
}
#[must_use]
pub fn comment(&self) -> Option<&[u8]> {
self.comment.as_deref()
}
#[must_use]
pub const fn bundle_id(&self) -> Option<BundleId> {
self.bundle
}
#[must_use]
pub const fn content_checksum(&self) -> bool {
self.content_checksum
}
#[must_use]
pub const fn compressed_checksum(&self) -> bool {
self.compressed_checksum
}
#[must_use]
pub const fn header_len(&self) -> usize {
self.header_len
}
}
impl TryFrom<&[u8]> for FrameInfo {
type Error = DecodeError;
fn try_from(src: &[u8]) -> Result<Self, Self::Error> {
Self::parse(src, Backend::default().0, true)
}
}