allow parsing packet that does not contain full header

This commit is contained in:
Vinzenz Schroeter 2025-07-13 13:17:59 +02:00
parent 0117478751
commit b8dc57b1aa
3 changed files with 68 additions and 60 deletions

50
src/packet/header.rs Normal file
View file

@ -0,0 +1,50 @@
/// A raw header.
///
/// The header specifies the kind of command, the size of the payload and where to display the
/// payload, where applicable.
///
/// Because the meaning of most fields depend on the command, there are no speaking names for them.
///
/// The contained values are in platform endian-ness and may need to be converted before sending.
#[derive(Copy, Clone, Debug, PartialEq, Default, Hash)]
#[repr(C)]
pub struct Header {
/// The first two bytes specify which command this packet represents.
pub command_code: u16,
/// First command-specific value
pub a: u16,
/// Second command-specific value
pub b: u16,
/// Third command-specific value
pub c: u16,
/// Fourth command-specific value
pub d: u16,
}
impl Header {
pub fn read_from(value: &[u8]) -> Option<(Header, usize)> {
const NULL_U16_SLICE: &[u8] = &[0, 0];
fn convert(slice: &[u8]) -> u16 {
debug_assert_eq!(slice.len(), size_of::<u16>());
u16::from_be_bytes([slice[0], slice[1]])
}
let command_code = value.get(0..=1).map(convert)?;
let a = value.get(2..=3).map(convert);
let b = value.get(4..=5).map(convert);
let c = value.get(6..=7).map(convert);
let d = value.get(8..=9).map(convert);
let read_size = 2 + 2 * [a, b, c, d].iter().flatten().count();
let header = Header {
command_code,
a: a.unwrap_or(0),
b: b.unwrap_or(0),
c: c.unwrap_or(0),
d: d.unwrap_or(0),
};
Some((header, read_size))
}
}

10
src/packet/mod.rs Normal file
View file

@ -0,0 +1,10 @@
mod header;
mod packet;
pub use self::header::Header;
pub use self::packet::Packet;
/// The raw payload.
///
/// Should probably only be used directly to use features not exposed by the library.
pub type Payload = Vec<u8>;

View file

@ -1,36 +1,6 @@
use crate::{command_code::CommandCode, Grid, Origin, Tiles};
use crate::{command_code::CommandCode, Grid, Header, Origin, Payload, Tiles};
use log::trace;
use std::{mem::size_of, num::TryFromIntError};
/// A raw header.
///
/// The header specifies the kind of command, the size of the payload and where to display the
/// payload, where applicable.
///
/// Because the meaning of most fields depend on the command, there are no speaking names for them.
///
/// The contained values are in platform endian-ness and may need to be converted before sending.
#[derive(Copy, Clone, Debug, PartialEq, Default, Hash)]
#[repr(C)]
pub struct Header {
/// The first two bytes specify which command this packet represents.
pub command_code: u16,
/// First command-specific value
pub a: u16,
/// Second command-specific value
pub b: u16,
/// Third command-specific value
pub c: u16,
/// Fourth command-specific value
pub d: u16,
}
pub const HEADER_SIZE: usize = size_of::<Header>();
/// The raw payload.
///
/// Should probably only be used directly to use features not exposed by the library.
pub type Payload = Vec<u8>;
use std::num::TryFromIntError;
/// The raw packet.
///
@ -71,29 +41,14 @@ impl TryFrom<&[u8]> for Packet {
///
/// returns: `Error` if slice is not long enough to be a [Packet]
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() < HEADER_SIZE {
return Err(SliceSmallerThanHeader);
}
let (header, read_bytes) =
Header::read_from(value).ok_or(SliceSmallerThanHeader)?;
let header = {
let command_code = Self::u16_from_be_slice(&value[0..=1]);
let a = Self::u16_from_be_slice(&value[2..=3]);
let b = Self::u16_from_be_slice(&value[4..=5]);
let c = Self::u16_from_be_slice(&value[6..=7]);
let d = Self::u16_from_be_slice(&value[8..=9]);
Header {
command_code,
a,
b,
c,
d,
}
};
let payload = if value.len() <= HEADER_SIZE {
let payload = &value[read_bytes..];
let payload = if payload.is_empty() {
None
} else {
Some(value[HEADER_SIZE..].to_vec())
Some(payload.to_vec())
};
trace!("loaded packet {header:?}");
@ -147,14 +102,7 @@ impl Packet {
/// Returns the amount of bytes this packet takes up when serialized.
#[must_use]
pub fn size(&self) -> usize {
HEADER_SIZE + self.payload.as_ref().map_or(0, Vec::len)
}
fn u16_from_be_slice(slice: &[u8]) -> u16 {
let mut bytes = [0u8; 2];
bytes[0] = slice[0];
bytes[1] = slice[1];
u16::from_be_bytes(bytes)
size_of::<Header>() + self.payload.as_ref().map_or(0, Vec::len)
}
pub(crate) fn origin_grid_to_packet<T>(