/// Big-endian binary read helpers with bounds checking. use crate::error::{Result, Xex2Error}; /// Reads a big-endian `u32` from `data` at the given byte `offset`. pub fn read_u32_be(data: &[u8], offset: usize) -> Result { let end = offset + 4; if end > data.len() { return Err(Xex2Error::FileTooSmall { expected: end, actual: data.len(), }); } Ok(u32::from_be_bytes([ data[offset], data[offset + 1], data[offset + 2], data[offset + 3], ])) } /// Reads a big-endian `u16` from `data` at the given byte `offset`. pub fn read_u16_be(data: &[u8], offset: usize) -> Result { let end = offset + 2; if end > data.len() { return Err(Xex2Error::FileTooSmall { expected: end, actual: data.len(), }); } Ok(u16::from_be_bytes([data[offset], data[offset + 1]])) } /// Reads a single byte from `data` at the given `offset`. pub fn read_u8(data: &[u8], offset: usize) -> Result { if offset >= data.len() { return Err(Xex2Error::FileTooSmall { expected: offset + 1, actual: data.len(), }); } Ok(data[offset]) } /// Returns a byte slice of `len` bytes from `data` starting at `offset`. pub fn read_bytes(data: &[u8], offset: usize, len: usize) -> Result<&[u8]> { let end = offset + len; if end > data.len() { return Err(Xex2Error::FileTooSmall { expected: end, actual: data.len(), }); } Ok(&data[offset..end]) } #[cfg(test)] mod tests { use super::*; #[test] fn test_read_u32_be() { let data = [0x58, 0x45, 0x58, 0x32]; assert_eq!(read_u32_be(&data, 0).unwrap(), 0x58455832); } #[test] fn test_read_u32_be_with_offset() { let data = [0x00, 0x00, 0x58, 0x45, 0x58, 0x32]; assert_eq!(read_u32_be(&data, 2).unwrap(), 0x58455832); } #[test] fn test_read_u16_be() { let data = [0x12, 0x34]; assert_eq!(read_u16_be(&data, 0).unwrap(), 0x1234); } #[test] fn test_read_u8() { let data = [0xAB, 0xCD]; assert_eq!(read_u8(&data, 1).unwrap(), 0xCD); } #[test] fn test_read_bytes() { let data = [0x01, 0x02, 0x03, 0x04, 0x05]; assert_eq!(read_bytes(&data, 1, 3).unwrap(), &[0x02, 0x03, 0x04]); } #[test] fn test_read_u32_be_out_of_bounds() { let data = [0x00, 0x01]; let err = read_u32_be(&data, 0).unwrap_err(); assert!(matches!(err, Xex2Error::FileTooSmall { expected: 4, actual: 2 })); } #[test] fn test_read_u16_be_out_of_bounds() { let data = [0x00]; let err = read_u16_be(&data, 0).unwrap_err(); assert!(matches!(err, Xex2Error::FileTooSmall { expected: 2, actual: 1 })); } #[test] fn test_read_u8_out_of_bounds() { let data: [u8; 0] = []; let err = read_u8(&data, 0).unwrap_err(); assert!(matches!(err, Xex2Error::FileTooSmall { expected: 1, actual: 0 })); } #[test] fn test_read_bytes_out_of_bounds() { let data = [0x01, 0x02]; let err = read_bytes(&data, 1, 3).unwrap_err(); assert!(matches!(err, Xex2Error::FileTooSmall { expected: 4, actual: 2 })); } }