Cleanup before network implementation
This commit is contained in:
@@ -1,11 +1,126 @@
|
||||
pub mod ecs;
|
||||
mod server;
|
||||
|
||||
/// One sensor sample on the wire.
|
||||
///
|
||||
/// Fixed 38-byte little-endian layout — same on x86_64 and aarch64 (the two
|
||||
/// evaluation hosts), so encode/decode is effectively a memcpy.
|
||||
///
|
||||
/// ```text
|
||||
/// offset size field
|
||||
/// ------ ---- --------------------------
|
||||
/// 0 16 device_id (UUID)
|
||||
/// 16 2 data_stream_id (u16)
|
||||
/// 18 8 raw_value (f64)
|
||||
/// 26 8 timestamp_us (u64)
|
||||
/// 34 4 sequence_number (u32)
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, Copy, PartialEq)]
|
||||
pub struct QuicMessage{
|
||||
pub struct QuicMessage {
|
||||
pub device_id: uuid::Uuid,
|
||||
pub data_stream_id: u16,
|
||||
pub raw_value: f64,
|
||||
pub timestamp_us: u64,
|
||||
pub sequence_number: u32,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum WireError {
|
||||
#[error("expected exactly {expected} bytes, got {got}")]
|
||||
BadLength { expected: usize, got: usize },
|
||||
}
|
||||
|
||||
impl QuicMessage {
|
||||
/// Bytes on the wire — fixed-size, no length prefix.
|
||||
pub const WIRE_SIZE: usize = 38;
|
||||
|
||||
pub fn encode_to(&self, buf: &mut [u8]) -> Result<(), WireError> {
|
||||
if buf.len() != Self::WIRE_SIZE {
|
||||
return Err(WireError::BadLength {
|
||||
expected: Self::WIRE_SIZE,
|
||||
got: buf.len(),
|
||||
});
|
||||
}
|
||||
buf[0..16].copy_from_slice(self.device_id.as_bytes());
|
||||
buf[16..18].copy_from_slice(&self.data_stream_id.to_le_bytes());
|
||||
buf[18..26].copy_from_slice(&self.raw_value.to_le_bytes());
|
||||
buf[26..34].copy_from_slice(&self.timestamp_us.to_le_bytes());
|
||||
buf[34..38].copy_from_slice(&self.sequence_number.to_le_bytes());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; Self::WIRE_SIZE] {
|
||||
let mut buf = [0u8; Self::WIRE_SIZE];
|
||||
self.encode_to(&mut buf).expect("WIRE_SIZE buffer is exactly sized");
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn decode(buf: &[u8]) -> Result<Self, WireError> {
|
||||
if buf.len() != Self::WIRE_SIZE {
|
||||
return Err(WireError::BadLength {
|
||||
expected: Self::WIRE_SIZE,
|
||||
got: buf.len(),
|
||||
});
|
||||
}
|
||||
let mut id_bytes = [0u8; 16];
|
||||
id_bytes.copy_from_slice(&buf[0..16]);
|
||||
Ok(Self {
|
||||
device_id: uuid::Uuid::from_bytes(id_bytes),
|
||||
data_stream_id: u16::from_le_bytes(buf[16..18].try_into().unwrap()),
|
||||
raw_value: f64::from_le_bytes(buf[18..26].try_into().unwrap()),
|
||||
timestamp_us: u64::from_le_bytes(buf[26..34].try_into().unwrap()),
|
||||
sequence_number: u32::from_le_bytes(buf[34..38].try_into().unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn wire_size_matches_fields() {
|
||||
assert_eq!(QuicMessage::WIRE_SIZE, 16 + 2 + 8 + 8 + 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_preserves_all_fields() {
|
||||
let msg = QuicMessage {
|
||||
device_id: uuid::Uuid::from_u128(0x0123456789abcdef_fedcba9876543210),
|
||||
data_stream_id: 0xBEEF,
|
||||
raw_value: -273.15,
|
||||
timestamp_us: 1_700_000_000_000_001,
|
||||
sequence_number: 42,
|
||||
};
|
||||
let bytes = msg.to_bytes();
|
||||
assert_eq!(bytes.len(), QuicMessage::WIRE_SIZE);
|
||||
let decoded = QuicMessage::decode(&bytes).unwrap();
|
||||
assert_eq!(msg, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_rejects_wrong_length() {
|
||||
assert!(matches!(
|
||||
QuicMessage::decode(&[0u8; 37]),
|
||||
Err(WireError::BadLength { expected: 38, got: 37 })
|
||||
));
|
||||
assert!(matches!(
|
||||
QuicMessage::decode(&[0u8; 39]),
|
||||
Err(WireError::BadLength { expected: 38, got: 39 })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_layout_is_little_endian() {
|
||||
let msg = QuicMessage {
|
||||
device_id: uuid::Uuid::nil(),
|
||||
data_stream_id: 0x0102,
|
||||
raw_value: 0.0,
|
||||
timestamp_us: 0,
|
||||
sequence_number: 0x04030201,
|
||||
};
|
||||
let bytes = msg.to_bytes();
|
||||
assert_eq!(&bytes[16..18], &[0x02, 0x01]);
|
||||
assert_eq!(&bytes[34..38], &[0x01, 0x02, 0x03, 0x04]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user