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 {
use super::*;
fn build_donor_skippable(magic_variant: u8, payload: &[u8]) -> Vec<u8> {
let magic = (SKIPPABLE_MAGIC_START + u32::from(magic_variant)).to_le_bytes();
let size = (payload.len() as u32).to_le_bytes();
let mut out = Vec::with_capacity(payload.len() + SKIPPABLE_HEADER_SIZE);
out.extend_from_slice(&magic);
out.extend_from_slice(&size);
out.extend_from_slice(payload);
out
}
#[test]
fn round_trip_all_sixteen_variants() {
for variant in 0u8..=15 {
let payload = alloc::vec![variant; 32 + variant as usize];
let frame = SkippableFrame::new(variant, payload.clone()).expect("variant in range");
let mut wire = Vec::new();
frame
.encode_into(&mut wire)
.expect("encode into Vec succeeds");
let mut cursor: &[u8] = wire.as_slice();
let decoded = SkippableFrame::decode_from(&mut cursor).expect("round-trip decode");
assert_eq!(decoded.magic_variant(), variant);
assert_eq!(
decoded.magic_number(),
SKIPPABLE_MAGIC_START + u32::from(variant)
);
assert_eq!(decoded.payload(), payload.as_slice());
assert!(
cursor.is_empty(),
"decode_from must consume exactly the frame bytes, no overshoot or undershoot"
);
}
}
#[test]
fn empty_payload_round_trips() {
let frame = SkippableFrame::new(7, Vec::new()).expect("empty payload OK");
assert_eq!(frame.serialized_size(), SKIPPABLE_HEADER_SIZE);
let mut wire = Vec::new();
frame.encode_into(&mut wire).unwrap();
assert_eq!(wire.len(), SKIPPABLE_HEADER_SIZE);
let mut cursor: &[u8] = wire.as_slice();
let decoded = SkippableFrame::decode_from(&mut cursor).unwrap();
assert!(decoded.payload().is_empty());
assert_eq!(decoded.magic_variant(), 7);
}
#[test]
fn large_payload_round_trips() {
let payload = alloc::vec![0xABu8; 1024 * 1024];
let frame = SkippableFrame::new(0, payload.clone()).unwrap();
let mut wire = Vec::new();
frame.encode_into(&mut wire).unwrap();
assert_eq!(wire.len(), payload.len() + SKIPPABLE_HEADER_SIZE);
let mut cursor: &[u8] = wire.as_slice();
let decoded = SkippableFrame::decode_from(&mut cursor).unwrap();
assert_eq!(decoded.payload().len(), payload.len());
assert!(decoded.payload() == payload.as_slice());
}
#[test]
fn new_rejects_variant_sixteen() {
let err = SkippableFrame::new(16, Vec::new()).expect_err("variant 16 out of range");
match err {
SkippableFrameError::InvalidMagicVariant(v) => assert_eq!(v, 16),
other => panic!("expected InvalidMagicVariant(16), got {other:?}"),
}
}
#[test]
fn new_rejects_variant_max() {
let err = SkippableFrame::new(255, Vec::new()).unwrap_err();
match err {
SkippableFrameError::InvalidMagicVariant(v) => assert_eq!(v, 255),
other => panic!("expected InvalidMagicVariant(255), got {other:?}"),
}
}
#[test]
fn write_function_rejects_invalid_variant() {
let mut sink: Vec<u8> = Vec::new();
let err = write_skippable_frame(16, b"x", &mut sink).unwrap_err();
assert!(matches!(err, SkippableFrameError::InvalidMagicVariant(16)));
assert!(
sink.is_empty(),
"no bytes must be written on rejected input"
);
}
#[test]
fn byte_parity_with_donor_layout() {
for &payload_len in &[0usize, 1, 8, 256, 4096] {
let payload: Vec<u8> = (0..payload_len).map(|i| (i % 251) as u8).collect();
for variant in 0u8..=15 {
let expected = build_donor_skippable(variant, &payload);
let mut via_struct = Vec::new();
SkippableFrame::new(variant, payload.clone())
.unwrap()
.encode_into(&mut via_struct)
.unwrap();
assert_eq!(
via_struct, expected,
"struct encode mismatch: variant={variant} len={payload_len}"
);
let mut via_free = Vec::new();
let written = write_skippable_frame(variant, &payload, &mut via_free).unwrap();
assert_eq!(written, expected.len());
assert_eq!(
via_free, expected,
"free-fn encode mismatch: variant={variant} len={payload_len}"
);
}
}
}
#[test]
fn decode_rejects_non_skippable_magic() {
let mut wire = Vec::new();
wire.extend_from_slice(&0xFD2F_B528u32.to_le_bytes());
wire.extend_from_slice(&0u32.to_le_bytes());
let mut cursor: &[u8] = wire.as_slice();
let err = SkippableFrame::decode_from(&mut cursor).unwrap_err();
match err {
DecodeSkippableFrameError::BadMagicNumber(m) => assert_eq!(m, 0xFD2F_B528),
other => panic!("expected BadMagicNumber, got {other:?}"),
}
}
#[test]
fn decode_rejects_magic_above_band() {
let mut wire = Vec::new();
wire.extend_from_slice(&0x184D_2A60u32.to_le_bytes());
wire.extend_from_slice(&0u32.to_le_bytes());
let mut cursor: &[u8] = wire.as_slice();
let err = SkippableFrame::decode_from(&mut cursor).unwrap_err();
assert!(matches!(
err,
DecodeSkippableFrameError::BadMagicNumber(0x184D_2A60)
));
}
#[test]
fn decode_truncated_magic_surfaces_typed_error() {
let wire = [0x50u8, 0x2A, 0x4D];
let mut cursor: &[u8] = &wire;
let err = SkippableFrame::decode_from(&mut cursor).unwrap_err();
assert!(
matches!(err, DecodeSkippableFrameError::Magic(_)),
"expected Magic, got {err:?}"
);
}
#[test]
fn decode_truncated_length_surfaces_typed_error() {
let mut wire = Vec::new();
wire.extend_from_slice(&SKIPPABLE_MAGIC_START.to_le_bytes());
wire.extend_from_slice(&[0u8, 0, 0]);
let mut cursor: &[u8] = wire.as_slice();
let err = SkippableFrame::decode_from(&mut cursor).unwrap_err();
assert!(
matches!(err, DecodeSkippableFrameError::Length(_)),
"expected Length, got {err:?}"
);
}
#[test]
fn decode_truncated_payload_surfaces_typed_error() {
let mut wire = Vec::new();
wire.extend_from_slice(&SKIPPABLE_MAGIC_START.to_le_bytes());
wire.extend_from_slice(&16u32.to_le_bytes());
wire.extend_from_slice(&[0u8; 4]);
let mut cursor: &[u8] = wire.as_slice();
let err = SkippableFrame::decode_from(&mut cursor).unwrap_err();
assert!(
matches!(err, DecodeSkippableFrameError::Payload(_)),
"expected Payload, got {err:?}"
);
}
#[test]
fn serialized_size_matches_encoded_length() {
for payload_len in [0usize, 1, 7, 8, 9, 255, 256, 1023, 1024] {
let payload = alloc::vec![0u8; payload_len];
let frame = SkippableFrame::new(3, payload).unwrap();
let mut wire = Vec::new();
frame.encode_into(&mut wire).unwrap();
assert_eq!(
wire.len(),
frame.serialized_size(),
"serialized_size() must match actual encode length for payload_len={payload_len}"
);
}
}
#[test]
fn decode_huge_length_returns_typed_error_not_oom_abort() {
let huge: u32 = u32::MAX;
let mut wire = Vec::new();
wire.extend_from_slice(&SKIPPABLE_MAGIC_START.to_le_bytes());
wire.extend_from_slice(&huge.to_le_bytes());
let mut cursor: &[u8] = wire.as_slice();
let err = SkippableFrame::decode_from(&mut cursor).unwrap_err();
match err {
DecodeSkippableFrameError::PayloadTooLarge { length } => {
assert_eq!(length, huge);
}
DecodeSkippableFrameError::AllocationFailed { requested } => {
assert_eq!(requested, huge as usize);
}
DecodeSkippableFrameError::Payload(_) => {
}
other => panic!("expected PayloadTooLarge / AllocationFailed / Payload, got {other:?}"),
}
}
#[test]
fn payload_too_large_check_branches_on_pointer_width() {
#[cfg(target_pointer_width = "64")]
{
let result = validate_payload_size(u32::MAX as usize + 1);
assert!(matches!(
result,
Err(SkippableFrameError::PayloadTooLarge(_))
));
let ok = validate_payload_size(u32::MAX as usize);
assert!(ok.is_ok(), "u32::MAX representable on 64-bit");
}
#[cfg(target_pointer_width = "32")]
{
let result = validate_payload_size(u32::MAX as usize);
assert!(
matches!(result, Err(SkippableFrameError::PayloadTooLarge(_))),
"u32::MAX overflows when combined with the 8-byte header on 32-bit"
);
let ok = validate_payload_size((u32::MAX as usize) - SKIPPABLE_HEADER_SIZE);
assert!(ok.is_ok(), "below the header-overflow boundary on 32-bit");
}
}
}