//! 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() }