This commit is contained in:
Vinzenz Schroeter 2025-07-19 17:02:06 +02:00
parent 9ac5ff7186
commit 95b31125fc
16 changed files with 143 additions and 88 deletions

View file

@ -73,10 +73,10 @@ impl TryFrom<BitmapCommand> for Packet {
}
impl LoadableFromPacket for BitmapCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let code = CommandCode::try_from(header.command_code)?;
let compression = BitmapCommand::compression_for_command_code(code)
.ok_or(InvalidCommandCodeError(header.command_code))?;

View file

@ -23,12 +23,12 @@ use std::fmt::Debug;
pub struct BitmapLegacyCommand;
impl LoadableFromPacket for BitmapLegacyCommand {
fn load(
fn load<'t>(
header: &Header,
_: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
check_command_code_only(header, CommandCode::BitmapLegacy)?;
Ok((Self, 0))
Ok((Self, payload))
}
}

View file

@ -81,10 +81,10 @@ impl TryFrom<&BitVecCommand> for Packet {
}
impl LoadableFromPacket for BitVecCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let Header {
command_code,
a: offset,

View file

@ -48,10 +48,10 @@ impl From<BrightnessGrid> for BrightnessGridCommand {
}
impl LoadableFromPacket for BrightnessGridCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let Header {
command_code,
a: x,
@ -70,10 +70,11 @@ impl LoadableFromPacket for BrightnessGridCommand {
});
};
let (payload, remaining) = payload.split_at(expected_size);
let grid = ByteGrid::from_raw_parts_unchecked(
width as usize,
height as usize,
payload[..expected_size].to_vec(),
payload.to_vec(),
);
let grid = match BrightnessGrid::try_from(grid) {
Ok(grid) => grid,
@ -85,7 +86,7 @@ impl LoadableFromPacket for BrightnessGridCommand {
grid,
origin: Origin::new(x as usize, y as usize),
},
expected_size,
remaining,
))
}
}

View file

@ -49,10 +49,10 @@ impl TryFrom<&CharGridCommand> for Packet {
}
impl LoadableFromPacket for CharGridCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let Header {
command_code,
a: origin_x,
@ -72,9 +72,9 @@ impl LoadableFromPacket for CharGridCommand {
}
// TODO this does not work as char != byte
let payload = String::from_utf8(payload[..expected].to_vec())?
.chars()
.collect();
let (payload, remaining) = payload.split_at(expected);
let payload = String::from_utf8(payload.to_vec())?.chars().collect();
Ok((
Self {
origin: Origin::new(origin_x as usize, origin_y as usize),
@ -84,7 +84,7 @@ impl LoadableFromPacket for CharGridCommand {
payload,
),
},
expected,
remaining,
))
}
}

View file

@ -18,12 +18,12 @@ use std::fmt::Debug;
pub struct ClearCommand;
impl LoadableFromPacket for ClearCommand {
fn load(
fn load<'t>(
header: &Header,
_: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
check_command_code_only(header, CommandCode::Clear)?;
Ok((Self, 0))
Ok((Self, payload))
}
}

View file

@ -63,10 +63,10 @@ impl TryFrom<&Cp437GridCommand> for Packet {
}
impl LoadableFromPacket for Cp437GridCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let Header {
command_code,
a: origin_x,
@ -84,6 +84,8 @@ impl LoadableFromPacket for Cp437GridCommand {
});
}
let (payload, remaining) = payload.split_at(expected);
Ok((
Self {
origin: Origin::new(origin_x as usize, origin_y as usize),
@ -93,7 +95,7 @@ impl LoadableFromPacket for Cp437GridCommand {
Vec::from(&payload[..expected]),
),
},
expected,
remaining,
))
}
}

View file

@ -20,12 +20,12 @@ use std::fmt::Debug;
pub struct FadeOutCommand;
impl LoadableFromPacket for FadeOutCommand {
fn load(
fn load<'t>(
header: &Header,
_: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
check_command_code_only(header, CommandCode::FadeOut)?;
Ok((Self, 0))
Ok((Self, payload))
}
}

View file

@ -42,10 +42,10 @@ impl From<&GlobalBrightnessCommand> for Packet {
}
impl LoadableFromPacket for GlobalBrightnessCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let Header {
command_code,
a,
@ -67,9 +67,11 @@ impl LoadableFromPacket for GlobalBrightnessCommand {
},
)?;
let (_, remaining) = payload.split_at(1);
let brightness = Brightness::try_from(brightness)
.map_err(|_| TryFromPacketError::InvalidBrightness(brightness))?;
Ok((Self { brightness }, 1))
Ok((Self { brightness }, remaining))
}
}

View file

@ -20,12 +20,12 @@ use std::fmt::Debug;
pub struct HardResetCommand;
impl LoadableFromPacket for HardResetCommand {
fn load(
fn load<'t>(
header: &Header,
_: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
check_command_code_only(header, CommandCode::HardReset)?;
Ok((Self, 0))
Ok((Self, payload))
}
}

View file

@ -99,24 +99,26 @@ impl<
{
}
/// Similar to [`TryFrom<Packet>`].
pub trait LoadableFromPacket: Sized {
/// Load the specified header and payload.
///
/// In case of success, returns a tuple of Self and the number of bytes read from payload.
fn load(
/// In case of success, returns a tuple of Self and the remaining payload.
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError>;
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError>;
/// Load the specified bytes.
///
/// In case of success, returns a tuple of Self and the number of bytes read from the buffer.
fn from_bytes(buf: &[u8]) -> Result<(Self, usize), TryFromPacketError> {
/// In case of success, returns a tuple of Self and the remaining payload.
fn from_bytes<'t>(
buf: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
let (header, bytes_header) =
Header::read_from(buf).ok_or(TryFromPacketError::TooSmall)?;
let (result, bytes_payload) =
Self::load(&header, &buf[bytes_header..])?;
Ok((result, bytes_header + bytes_payload))
let (result, remaining) = Self::load(&header, &buf[bytes_header..])?;
Ok((result, remaining))
}
}
@ -132,13 +134,15 @@ macro_rules! derive_try_from_packet {
fn try_from(packet: Packet) -> Result<Self, Self::Error> {
let payload = packet.payload.as_deref().unwrap_or(&[]);
let (result, read_bytes) = Self::load(&packet.header, payload)?;
//if payload.len() != read_bytes {
// return Err(TryFromPacketError::UnexpectedPayloadSize {
// actual: payload.len(),
// expected: read_bytes,
// });
//}
let (result, remaining) = Self::load(&packet.header, payload)?;
let remaining_len = remaining.len();
if remaining_len != 0 {
let payload_len = payload.len();
return Err(TryFromPacketError::UnexpectedPayloadSize {
actual: payload_len,
expected: payload_len - remaining_len,
});
}
Ok(result)
}
}

View file

@ -1,9 +1,8 @@
use crate::{
command_code::CommandCode, commands::errors::TryFromPacketError,
BitVecCommand, BitmapCommand, BitmapLegacyCommand, BrightnessGridCommand,
CharGridCommand, ClearCommand, Cp437GridCommand, FadeOutCommand,
GlobalBrightnessCommand, HardResetCommand, Header, LoadableFromPacket,
Packet, TryIntoPacketError,
BitVecCommand, BitmapCommand, BrightnessGridCommand, CharGridCommand,
ClearCommand, Cp437GridCommand, FadeOutCommand, GlobalBrightnessCommand,
HardResetCommand, Header, LoadableFromPacket, Packet, TryIntoPacketError,
};
/// This enum contains all commands provided by the library.
@ -28,14 +27,14 @@ pub enum TypedCommand {
}
impl LoadableFromPacket for TypedCommand {
fn load(
fn load<'t>(
header: &Header,
payload: &[u8],
) -> Result<(Self, usize), TryFromPacketError> {
fn load_as_typed<T: LoadableFromPacket + Into<TypedCommand>>(
payload: &'t [u8],
) -> Result<(Self, &'t [u8]), TryFromPacketError> {
fn load_as_typed<'t, T: LoadableFromPacket + Into<TypedCommand>>(
header: &Header,
payload: &[u8],
) -> Result<(TypedCommand, usize), TryFromPacketError> {
payload: &'t [u8],
) -> Result<(TypedCommand, &'t [u8]), TryFromPacketError> {
let (cmd, read) = T::load(header, payload)?;
Ok((cmd.into(), read))
}
@ -64,7 +63,7 @@ impl LoadableFromPacket for TypedCommand {
}
#[allow(deprecated)]
CommandCode::BitmapLegacy => {
load_as_typed::<BitmapLegacyCommand>(header, payload)
load_as_typed::<crate::BitmapLegacyCommand>(header, payload)
}
CommandCode::BitmapLinear
| CommandCode::BitmapLinearOr

View file

@ -4,7 +4,7 @@ use crate::{
};
use log::error;
use lzma::LzmaReader;
use std::io::Read;
use std::io::{IoSliceMut, Read};
pub struct Lzma;
@ -21,13 +21,61 @@ impl CompressionAlgo for Lzma {
fn decompress(
payload: &[u8],
expected_size_hint: usize,
) -> Result<(Vec<u8>, usize), CompressionError> {
) -> Result<(Vec<u8>, &[u8]), CompressionError> {
let mut counted = Counted::new(payload);
let mut output: Vec<u8> = Vec::with_capacity(expected_size_hint);
let mut reader = LzmaReader::new_decompressor(payload)
let mut reader = LzmaReader::new_decompressor(&mut counted)
.map_err(|_| CompressionError::LibraryError)?;
let read = reader
reader
.read_to_end(&mut output)
.map_err(|_| CompressionError::CompressionFailed)?;
Ok((output, read))
let (_, remaining) = payload.split_at(counted.total_read());
Ok((output, remaining))
}
}
pub(crate) struct Counted<R: Read> {
inner: R,
count: usize,
}
impl<R: Read> Counted<R> {
pub(crate) fn new(inner: R) -> Self {
Self { inner, count: 0 }
}
}
impl<R: Read> Counted<R> {
pub(crate) fn total_read(&self) -> usize {
self.count
}
}
impl<R: Read> Read for Counted<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.inner.read(buf).inspect(|count| self.count += *count)
}
fn read_vectored(
&mut self,
bufs: &mut [IoSliceMut<'_>],
) -> std::io::Result<usize> {
self.inner
.read_vectored(bufs)
.inspect(|count| self.count += *count)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
self.inner
.read_to_end(buf)
.inspect(|count| self.count += *count)
}
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
self.inner
.read_to_string(buf)
.inspect(|count| self.count += *count)
}
}

View file

@ -26,17 +26,17 @@ pub(crate) enum CompressionError {
pub(crate) trait CompressionAlgo {
const CODE: CompressionCode;
fn compress(payload: &[u8]) -> Result<Vec<u8>, CompressionError>;
fn decompress(
payload: &[u8],
fn decompress<'t>(
payload: &'t [u8],
expected_size_hint: usize,
) -> Result<(Vec<u8>, usize), CompressionError>;
) -> Result<(Vec<u8>, &'t [u8]), CompressionError>;
}
pub(crate) fn decompress(
pub(crate) fn decompress<'t>(
kind: CompressionCode,
#[allow(unused, reason = "depends on features")] payload: &[u8],
#[allow(unused, reason = "depends on features")] payload: &'t [u8],
expected_size_hint: usize,
) -> Result<(Vec<u8>, usize), CompressionError> {
) -> Result<(Vec<u8>, &'t [u8]), CompressionError> {
match kind {
CompressionCode::Uncompressed => {
uncompressed::Uncompressed::decompress(payload, expected_size_hint)

View file

@ -12,13 +12,12 @@ impl CompressionAlgo for Uncompressed {
Ok(payload.to_vec())
}
fn decompress(
payload: &[u8],
fn decompress<'t>(
payload: &'t [u8],
expected_size_hint: usize,
) -> Result<(Vec<u8>, usize), CompressionError> {
Ok((
payload[..expected_size_hint.min(payload.len())].to_vec(),
expected_size_hint,
))
) -> Result<(Vec<u8>, &'t [u8]), CompressionError> {
let index = expected_size_hint.min(payload.len());
let (payload, remaining) = payload.split_at(index);
Ok((payload.to_vec(), remaining))
}
}

View file

@ -10,7 +10,7 @@ pub(crate) struct Zstd;
impl CompressionAlgo for Zstd {
const CODE: CompressionCode = CompressionCode::Zstd;
fn compress(payload: &[u8]) -> Result<Payload, CompressionError> {
let buf = Vec::with_capacity(payload.len());
let mut encoder = Encoder::new(buf, DEFAULT_COMPRESSION_LEVEL)