use crate::crc::crc32k_update;
use crate::{FrameSink, SinkFull};
const MASK: u8 = 0x55;
const MAX_RUN: usize = 254;
pub(crate) const ENCODED_CRC_LENGTH: usize = 5;
pub(crate) const LENGTH_ADJUSTMENT: usize = 2;
fn for_each_block(data: &[u8], mut block: impl FnMut(u8, &[u8])) {
let mut rest = data;
loop {
let run = rest
.iter()
.take(MAX_RUN)
.take_while(|&&octet| octet != 0)
.count();
let (octets, tail) = rest.split_at(run);
block(run as u8 + 1, octets);
rest = tail;
if run == MAX_RUN {
if rest.is_empty() {
return;
}
} else {
match rest.split_first() {
Some((_, after_zero)) => rest = after_zero,
None => return,
}
}
}
}
pub(crate) fn frame_length(data: &[u8]) -> usize {
let mut encoded = 0;
for_each_block(data, |_, octets| encoded += 1 + octets.len());
encoded + ENCODED_CRC_LENGTH - LENGTH_ADJUSTMENT
}
const CHUNK: usize = 32;
struct Output<'s, S: FrameSink + ?Sized> {
sink: &'s mut S,
chunk: [u8; CHUNK],
used: usize,
crc: u32,
result: Result<(), SinkFull>,
}
impl<S: FrameSink + ?Sized> Output<'_, S> {
fn blocks(&mut self, data: &[u8]) {
for_each_block(data, |code, octets| {
self.push(code);
for &octet in octets {
self.push(octet);
}
});
}
fn push(&mut self, octet: u8) {
if self.used == CHUNK {
self.flush();
}
self.chunk[self.used] = octet ^ MASK;
self.used += 1;
}
fn flush(&mut self) {
let chunk = &self.chunk[..self.used];
self.crc = crc32k_update(self.crc, chunk);
if self.result.is_ok() {
self.result = self.sink.write(chunk);
}
self.used = 0;
}
}
pub(crate) fn write_fields<S: FrameSink + ?Sized>(
data: &[u8],
sink: &mut S,
) -> Result<(), SinkFull> {
let mut output = Output {
sink,
chunk: [0; CHUNK],
used: 0,
crc: u32::MAX,
result: Ok(()),
};
output.blocks(data);
output.flush();
let crc = !output.crc;
output.blocks(&crc.to_le_bytes());
output.flush();
output.result
}
fn decoded_length(field: &[u8]) -> Option<usize> {
let mut read = 0;
let mut length = 0;
while let Some(&octet) = field.get(read) {
let code = usize::from(octet ^ MASK);
if code == 0 || code > field.len() - read {
return None;
}
read += code;
length += code - 1;
if code != 0xFF && read < field.len() {
length += 1;
}
}
Some(length)
}
fn decode_in_place(field: &mut [u8]) -> usize {
let (mut read, mut write) = (0, 0);
while read < field.len() {
let code = usize::from(field[read] ^ MASK);
read += 1;
let end = (read + code.saturating_sub(1)).min(field.len());
while read < end {
field[write] = field[read] ^ MASK;
read += 1;
write += 1;
}
if code != 0xFF && read < field.len() {
field[write] = 0;
write += 1;
}
}
write
}
pub(crate) fn check_fields(fields: &[u8]) -> Option<usize> {
let split = fields.len().checked_sub(ENCODED_CRC_LENGTH)?;
let (data, crc_field) = fields.split_at(split);
let length = decoded_length(data)?;
let mut crc = [0u8; ENCODED_CRC_LENGTH];
crc.copy_from_slice(crc_field);
decoded_length(&crc)?;
decode_in_place(&mut crc);
let received = u32::from_le_bytes([crc[0], crc[1], crc[2], crc[3]]);
(received == !crc32k_update(u32::MAX, data)).then_some(length)
}
pub(crate) fn decode_data_in_place(fields: &mut [u8]) -> usize {
let encoded = fields.len() - ENCODED_CRC_LENGTH;
decode_in_place(&mut fields[..encoded])
}