#![no_std]
#![warn(missing_docs)]
use core::convert::TryInto;
struct MarkerInfo {
index: usize,
points_to: usize,
}
impl MarkerInfo {
fn adjust_accordingly<const SIZE: usize>(
&mut self,
out_buffer: &mut [u8; SIZE],
new_index: usize,
) {
out_buffer[self.index] = (new_index - self.index).try_into().unwrap();
self.index = new_index;
self.points_to = new_index + 0xff;
}
}
pub fn stuff<const INPUT: usize, const OUTPUT: usize>(
buff: [u8; INPUT],
marker: u8,
) -> [u8; OUTPUT] {
let mut output_buffer: [u8; OUTPUT] = [marker; OUTPUT];
let mut last_marker = MarkerInfo {
index: 0,
points_to: 0xff,
};
let mut overhead_bytes = 1;
for i in 0..INPUT {
let value = buff[i];
if last_marker.points_to == (overhead_bytes + i) {
last_marker.adjust_accordingly(&mut output_buffer, overhead_bytes + i);
overhead_bytes += 1;
}
if value == marker {
last_marker.adjust_accordingly(&mut output_buffer, overhead_bytes + i);
continue;
}
output_buffer[overhead_bytes + i] = value;
}
output_buffer[last_marker.index] = (INPUT + overhead_bytes - last_marker.index)
.try_into()
.unwrap();
if marker != 0x00 {
for i in 0..(OUTPUT - 1) {
output_buffer[i] ^= marker;
}
}
output_buffer
}
pub fn unstuff<const INPUT: usize, const OUTPUT: usize>(
mut buff: [u8; INPUT],
marker: u8,
) -> ([u8; OUTPUT], usize) {
let mut output_buffer = [0; OUTPUT];
if marker != 0x00 {
for i in 0..(INPUT - 1) {
buff[i] ^= marker;
}
}
let mut until_next_marker = buff[0] - 1;
let mut next_is_overhead_byte = buff[0] == 0xff;
let mut overhead_bytes = 1;
let mut i = 1;
let output_buffer_length = loop {
let value = buff[i];
if value == marker {
break i - overhead_bytes - 1;
}
if until_next_marker == 0 {
until_next_marker = value;
if next_is_overhead_byte {
overhead_bytes += 1;
} else {
output_buffer[i - overhead_bytes] = marker;
}
next_is_overhead_byte = until_next_marker == 0xff;
} else {
output_buffer[i - overhead_bytes] = value;
}
until_next_marker -= 1;
if i < INPUT {
i += 1;
} else {
panic!("No marker value found!");
}
} + 1;
(output_buffer, output_buffer_length)
}
#[cfg(test)]
mod tests {
use super::*;
use core::ops::Range;
#[derive(Debug)]
struct TestVector<const N: usize, const M: usize> {
unencoded_data: [u8; N],
encoded_data: [u8; M],
}
impl<const N: usize, const M: usize> TestVector<N, M> {
const fn new(unencoded_data: [u8; N], encoded_data: [u8; M]) -> Self {
Self {
unencoded_data,
encoded_data,
}
}
fn assert_stuff(&self) {
assert_eq!(stuff::<N, M>(self.unencoded_data, 0x00), self.encoded_data);
}
fn assert_unstuff(&self) {
assert_eq!(
unstuff::<M, N>(self.encoded_data, 0x00),
(self.unencoded_data, self.unencoded_data.len())
);
}
fn assert_stuff_then_unstuff(&self) {
assert_eq!(
unstuff::<M, N>(stuff(self.unencoded_data, 0x00), 0x00),
(self.unencoded_data, self.unencoded_data.len())
);
}
fn assert_unstuff_then_stuff(&self) {
assert_eq!(
stuff::<N, M>(unstuff(self.encoded_data, 0x00).0, 0x00),
self.encoded_data
);
}
}
fn get_range<const N: usize>(
mut initial: [u8; N],
start_index: usize,
range: Range<u8>,
) -> [u8; N] {
for (index, value) in range.enumerate() {
initial[index + start_index] = value;
}
initial
}
const TV_1: TestVector<1, 3> = TestVector::new([0x00], [0x01, 0x01, 0x00]);
const TV_2: TestVector<2, 4> = TestVector::new([0x00, 0x00], [0x01, 0x01, 0x01, 0x00]);
const TV_3: TestVector<4, 6> = TestVector::new(
[0x11, 0x22, 0x00, 0x33],
[0x03, 0x11, 0x22, 0x02, 0x33, 0x00],
);
const TV_4: TestVector<4, 6> = TestVector::new(
[0x11, 0x22, 0x33, 0x44],
[0x05, 0x11, 0x22, 0x33, 0x44, 0x00],
);
const TV_5: TestVector<4, 6> = TestVector::new(
[0x11, 0x00, 0x00, 0x00],
[0x02, 0x11, 0x01, 0x01, 0x01, 0x00],
);
fn tv_6() -> TestVector<254, 256> {
TestVector::new(
get_range([0; 254], 0, 0x01..0xff),
get_range(
{
let mut arr = [0; 256];
arr[0] = 0xff;
arr
},
1,
0x01..0xff,
),
)
}
fn tv_7() -> TestVector<255, 257> {
TestVector::new(
get_range([0; 255], 0, 0x00..0xff),
get_range(
{
let mut arr = [0; 257];
arr[0] = 0x01;
arr[1] = 0xff;
arr
},
2,
0x01..0xff,
),
)
}
fn tv_8() -> TestVector<255, 258> {
TestVector::new(
get_range([0xff; 255], 0, 0x01..0xff),
get_range(
{
let mut arr = [0; 258];
arr[0] = 0xff;
arr[255] = 0x02;
arr[256] = 0xff;
arr
},
1,
0x01..0xff,
),
)
}
fn tv_9() -> TestVector<255, 258> {
TestVector::new(
get_range(
{
let mut arr = [0xff; 255];
arr[254] = 0;
arr
},
0,
0x02..0xff,
),
get_range(
{
let mut arr = [0; 258];
arr[0] = 0xff;
arr[254] = 0xff;
arr[255] = 0x01;
arr[256] = 0x01;
arr
},
1,
0x02..0xff,
),
)
}
fn tv_10() -> TestVector<255, 257> {
TestVector::new(
get_range(
{
let mut arr = [0xff; 255];
arr[253] = 0x00;
arr[254] = 0x01;
arr
},
0,
0x03..0xff,
),
get_range(
{
let mut arr = [0; 257];
arr[0] = 0xfe;
arr[253] = 0xff;
arr[254] = 0x02;
arr[255] = 0x01;
arr
},
1,
0x03..0xff,
),
)
}
#[test]
fn stuff_test_vectors() {
TV_1.assert_stuff();
TV_2.assert_stuff();
TV_3.assert_stuff();
TV_4.assert_stuff();
TV_5.assert_stuff();
tv_6().assert_stuff();
tv_7().assert_stuff();
tv_8().assert_stuff();
tv_9().assert_stuff();
tv_10().assert_stuff();
}
#[test]
fn unstuff_test_vectors() {
TV_1.assert_unstuff();
TV_2.assert_unstuff();
TV_3.assert_unstuff();
TV_4.assert_unstuff();
TV_5.assert_unstuff();
tv_6().assert_unstuff();
tv_7().assert_unstuff();
tv_8().assert_unstuff();
tv_9().assert_unstuff();
tv_10().assert_unstuff();
assert_eq!(
unstuff([0x01, 0x01, 0x00], 0x00),
([0x00, 0x00, 0x00, 0x00], 1)
);
assert_eq!(
unstuff([0x02, 0x01, 0x00], 0x00),
([0x01, 0x00, 0x00, 0x00], 1)
);
}
#[test]
fn inverses() {
TV_1.assert_stuff_then_unstuff();
TV_2.assert_stuff_then_unstuff();
TV_3.assert_stuff_then_unstuff();
TV_4.assert_stuff_then_unstuff();
TV_5.assert_stuff_then_unstuff();
tv_6().assert_stuff_then_unstuff();
tv_7().assert_stuff_then_unstuff();
tv_8().assert_stuff_then_unstuff();
tv_9().assert_stuff_then_unstuff();
tv_10().assert_stuff_then_unstuff();
TV_1.assert_unstuff_then_stuff();
TV_2.assert_unstuff_then_stuff();
TV_3.assert_unstuff_then_stuff();
TV_4.assert_unstuff_then_stuff();
TV_5.assert_unstuff_then_stuff();
tv_6().assert_unstuff_then_stuff();
tv_7().assert_unstuff_then_stuff();
tv_8().assert_unstuff_then_stuff();
tv_9().assert_unstuff_then_stuff();
tv_10().assert_unstuff_then_stuff();
}
#[test]
fn non_zero_byte() {
let transfer: [u8; 130] = stuff(
*b"----------------------------------------------------------------A----------------------------------------------------------------",
b'A'
);
assert!(transfer.iter().all(|byte| *byte != b'A'));
}
}