use core::{fmt, ops::DerefMut};
use crate::{
bytewords,
collections::Set,
fountain::{chooser, chooser::BaseFragmentChooser, util::xor_into},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MessageDescription {
pub sequence_count: u32,
pub message_length: usize,
pub checksum: u32,
pub fragment_length: usize,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Part<'a> {
pub sequence: u32,
pub sequence_count: u32,
pub message_length: usize,
pub checksum: u32,
pub data: &'a [u8],
}
impl<'a> Part<'a> {
pub fn is_valid(&self) -> bool {
self.sequence > 0
&& self.sequence_count > 0
&& self.message_length > 0
&& !self.data.is_empty()
&& self.data.len() <= self.message_length
}
pub fn indexes<C, I>(&self) -> I
where
C: chooser::Types,
I: Set<usize>,
{
BaseFragmentChooser::<C>::default().choose_fragments(
self.sequence,
self.sequence_count,
self.checksum,
)
}
pub const fn max_encoded_len() -> usize {
#[rustfmt::skip]
const MAX_CBOR: &[u8] = &[
0x85, 0x1A, 0xFF, 0xFF, 0xFF, 0xFF, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1A, 0xFF, 0xFF, 0xFF, 0xFF, 0x5B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, ];
MAX_CBOR.len()
}
pub fn to_message_description(&self) -> MessageDescription {
MessageDescription {
sequence_count: self.sequence_count,
message_length: self.message_length,
checksum: self.checksum,
fragment_length: self.data.len(),
}
}
}
impl<'a> fmt::Display for Part<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut encoder = minicbor::Encoder::new(bytewords::minicbor::Writer::new(f));
encoder.encode(self).map_err(|_| fmt::Error)?;
encoder.into_writer().finish()?;
Ok(())
}
}
impl<'a> PartialEq<MessageDescription> for Part<'a> {
fn eq(&self, other: &MessageDescription) -> bool {
self.sequence_count == other.sequence_count
&& self.message_length == other.message_length
&& self.checksum == other.checksum
&& self.data.len() == other.fragment_length
}
}
impl<'a, C> minicbor::Encode<C> for Part<'a> {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(5)?
.u32(self.sequence)?
.u64(u64::from(self.sequence_count))?
.u64(
self.message_length
.try_into()
.map_err(|_| minicbor::encode::Error::message("expected u64"))?,
)?
.u32(self.checksum)?
.bytes(self.data)?;
Ok(())
}
}
impl<'b, C> minicbor::Decode<'b, C> for Part<'b> {
fn decode(
d: &mut minicbor::Decoder<'b>,
_ctx: &mut C,
) -> Result<Self, minicbor::decode::Error> {
if !matches!(d.array()?, Some(5)) {
return Err(minicbor::decode::Error::message(
"invalid CBOR array length",
));
}
Ok(Self {
sequence: d.u32()?,
sequence_count: d.u32()?,
message_length: d
.u32()?
.try_into()
.map_err(|_| minicbor::decode::Error::message("expected usize"))?,
checksum: d.u32()?,
data: d.bytes()?,
})
}
}
#[derive(Debug, Clone)]
pub struct IndexedPart<D, I> {
pub data: D,
pub indexes: I,
}
impl<D, I> IndexedPart<D, I> {
pub fn new(data: D, indexes: I) -> Self {
Self { data, indexes }
}
#[inline]
pub fn is_simple(&self) -> bool
where
I: Set<usize>,
{
self.indexes.len() == 1
}
pub fn reduce(&mut self, part: &IndexedPart<D, I>)
where
D: DerefMut<Target = [u8]>,
I: Set<usize>,
{
if self.indexes.len() == 1 {
return;
}
if part.indexes.is_subset(&self.indexes) {
self.indexes = self.indexes.sub(&part.indexes);
xor_into(&mut self.data, &part.data);
}
}
pub fn reduce_by_simple(&mut self, data: &[u8], index: usize)
where
D: DerefMut<Target = [u8]>,
I: Set<usize>,
{
assert!(self.indexes.len() > 1, "cannot reduce a simple part");
if self.indexes.contains(&index) {
self.indexes.remove(&index);
xor_into(&mut self.data, data);
}
}
}
#[cfg(test)]
pub mod tests {
use super::*;
#[test]
#[cfg(feature = "alloc")]
fn test_part_cbor_roundtrip() {
const PART: Part = Part {
sequence: 12,
sequence_count: 8,
message_length: 100,
checksum: 0x1234_5678,
data: &[1, 5, 3, 3, 5],
};
let mut cbor = alloc::vec::Vec::new();
minicbor::encode(&PART, &mut cbor).unwrap();
let part2: Part = minicbor::decode(&cbor).unwrap();
assert_eq!(part2, PART);
let mut cbor2 = alloc::vec::Vec::new();
minicbor::encode(&part2, &mut cbor2).unwrap();
assert_eq!(cbor, cbor2);
}
#[test]
fn test_part_cbor_decode() {
assert!(minicbor::decode::<'_, Part>(&[0x18]).is_err());
assert!(minicbor::decode::<'_, Part>(&[0x1]).is_err());
assert!(minicbor::decode::<'_, Part>(&[0x84, 0x1, 0x2, 0x3, 0x4]).is_err());
assert!(minicbor::decode::<'_, Part>(&[0x86, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6]).is_err());
assert!(
minicbor::decode::<'_, Part>(&[0x85, 0x41, 0x1, 0x2, 0x3, 0x4, 0x41, 0x1]).is_err()
);
assert!(
minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x41, 0x2, 0x3, 0x4, 0x41, 0x1]).is_err()
);
assert!(
minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x2, 0x41, 0x3, 0x4, 0x41, 0x1]).is_err()
);
assert!(
minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x2, 0x3, 0x41, 0x4, 0x41, 0x1]).is_err()
);
assert!(minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x2, 0x3, 0x4, 0x5]).is_err());
assert!(minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x2, 0x3, 0x4, 0x5]).is_err());
minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x2, 0x3, 0x4, 0x41, 0x5]).unwrap();
}
#[test]
fn test_part_cbor_decode_unsigned_types() {
minicbor::decode::<'_, Part>(&[0x85, 0x1, 0x2, 0x3, 0x4, 0x41, 0x5]).unwrap();
minicbor::decode::<'_, Part>(&[
0x85, 0x19, 0x1, 0x2, 0x19, 0x3, 0x4, 0x19, 0x5, 0x6, 0x19, 0x7, 0x8, 0x41, 0x5,
])
.unwrap();
minicbor::decode::<'_, Part>(&[
0x85, 0x1a, 0x1, 0x2, 0x3, 0x4, 0x1a, 0x5, 0x6, 0x7, 0x8, 0x1a, 0x9, 0x10, 0x11, 0x12,
0x1a, 0x13, 0x14, 0x15, 0x16, 0x41, 0x5,
])
.unwrap();
assert!(minicbor::decode::<'_, Part>(&[
0x85, 0x1b, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb, 0xc, 0xd, 0x1a, 0x5, 0x6, 0x7, 0x8, 0x1a,
0x9, 0x10, 0x11, 0x12, 0x1a, 0x13, 0x14, 0x15, 0x16, 0x41, 0x5,
])
.is_err());
assert!(minicbor::decode::<'_, Part>(&[
0x85, 0x1a, 0x1, 0x2, 0x3, 0x4, 0x1b, 0x5, 0x6, 0x7, 0x8, 0xa, 0xb, 0xc, 0xd, 0x1a,
0x9, 0x10, 0x11, 0x12, 0x1a, 0x13, 0x14, 0x15, 0x16, 0x41, 0x5,
])
.is_err());
assert!(minicbor::decode::<'_, Part>(&[
0x85, 0x1a, 0x1, 0x2, 0x3, 0x4, 0x1a, 0x5, 0x6, 0x7, 0x8, 0x1b, 0x9, 0x10, 0x11, 0x12,
0xa, 0xb, 0xc, 0xd, 0x1a, 0x13, 0x14, 0x15, 0x16, 0x41, 0x5,
])
.is_err());
assert!(minicbor::decode::<'_, Part>(&[
0x85, 0x1a, 0x1, 0x2, 0x3, 0x4, 0x1a, 0x5, 0x6, 0x7, 0x8, 0x1a, 0x9, 0x10, 0x11, 0x12,
0x1b, 0x13, 0x14, 0x15, 0x16, 0xa, 0xb, 0xc, 0xd, 0x41, 0x5,
])
.is_err());
}
}