use alloc::{boxed::Box, vec::Vec};
use embedded_heatshrink::{
HSEFinishRes, HSEPollRes, HSESinkRes, HeatshrinkEncoder,
};
use miniz_oxide::deflate::compress_to_vec_zlib;
use crate::components::common::{
crc32, BinaryGcodeError, BlockKind, Checksum, CompressionAlgorithm,
Encoding, MAGIC,
};
pub fn serialise_file_header(
version: u32,
checksum: Checksum,
) -> Box<[u8]> {
let mut header = Vec::with_capacity(10);
header.extend(MAGIC.to_le_bytes());
header.extend(version.to_le_bytes());
header.extend(checksum.to_le_bytes());
header.into_boxed_slice()
}
pub fn serialise_block(
kind: BlockKind,
compression: CompressionAlgorithm,
encoding: Encoding,
checksum: Checksum,
additional_parameters: &[u8],
data: &[u8],
) -> Result<Box<[u8]>, BinaryGcodeError> {
let mut block: Vec<u8> = Vec::new();
block.extend(kind.to_le_bytes());
block.extend(compression.to_le_bytes());
let uncompressed_len = data.len() as u32;
block.extend(uncompressed_len.to_le_bytes());
let mut parameters: Vec<u8> = Vec::with_capacity(0);
parameters.extend(encoding.to_le_bytes());
parameters.extend(additional_parameters);
match compression {
CompressionAlgorithm::None => {
block.extend(parameters);
block.extend(data);
}
CompressionAlgorithm::Deflate => {
let compressed = compress_to_vec_zlib(data, 10);
let compressed_len = compressed.len() as u32;
block.extend(compressed_len.to_le_bytes());
block.extend(parameters);
block.extend(compressed);
}
CompressionAlgorithm::Heatshrink11_4 => {
let compressed = shrink(11, 4, data)?;
let compressed_len = compressed.len() as u32;
block.extend(compressed_len.to_le_bytes());
block.extend(parameters);
block.extend(compressed);
}
CompressionAlgorithm::Heatshrink12_4 => {
let compressed = shrink(12, 4, data)?;
let compressed_len = compressed.len() as u32;
block.extend(compressed_len.to_le_bytes());
block.extend(parameters);
block.extend(compressed);
}
}
if checksum == Checksum::Crc32 {
let crc = crc32(&block);
block.extend(crc.to_le_bytes());
}
Ok(block.into_boxed_slice())
}
fn shrink(
window: u8,
lookahead: u8,
input: &[u8],
) -> Result<Box<[u8]>, BinaryGcodeError> {
let mut encoder = HeatshrinkEncoder::new(window, lookahead).unwrap();
let mut sunk: usize = 0;
let mut polled: usize = 0;
let mut output = vec![0u8; input.len()];
while sunk < input.len() {
match encoder.sink(&input[sunk..]) {
HSESinkRes::Ok(sz) => {
sunk += sz;
}
_ => return Err(BinaryGcodeError::SerialiseError("heatshrink_01")),
}
loop {
match encoder.poll(&mut output[polled..]) {
HSEPollRes::Empty(sz) => {
polled += sz;
if sz == 0 {
break;
}
}
_ => {
return Err(BinaryGcodeError::SerialiseError(
"heatshrink_02",
))
}
}
}
}
loop {
match encoder.finish() {
HSEFinishRes::Done => break,
HSEFinishRes::More => match encoder.poll(&mut output[polled..]) {
HSEPollRes::Empty(sz) => polled += sz,
_ => {
return Err(BinaryGcodeError::SerialiseError(
"heatshrink_03",
))
}
},
_ => return Err(BinaryGcodeError::SerialiseError("heatshrink_04")),
}
}
output.resize(polled, 0u8);
Ok(output.into_boxed_slice())
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
Checksum, {DeserialisedResult, Deserialiser},
};
#[test]
pub fn serde_gcode_none() {
let header = serialise_file_header(1, Checksum::Crc32);
let gcode = "M73 P0 R30";
let block = serialise_block(
BlockKind::GCode,
CompressionAlgorithm::None,
Encoding::Ascii,
Checksum::Crc32,
&[],
gcode.as_bytes(),
)
.unwrap();
let mut deserialiser = Deserialiser::default();
deserialiser.digest(&header);
deserialiser.digest(&block);
loop {
let r = deserialiser.deserialise().unwrap();
match r {
DeserialisedResult::FileHeader(_) => {}
DeserialisedResult::Block(_) => {}
DeserialisedResult::MoreBytesRequired(_) => {
break;
}
}
}
}
#[test]
pub fn serde_gcode_deflate() {
let header = serialise_file_header(1, Checksum::Crc32);
let gcode = "M73 P0 R30";
let block = serialise_block(
BlockKind::GCode,
CompressionAlgorithm::Deflate,
Encoding::Ascii,
Checksum::Crc32,
&[],
gcode.as_bytes(),
)
.unwrap();
let mut deserialiser = Deserialiser::default();
deserialiser.digest(&header);
deserialiser.digest(&block);
loop {
let r = deserialiser.deserialise().unwrap();
match r {
DeserialisedResult::FileHeader(_) => {}
DeserialisedResult::Block(_) => {}
DeserialisedResult::MoreBytesRequired(_) => {
break;
}
}
}
}
#[test]
pub fn serde_gcode_deflate_no_crc() {
let header = serialise_file_header(1, Checksum::None);
let gcode = "M73 P0 R30";
let block = serialise_block(
BlockKind::GCode,
CompressionAlgorithm::Deflate,
Encoding::Ascii,
Checksum::None,
&[],
gcode.as_bytes(),
)
.unwrap();
let mut deserialiser = Deserialiser::default();
deserialiser.digest(&header);
deserialiser.digest(&block);
loop {
let r = deserialiser.deserialise().unwrap();
match r {
DeserialisedResult::FileHeader(_) => {}
DeserialisedResult::Block(_) => {}
DeserialisedResult::MoreBytesRequired(_) => {
break;
}
}
}
}
}