use crate::{
bytewords::{self, Style},
collections::Vec,
fountain,
ur::UR,
};
use core::{fmt, str};
#[cfg(feature = "alloc")]
pub type Decoder = BaseDecoder<Alloc>;
pub type HeaplessDecoder<
const MAX_MESSAGE_LEN: usize,
const MAX_MIXED_PARTS: usize,
const MAX_FRAGMENT_LEN: usize,
const MAX_SEQUENCE_COUNT: usize,
const QUEUE_SIZE: usize,
const MAX_UR_TYPE: usize,
> = BaseDecoder<
Heapless<
MAX_MESSAGE_LEN,
MAX_MIXED_PARTS,
MAX_FRAGMENT_LEN,
MAX_SEQUENCE_COUNT,
QUEUE_SIZE,
MAX_UR_TYPE,
>,
>;
impl<
const MAX_MESSAGE_LEN: usize,
const MAX_MIXED_PARTS: usize,
const MAX_FRAGMENT_LEN: usize,
const MAX_SEQUENCE_COUNT: usize,
const QUEUE_SIZE: usize,
const MAX_UR_TYPE: usize,
>
HeaplessDecoder<
MAX_MESSAGE_LEN,
MAX_MIXED_PARTS,
MAX_FRAGMENT_LEN,
MAX_SEQUENCE_COUNT,
QUEUE_SIZE,
MAX_UR_TYPE,
>
{
pub const fn new() -> Self {
Self {
fountain: fountain::decoder::HeaplessDecoder::new(),
fragment: heapless::Vec::new(),
ur_type: heapless::Vec::new(),
}
}
}
#[derive(Default)]
pub struct BaseDecoder<T: Types> {
fountain: fountain::decoder::BaseDecoder<T::Decoder>,
fragment: T::Fragment,
ur_type: T::URType,
}
impl<T: Types> BaseDecoder<T> {
pub fn receive(&mut self, ur: UR) -> Result<(), Error> {
if !ur.is_multi_part() {
return Err(Error::NotMultiPart);
}
if self.ur_type.is_empty() {
self.ur_type
.try_extend_from_slice(ur.as_type().as_bytes())
.map_err(|_| Error::URTypeTooBig {
size: ur.as_type().as_bytes().len(),
})?;
} else if (&self.ur_type as &[_]) != ur.as_type().as_bytes() {
return Err(Error::InconsistentType);
}
let part = if !ur.is_deserialized() {
let bytewords = ur
.as_bytewords()
.expect("resource shouldn't be deserialized at this point");
let size = bytewords::validate(bytewords, Style::Minimal)?;
self.fragment.clear();
self.fragment
.try_resize(size, 0)
.map_err(|_| Error::FragmentTooBig { size })?;
bytewords::decode_to_slice(bytewords, &mut self.fragment, Style::Minimal)?;
Some(minicbor::decode(&self.fragment[..size])?)
} else {
None
};
let part = part.as_ref().unwrap_or_else(|| ur.as_part().unwrap());
self.fountain.receive(part)?;
Ok(())
}
#[must_use]
#[inline]
pub fn is_complete(&self) -> bool {
self.fountain.is_complete()
}
pub fn ur_type(&self) -> Option<&str> {
if !self.ur_type.is_empty() {
Some(str::from_utf8(&self.ur_type).unwrap())
} else {
None
}
}
#[inline]
pub fn message(&self) -> Result<Option<&[u8]>, Error> {
self.fountain.message().map_err(Error::from)
}
#[inline]
pub fn estimated_percent_complete(&self) -> f64 {
self.fountain.estimated_percent_complete()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.fountain.is_empty()
}
pub fn clear(&mut self) {
self.fountain.clear();
self.fragment.clear();
self.ur_type.clear();
}
}
pub trait Types: Default {
type Decoder: fountain::decoder::Types;
type Fragment: Vec<u8>;
type URType: Vec<u8>;
}
#[derive(Default)]
#[cfg(feature = "alloc")]
pub struct Alloc;
#[cfg(feature = "alloc")]
impl Types for Alloc {
type Decoder = fountain::decoder::Alloc;
type Fragment = alloc::vec::Vec<u8>;
type URType = alloc::vec::Vec<u8>;
}
#[derive(Default)]
pub struct Heapless<
const MAX_MESSAGE_LEN: usize,
const MAX_MIXED_PARTS: usize,
const MAX_FRAGMENT_LEN: usize,
const MAX_SEQUENCE_COUNT: usize,
const QUEUE_SIZE: usize,
const MAX_UR_TYPE: usize,
>;
impl<
const MAX_MESSAGE_LEN: usize,
const MAX_MIXED_PARTS: usize,
const MAX_FRAGMENT_LEN: usize,
const MAX_SEQUENCE_COUNT: usize,
const QUEUE_SIZE: usize,
const MAX_UR_TYPE: usize,
> Types
for Heapless<
MAX_MESSAGE_LEN,
MAX_MIXED_PARTS,
MAX_FRAGMENT_LEN,
MAX_SEQUENCE_COUNT,
QUEUE_SIZE,
MAX_UR_TYPE,
>
{
type Decoder = fountain::decoder::Heapless<
MAX_MESSAGE_LEN,
MAX_MIXED_PARTS,
MAX_FRAGMENT_LEN,
MAX_SEQUENCE_COUNT,
QUEUE_SIZE,
>;
type Fragment = heapless::Vec<u8, MAX_FRAGMENT_LEN>;
type URType = heapless::Vec<u8, MAX_UR_TYPE>;
}
#[derive(Debug)]
pub enum Error {
Cbor(minicbor::decode::Error),
Fountain(fountain::decoder::Error),
Bytewords(bytewords::DecodeError),
NotMultiPart,
FragmentTooBig {
size: usize,
},
URTypeTooBig {
size: usize,
},
InconsistentType,
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Cbor(e) => write!(f, "CBOR decoding error: {e}"),
Error::Fountain(e) => write!(f, "Fountain decoding error: {e}"),
Error::Bytewords(e) => write!(f, "Bytewords decoding error: {e}"),
Error::NotMultiPart => write!(f, "The Uniform Resource is not multi-part"),
Error::FragmentTooBig { size } => write!(
f,
"The fragment size ({size} bytes) is too big for the decoder"
),
Error::URTypeTooBig { size } => {
write!(f, "The UR type ({size} bytes) is too big for the decoder")
}
Error::InconsistentType => write!(
f,
"The received fragment is not consistent with the type of the previous fragments"
),
}
}
}
impl From<minicbor::decode::Error> for Error {
fn from(e: minicbor::decode::Error) -> Self {
Self::Cbor(e)
}
}
impl From<bytewords::DecodeError> for Error {
fn from(e: bytewords::DecodeError) -> Self {
Self::Bytewords(e)
}
}
impl From<fountain::decoder::Error> for Error {
fn from(e: fountain::decoder::Error) -> Self {
Self::Fountain(e)
}
}