use crate::core::TransferId;
use crate::format::{PAD_VALUE, SOT_TOGGLE_BIT, TailByte, TransferCrc};
use crate::frame::{Data, DataLength, Mtu};
use core::cmp::{max, min};
pub struct Scatter {
transfer_id: TransferId,
toggle_bit: bool,
data_length: usize,
offset: usize,
crc: TransferCrc,
}
impl Scatter {
const CRC_LENGTH: usize = TransferCrc::LENGTH;
const TAIL_LENGTH: usize = 1;
pub fn new(
transfer_id: TransferId,
_buffer: &[u8],
data_length: usize,
crc: TransferCrc,
) -> Self {
Self {
transfer_id,
toggle_bit: SOT_TOGGLE_BIT,
data_length,
offset: 0,
crc,
}
}
pub fn is_exhausted(&self) -> bool {
self.offset == self.data_length + Self::CRC_LENGTH
}
pub fn fetch_frame_data(&mut self, buffer: &[u8], mtu: Mtu) -> Option<Data> {
let max_payload_length = usize::from(mtu) - Self::TAIL_LENGTH;
if self.offset == 0 && self.data_length <= max_payload_length {
let frame_length = unwrap!(DataLength::new_ceil(self.data_length + Self::TAIL_LENGTH));
let mut frame_data = Data::new_zeros(frame_length);
let (tail, payload) = unwrap!(frame_data.split_last_mut());
self.fetch_single_segment(buffer, payload);
*tail = TailByte::new(true, true, SOT_TOGGLE_BIT, self.transfer_id).into();
return Some(frame_data);
}
let residual_length = self.data_length + Self::CRC_LENGTH - self.offset;
if residual_length == 0 {
return None;
}
let frame_length = unwrap!(DataLength::new_ceil(
min(residual_length, max_payload_length) + Self::TAIL_LENGTH
));
let mut frame_data = Data::new_zeros(frame_length);
let (tail, payload) = unwrap!(frame_data.split_last_mut());
let sot = self.offset == 0;
if residual_length >= payload.len() {
self.fetch_full_segment(buffer, payload);
} else {
assert!(self.offset <= self.data_length);
self.fetch_padded_segment(buffer, payload);
}
*tail = TailByte::new(
sot,
self.offset == self.data_length + Self::CRC_LENGTH,
self.toggle_bit,
self.transfer_id,
)
.into();
self.toggle_bit = !self.toggle_bit;
Some(frame_data)
}
fn fetch_single_segment(&mut self, buffer: &[u8], payload: &mut [u8]) {
let (payload_data, payload_pad) = payload.split_at_mut(self.data_length);
payload_data.copy_from_slice(&buffer[..self.data_length]);
payload_pad.fill(PAD_VALUE);
self.offset = self.data_length + Self::CRC_LENGTH;
}
fn fetch_full_segment(&mut self, buffer: &[u8], payload: &mut [u8]) {
let residual_length = self.data_length + Self::CRC_LENGTH - self.offset;
assert!(residual_length >= payload.len());
let residual_data = &buffer[min(self.offset, self.data_length)..self.data_length];
let (payload_data, payload_crc) =
payload.split_at_mut(min(residual_data.len(), payload.len()));
payload_data.copy_from_slice(&residual_data[..payload_data.len()]);
let crc_offset = max(self.offset, self.data_length) - self.data_length;
let crc_bytes = self.crc.get().to_be_bytes();
let residual_crc = &crc_bytes[crc_offset..];
payload_crc.copy_from_slice(&residual_crc[..payload_crc.len()]);
self.offset += payload.len();
}
fn fetch_padded_segment(&mut self, buffer: &[u8], payload: &mut [u8]) {
assert!(
self.offset <= self.data_length,
"Can not insert padding once CRC has started"
);
let residual_data = &buffer[self.offset..self.data_length];
let (payload_data_pad, payload_crc) =
unwrap!(payload.split_last_chunk_mut::<{ Self::CRC_LENGTH }>());
let (payload_data, payload_pad) = payload_data_pad.split_at_mut(residual_data.len());
payload_data.copy_from_slice(residual_data);
payload_pad.fill(PAD_VALUE);
let mut crc = self.crc;
crc.add_bytes(payload_pad);
*payload_crc = crc.get().to_be_bytes();
self.offset += payload_data.len() + payload_crc.len();
}
}
#[cfg(test)]
mod tests {
use super::*;
const TRANSFER_ID: TransferId = TransferId::new(27).unwrap();
#[test]
fn test_zero_frame_length() {
let buffer: [u8; 0] = [];
let mut scatter = Scatter::new(
TRANSFER_ID,
&buffer,
buffer.len(),
TransferCrc::from(0xffff),
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0b1110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_non_full_single_frame() {
let buffer: [u8; 6] = [0, 1, 2, 3, 4, 5];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x8c18.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 0b1110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_full_single_frame() {
let buffer: [u8; 7] = [0, 1, 2, 3, 4, 5, 6];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x28c2.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_minimum_double_frame() {
let buffer: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x178d.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1010_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[7, 0x17, 0x8d, 0b0100_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_non_full_double_frame() {
let buffer: [u8; 11] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x1944.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1010_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[7, 8, 9, 10, 0x19, 0x44, 0b0100_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_full_double_frame() {
let buffer: [u8; 12] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x7673.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1010_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[7, 8, 9, 10, 11, 0x76, 0x73, 0b0100_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_minimal_triple_frame() {
let buffer: [u8; 13] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0xacdd.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1010_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[7, 8, 9, 10, 11, 12, 0xac, 0b0000_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0xdd, 0b0110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_crc_only_triple_frame() {
let buffer: [u8; 14] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x78cb.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1010_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[7, 8, 9, 10, 11, 12, 13, 0b0000_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0x78, 0xcb, 0b0110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_non_full_triple_frame() {
let buffer: [u8; 15] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0xd551.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 0b1010_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[7, 8, 9, 10, 11, 12, 13, 0b0000_0000 + 27]).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Classic),
Some(Data::new(&[14, 0xd5, 0x51, 0b0110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[test]
fn test_padding_single_frame() {
let buffer: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0x178d.into());
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Fd),
Some(Data::new(&[0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0b1110_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
#[rustfmt::skip]
#[test]
fn test_padding_multi_frame() {
let buffer: [u8; 69] = core::array::from_fn(|i| i.try_into().unwrap());
let mut scatter = Scatter::new(TRANSFER_ID, &buffer, buffer.len(), 0xd7de.into());
let vec: heapless::Vec<u8, 64> = (0u8..63).chain([0b1010_0000u8 + 27].iter().copied()).collect();
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Fd),
Some(Data::new(&vec).unwrap())
);
assert_eq!(
scatter.fetch_frame_data(&buffer, Mtu::Fd),
Some(Data::new(&[63, 64, 65, 66, 67, 68, 0, 0, 0, 0xd6, 0x2c, 0b0100_0000 + 27]).unwrap())
);
assert_eq!(scatter.fetch_frame_data(&buffer, Mtu::Classic), None);
}
}