extern crate alloc;
use alloc::vec::Vec;
use crate::io::{Error, Read, Write};
pub const SKIPPABLE_MAGIC_START: u32 = 0x184D_2A50;
pub const SKIPPABLE_HEADER_SIZE: usize = 8;
pub const SKIPPABLE_MAGIC_MAX_VARIANT: u8 = 15;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkippableFrame {
magic_variant: u8,
payload: Vec<u8>,
}
impl SkippableFrame {
pub fn new(magic_variant: u8, payload: Vec<u8>) -> Result<Self, SkippableFrameError> {
validate_magic_variant(magic_variant)?;
validate_payload_size(payload.len())?;
Ok(Self {
magic_variant,
payload,
})
}
pub fn magic_variant(&self) -> u8 {
self.magic_variant
}
pub fn magic_number(&self) -> u32 {
SKIPPABLE_MAGIC_START + u32::from(self.magic_variant)
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn into_payload(self) -> Vec<u8> {
self.payload
}
pub fn serialized_size(&self) -> usize {
self.payload.len() + SKIPPABLE_HEADER_SIZE
}
pub fn encode_into<W: Write>(&self, writer: &mut W) -> Result<(), Error> {
write_skippable_frame_to(self.magic_variant, &self.payload, writer).map(|_| ())
}
pub fn decode_from<R: Read>(reader: &mut R) -> Result<Self, DecodeSkippableFrameError> {
let mut magic_buf = [0u8; 4];
reader
.read_exact(&mut magic_buf)
.map_err(DecodeSkippableFrameError::Magic)?;
let magic_number = u32::from_le_bytes(magic_buf);
let variant = magic_number.wrapping_sub(SKIPPABLE_MAGIC_START);
if !(0..=u32::from(SKIPPABLE_MAGIC_MAX_VARIANT)).contains(&variant) {
return Err(DecodeSkippableFrameError::BadMagicNumber(magic_number));
}
let mut len_buf = [0u8; 4];
reader
.read_exact(&mut len_buf)
.map_err(DecodeSkippableFrameError::Length)?;
let length_u32 = u32::from_le_bytes(len_buf);
let length = usize::try_from(length_u32)
.map_err(|_| DecodeSkippableFrameError::PayloadTooLarge { length: length_u32 })?;
if length.checked_add(SKIPPABLE_HEADER_SIZE).is_none() {
return Err(DecodeSkippableFrameError::PayloadTooLarge { length: length_u32 });
}
let mut payload: Vec<u8> = Vec::new();
payload
.try_reserve_exact(length)
.map_err(|_| DecodeSkippableFrameError::AllocationFailed { requested: length })?;
const CHUNK: usize = 1024;
let mut scratch = [0u8; CHUNK];
let mut remaining = length;
while remaining > 0 {
let take = remaining.min(CHUNK);
reader
.read_exact(&mut scratch[..take])
.map_err(DecodeSkippableFrameError::Payload)?;
payload.extend_from_slice(&scratch[..take]);
remaining -= take;
}
Ok(Self {
magic_variant: variant as u8,
payload,
})
}
}
pub fn write_skippable_frame<W: Write>(
magic_variant: u8,
payload: &[u8],
writer: &mut W,
) -> Result<usize, SkippableFrameError> {
validate_magic_variant(magic_variant)?;
validate_payload_size(payload.len())?;
write_skippable_frame_to(magic_variant, payload, writer).map_err(SkippableFrameError::Io)
}
fn write_skippable_frame_to<W: Write>(
magic_variant: u8,
payload: &[u8],
writer: &mut W,
) -> Result<usize, Error> {
let magic = SKIPPABLE_MAGIC_START + u32::from(magic_variant);
let length = payload.len() as u32;
writer.write_all(&magic.to_le_bytes())?;
writer.write_all(&length.to_le_bytes())?;
writer.write_all(payload)?;
Ok(payload.len() + SKIPPABLE_HEADER_SIZE)
}
#[inline]
fn validate_magic_variant(magic_variant: u8) -> Result<(), SkippableFrameError> {
if magic_variant > SKIPPABLE_MAGIC_MAX_VARIANT {
Err(SkippableFrameError::InvalidMagicVariant(magic_variant))
} else {
Ok(())
}
}
#[inline]
fn validate_payload_size(len: usize) -> Result<(), SkippableFrameError> {
if (len as u64) > u64::from(u32::MAX) {
return Err(SkippableFrameError::PayloadTooLarge(len));
}
if len.checked_add(SKIPPABLE_HEADER_SIZE).is_none() {
return Err(SkippableFrameError::PayloadTooLarge(len));
}
Ok(())
}
#[derive(Debug)]
#[non_exhaustive]
pub enum SkippableFrameError {
InvalidMagicVariant(u8),
PayloadTooLarge(usize),
Io(Error),
}
#[derive(Debug)]
#[non_exhaustive]
pub enum DecodeSkippableFrameError {
Magic(Error),
BadMagicNumber(u32),
Length(Error),
Payload(Error),
AllocationFailed { requested: usize },
PayloadTooLarge { length: u32 },
}
impl core::fmt::Display for SkippableFrameError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::InvalidMagicVariant(v) => {
write!(
f,
"skippable frame magic_variant {v} out of range 0..={}",
SKIPPABLE_MAGIC_MAX_VARIANT
)
}
Self::PayloadTooLarge(n) => write!(
f,
"skippable frame payload size {n} not representable: either exceeds u32::MAX (wire-format length-field ceiling) or overflows usize when combined with the 8-byte header (32-bit targets)"
),
Self::Io(e) => write!(f, "skippable frame I/O error: {e}"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SkippableFrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::InvalidMagicVariant(_) | Self::PayloadTooLarge(_) => None,
}
}
}
impl From<Error> for SkippableFrameError {
fn from(value: Error) -> Self {
Self::Io(value)
}
}
impl core::fmt::Display for DecodeSkippableFrameError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Magic(e) => write!(f, "skippable frame: error reading magic number: {e}"),
Self::BadMagicNumber(m) => write!(
f,
"skippable frame: magic 0x{m:08X} is not in the skippable range 0x184D2A50..=0x184D2A5F"
),
Self::Length(e) => write!(f, "skippable frame: error reading length field: {e}"),
Self::Payload(e) => write!(f, "skippable frame: error reading payload bytes: {e}"),
Self::AllocationFailed { requested } => write!(
f,
"skippable frame: failed to allocate {requested} bytes for payload"
),
Self::PayloadTooLarge { length } => write!(
f,
"skippable frame: declared length {length} not representable on this target (length > usize::MAX on 16-bit, or length + 8 byte header overflows usize on 32-bit)"
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeSkippableFrameError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Magic(e) | Self::Length(e) | Self::Payload(e) => Some(e),
Self::BadMagicNumber(_)
| Self::AllocationFailed { .. }
| Self::PayloadTooLarge { .. } => None,
}
}
}
#[cfg(all(test, feature = "std"))]
mod tests;