1use crate::error::BoxedError;
10use alloc::boxed::Box;
11use alloc::vec::Vec;
12use core::error::Error;
13use core::fmt::{self, Display, Formatter};
14
15#[cfg(feature = "std")]
16use {
17 std::fs::File,
18 std::io::{Seek, SeekFrom},
19};
20
21pub trait Ext4Read {
26 fn read(
32 &mut self,
33 start_byte: u64,
34 dst: &mut [u8],
35 ) -> Result<(), BoxedError>;
36}
37
38#[cfg(feature = "std")]
39impl Ext4Read for File {
40 fn read(
41 &mut self,
42 start_byte: u64,
43 dst: &mut [u8],
44 ) -> Result<(), BoxedError> {
45 use std::io::Read;
46
47 self.seek(SeekFrom::Start(start_byte)).map_err(Box::new)?;
48 self.read_exact(dst).map_err(Box::new)?;
49 Ok(())
50 }
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub struct MemIoError {
56 start: u64,
57 read_len: usize,
58 src_len: usize,
59}
60
61impl Display for MemIoError {
62 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
63 write!(
64 f,
65 "failed to read {} bytes at offset {} from a slice of length {}",
66 self.read_len, self.start, self.src_len
67 )
68 }
69}
70
71impl Error for MemIoError {}
72
73impl Ext4Read for Vec<u8> {
74 fn read(
75 &mut self,
76 start_byte: u64,
77 dst: &mut [u8],
78 ) -> Result<(), BoxedError> {
79 read_from_bytes(self, start_byte, dst).ok_or_else(|| {
80 Box::new(MemIoError {
81 start: start_byte,
82 read_len: dst.len(),
83 src_len: self.len(),
84 })
85 .into()
86 })
87 }
88}
89
90fn read_from_bytes(src: &[u8], start_byte: u64, dst: &mut [u8]) -> Option<()> {
91 let start = usize::try_from(start_byte).ok()?;
92 let end = start.checked_add(dst.len())?;
93 let src = src.get(start..end)?;
94 dst.copy_from_slice(src);
95
96 Some(())
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_vec_read() {
105 let mut src = vec![1, 2, 3];
106
107 let mut dst = [0; 3];
108 src.read(0, &mut dst).unwrap();
109 assert_eq!(dst, [1, 2, 3]);
110
111 let mut dst = [0; 2];
112 src.read(1, &mut dst).unwrap();
113 assert_eq!(dst, [2, 3]);
114
115 let err = src.read(4, &mut dst).unwrap_err();
116 assert_eq!(
117 format!("{err}"),
118 format!(
119 "failed to read 2 bytes at offset 4 from a slice of length 3"
120 )
121 );
122 }
123}