| // Licensed under the Apache-2.0 license |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| //! Input/Output traits and utilities. |
| |
| #![no_std] |
| |
| use util_error::{ErrorCode, ErrorModule}; |
| |
| /// The generic IO error module. |
| pub const IO_GENERIC: ErrorModule = ErrorModule::new(0x494F); // ascii: IO |
| /// The read operation is out of bounds. |
| pub const IO_GENERIC_READ_OUT_OF_BOUNDS: ErrorCode = |
| IO_GENERIC.from_pw(1, pw_status::Error::OutOfRange); |
| |
| /// Trait for random access read operations. |
| pub trait RandomRead { |
| type Error; |
| /// Reads data from the source into the destination buffer. |
| /// |
| /// # Arguments |
| /// * `start_addr`: The offset from which to start reading. |
| /// * `dst`: The buffer to read data into. |
| fn read(&mut self, start_addr: usize, dst: &mut [u8]) -> Result<(), Self::Error>; |
| |
| /// Returns the total size of the readable data in bytes. |
| fn size(&mut self) -> Result<usize, Self::Error>; |
| } |
| |
| impl RandomRead for &[u8] { |
| type Error = ErrorCode; |
| fn read(&mut self, start_addr: usize, dst: &mut [u8]) -> Result<(), Self::Error> { |
| // Explicit wrapping add. Overflows are expected to |
| // be detected in the indexing operation |
| let end_addr = start_addr.wrapping_add(dst.len()); |
| let src = self |
| .get(start_addr..end_addr) |
| .ok_or(IO_GENERIC_READ_OUT_OF_BOUNDS)?; |
| dst.copy_from_slice(src); |
| Ok(()) |
| } |
| fn size(&mut self) -> Result<usize, Self::Error> { |
| Ok(self.len()) |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| |
| #[test] |
| fn should_read() { |
| let mut src: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; |
| let mut dst: [u8; 3] = [0; 3]; |
| assert!(src.read(6, &mut dst).is_ok()); |
| assert_eq!(&dst, &[7, 8, 9]); |
| assert_eq!(RandomRead::size(&mut src).unwrap(), 9); |
| } |
| |
| #[test] |
| fn should_fail() { |
| let mut src: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; |
| let mut dst: [u8; 3] = [0; 3]; |
| assert!(src.read(7, &mut dst).is_err()); |
| } |
| |
| #[test] |
| fn invalid_start_address_should_not_panic() { |
| let mut src: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8]; |
| let mut dst: [u8; 4] = [0; 4]; |
| // Set `start_addr` so that adding `dst.len()` causes it to |
| // wrap around and become smaller than `src.len()` |
| let start_addr: usize = usize::MAX - (dst.len() - 1); |
| // Should not panic |
| let result = src.read(start_addr, &mut dst); |
| assert!(result.is_err()); |
| } |
| } |