rusty-telemetry/src/binary_utils.rs
Jan-Peter v. Hunnius 56d3169033 fix: resolve 7 code review issues from v0.4.x
- Fix KSUDP clutch field reading fuel at offset 59 (KSUDP has no clutch input)
- Replace Notify with watch::channel for reliable shutdown (no missed wakeups)
- Deduplicate read_u16 from parser_pcars into shared binary_utils
- Remove dead Display impl for FeedSource
- Remove unused read_i8 helper from binary_utils
- Change Vec<Instant> to [Instant; 50] in FeedState (eliminate heap alloc)
- Cache rarely-changing strings by driver_id in Telemetry Tool parser
2026-06-04 11:12:55 +02:00

107 lines
3.9 KiB
Rust

//! Shared binary reading helpers for UDP packet parsers.
//!
//! All helpers use `get()` for safe bounds checking — they return a zero/empty
//! fallback instead of panicking when the offset is out of bounds.
use crate::telemetry_frame::NUM_WHEELS;
/// Read a `bool` (1 byte) from the data at the given offset.
/// Returns `false` if the offset is out of bounds.
pub fn read_bool(data: &[u8], offset: usize) -> bool {
data.get(offset).map(|&b| b != 0).unwrap_or(false)
}
/// Read a little-endian `u32` from the data at the given offset.
/// Returns 0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_u32(data: &[u8], offset: usize) -> u32 {
let buf: [u8; 4] = data
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 4]);
u32::from_le_bytes(buf)
}
/// Read a little-endian `i32` from the data at the given offset.
/// Returns 0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_i32(data: &[u8], offset: usize) -> i32 {
let buf: [u8; 4] = data
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 4]);
i32::from_le_bytes(buf)
}
/// Read a little-endian `i16` from the data at the given offset.
/// Returns 0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_i16(data: &[u8], offset: usize) -> i16 {
let buf: [u8; 2] = data
.get(offset..offset + 2)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 2]);
i16::from_le_bytes(buf)
}
/// Read a little-endian `u16` from the data at the given offset.
/// Returns 0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_u16(data: &[u8], offset: usize) -> u16 {
let buf: [u8; 2] = data
.get(offset..offset + 2)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 2]);
u16::from_le_bytes(buf)
}
/// Read a little-endian `f32` from the data at the given offset.
/// Returns 0.0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_f32(data: &[u8], offset: usize) -> f32 {
let buf: [u8; 4] = data
.get(offset..offset + 4)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 4]);
f32::from_le_bytes(buf)
}
/// Read a little-endian `f64` from the data at the given offset.
/// Returns 0.0 if the offset is out of bounds (safe fallback for malformed packets).
pub fn read_f64(data: &[u8], offset: usize) -> f64 {
let buf: [u8; 8] = data
.get(offset..offset + 8)
.and_then(|s| s.try_into().ok())
.unwrap_or([0u8; 8]);
f64::from_le_bytes(buf)
}
/// Read an array of 4 little-endian `f32` from the data at the given offset.
pub fn read_f32_4(data: &[u8], offset: usize) -> [f32; NUM_WHEELS] {
let mut arr = [0.0f32; NUM_WHEELS];
for i in 0..NUM_WHEELS {
arr[i] = read_f32(data, offset + i * 4);
}
arr
}
/// Read an array of 2 little-endian `f32` from the data at the given offset.
pub fn read_f32_2(data: &[u8], offset: usize) -> [f32; 2] {
[read_f32(data, offset), read_f32(data, offset + 4)]
}
/// Read an array of 3 little-endian `f32` from the data at the given offset.
pub fn read_f32_3(data: &[u8], offset: usize) -> [f32; 3] {
[read_f32(data, offset), read_f32(data, offset + 4), read_f32(data, offset + 8)]
}
/// Read an array of 3 little-endian `i32` from the data at the given offset.
pub fn read_i32_3(data: &[u8], offset: usize) -> [i32; 3] {
[read_i32(data, offset), read_i32(data, offset + 4), read_i32(data, offset + 8)]
}
/// Read a null-terminated/padded ASCII string.
/// Returns an empty string if the offset is out of bounds.
pub fn read_string(data: &[u8], offset: usize, len: usize) -> String {
let slice = match data.get(offset..offset + len) {
Some(s) => s,
None => return String::new(),
};
let end = slice.iter().position(|&b| b == 0).unwrap_or(len);
String::from_utf8_lossy(&slice[..end]).to_string()
}