mod decode;
mod encode;
use crate::decode::Error as DecodeError;
use crate::{formats::Format, io::IoRead};
const U8_MAX: usize = u8::MAX as usize;
const U16_MAX: usize = u16::MAX as usize;
const U32_MAX: usize = u32::MAX as usize;
const U8_MAX_PLUS_ONE: usize = U8_MAX + 1;
const U16_MAX_PLUS_ONE: usize = U16_MAX + 1;
pub(crate) fn read_ext_header<'de, R>(
format: Format,
reader: &mut R,
) -> Result<(usize, i8), DecodeError<R::Error>>
where
R: IoRead<'de>,
{
use crate::decode::NbyteReader;
let len = match format {
Format::FixExt1 => 1,
Format::FixExt2 => 2,
Format::FixExt4 => 4,
Format::FixExt8 => 8,
Format::FixExt16 => 16,
Format::Ext8 => NbyteReader::<1>::read(reader)?,
Format::Ext16 => NbyteReader::<2>::read(reader)?,
Format::Ext32 => NbyteReader::<4>::read(reader)?,
_ => return Err(DecodeError::UnexpectedFormat),
};
let ext_type: [u8; 1] = reader
.read_slice(1)
.map_err(DecodeError::Io)?
.as_bytes()
.try_into()
.map_err(|_| DecodeError::UnexpectedEof)?;
let ty = ext_type[0] as i8;
Ok((len, ty))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExtensionRef<'a> {
pub r#type: i8,
pub data: &'a [u8],
}
impl<'a> ExtensionRef<'a> {
pub fn new(r#type: i8, data: &'a [u8]) -> Self {
Self { r#type, data }
}
pub fn to_format<E>(&self) -> core::result::Result<Format, crate::encode::Error<E>> {
let format = match self.data.len() {
1 => Format::FixExt1,
2 => Format::FixExt2,
4 => Format::FixExt4,
8 => Format::FixExt8,
16 => Format::FixExt16,
0..=U8_MAX => Format::Ext8,
U8_MAX_PLUS_ONE..=U16_MAX => Format::Ext16,
U16_MAX_PLUS_ONE..=U32_MAX => Format::Ext32,
_ => return Err(crate::encode::Error::InvalidFormat),
};
Ok(format)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FixedExtension<const N: usize> {
pub r#type: i8,
len: usize,
data: [u8; N],
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExtensionCapacityError(());
impl<const N: usize> FixedExtension<N> {
pub fn new(r#type: i8, data: &[u8]) -> Option<Self> {
if data.len() > N {
return None;
}
let mut buf = [0u8; N];
buf[..data.len()].copy_from_slice(data);
Some(Self {
r#type,
len: data.len(),
data: buf,
})
}
pub fn new_fixed(r#type: i8, data: [u8; N]) -> Self {
Self {
r#type,
len: N,
data,
}
}
pub fn new_fixed_with_prefix(
r#type: i8,
len: usize,
data: [u8; N],
) -> core::result::Result<Self, ExtensionCapacityError> {
if len <= N {
Ok(Self { r#type, len, data })
} else {
Err(ExtensionCapacityError(()))
}
}
pub fn as_ref(&self) -> ExtensionRef<'_> {
ExtensionRef {
r#type: self.r#type,
data: &self.data[..self.len],
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_slice(&self) -> &[u8] {
&self.data[..self.len]
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
&mut self.data[..self.len]
}
}
impl core::fmt::Display for ExtensionCapacityError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "extension data exceeds capacity")
}
}
impl core::error::Error for ExtensionCapacityError {}
impl<const N: usize> TryFrom<ExtensionRef<'_>> for FixedExtension<N> {
type Error = ExtensionCapacityError;
fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
if value.data.len() > N {
return Err(ExtensionCapacityError(()));
}
let mut buf = [0u8; N];
buf[..value.data.len()].copy_from_slice(value.data);
Ok(Self {
r#type: value.r#type,
len: value.data.len(),
data: buf,
})
}
}
#[cfg(feature = "alloc")]
mod owned {
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExtensionOwned {
pub r#type: i8,
pub data: alloc::vec::Vec<u8>,
}
impl ExtensionOwned {
pub fn new(r#type: i8, data: alloc::vec::Vec<u8>) -> Self {
Self { r#type, data }
}
pub fn as_ref(&self) -> ExtensionRef<'_> {
ExtensionRef {
r#type: self.r#type,
data: &self.data,
}
}
}
impl<'a> From<ExtensionRef<'a>> for ExtensionOwned {
fn from(value: ExtensionRef<'a>) -> Self {
Self {
r#type: value.r#type,
data: value.data.to_vec(),
}
}
}
impl<const N: usize> From<FixedExtension<N>> for ExtensionOwned {
fn from(value: FixedExtension<N>) -> Self {
Self {
r#type: value.r#type,
data: value.as_slice().to_vec(),
}
}
}
}
#[cfg(feature = "alloc")]
pub use owned::ExtensionOwned;