Skip to main content

fs_core/
slice.rs

1//! Slice adapters — view a byte sub-range of any `BlockRead` as its own
2//! device. Useful any time you want to feed a fragment of a larger
3//! device to a consumer that expects a whole block source — partition
4//! probes, image-file extents, mmap-style views, fuzzer harnesses.
5//!
6//! Two variants:
7//!
8//! - [`SliceReader`] borrows the parent, lifetime-tied. Cheaper when the
9//!   parent outlives the slice and you can express that statically.
10//! - [`OwnedSlice`] holds an `Arc` to the parent. Use when the parent's
11//!   lifetime can't be expressed in a borrow (FFI handles, slice handed
12//!   across thread boundaries, etc.).
13//!
14//! Both treat the slice as **read-only** — reads inside the range are
15//! forwarded to the parent, reads outside return [`Error::ShortRead`].
16//! The default `Err(ReadOnly)` write path from [`BlockDevice`] applies.
17
18use crate::block::{BlockDevice, BlockRead};
19use crate::error::{Error, Result};
20use std::sync::Arc;
21
22/// Borrowed slice of a parent `BlockRead`.
23///
24/// `read_at(0, …)` reads `start` of the parent. Reads past `length`
25/// return [`Error::ShortRead`].
26pub struct SliceReader<'a> {
27    parent: &'a (dyn BlockRead + 'a),
28    start: u64,
29    length: u64,
30}
31
32impl<'a> SliceReader<'a> {
33    pub fn new(parent: &'a (dyn BlockRead + 'a), start: u64, length: u64) -> Self {
34        Self {
35            parent,
36            start,
37            length,
38        }
39    }
40
41    /// Byte offset of this slice on the parent device.
42    pub fn start(&self) -> u64 {
43        self.start
44    }
45
46    /// Length of this slice in bytes (== `size_bytes()`).
47    pub fn length(&self) -> u64 {
48        self.length
49    }
50}
51
52impl<'a> BlockRead for SliceReader<'a> {
53    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
54        let want = buf.len() as u64;
55        if offset
56            .checked_add(want)
57            .map(|e| e > self.length)
58            .unwrap_or(true)
59        {
60            return Err(Error::ShortRead {
61                offset,
62                want: buf.len(),
63                got: 0,
64            });
65        }
66        self.parent.read_at(self.start + offset, buf)
67    }
68
69    fn size_bytes(&self) -> u64 {
70        self.length
71    }
72}
73
74/// Slices are read-only by default — even where the parent is writable,
75/// slicing is almost always paired with a read-only inspection or
76/// dispatch workflow.
77impl<'a> BlockDevice for SliceReader<'a> {}
78
79/// Owned slice over an `Arc<dyn BlockRead>`. Use when the parent's
80/// lifetime can't be expressed in a borrow — e.g. when the slice is
81/// handed across an FFI boundary or stored in a long-lived struct.
82pub struct OwnedSlice {
83    parent: Arc<dyn BlockRead>,
84    start: u64,
85    length: u64,
86}
87
88impl OwnedSlice {
89    pub fn new(parent: Arc<dyn BlockRead>, start: u64, length: u64) -> Self {
90        Self {
91            parent,
92            start,
93            length,
94        }
95    }
96
97    pub fn start(&self) -> u64 {
98        self.start
99    }
100
101    pub fn length(&self) -> u64 {
102        self.length
103    }
104}
105
106impl BlockRead for OwnedSlice {
107    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
108        let want = buf.len() as u64;
109        if offset
110            .checked_add(want)
111            .map(|e| e > self.length)
112            .unwrap_or(true)
113        {
114            return Err(Error::ShortRead {
115                offset,
116                want: buf.len(),
117                got: 0,
118            });
119        }
120        self.parent.read_at(self.start + offset, buf)
121    }
122
123    fn size_bytes(&self) -> u64 {
124        self.length
125    }
126}
127
128/// Same rationale as `SliceReader`: read-only by default.
129impl BlockDevice for OwnedSlice {}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use std::sync::Mutex;
135
136    struct Bytes(Mutex<Vec<u8>>);
137    impl BlockRead for Bytes {
138        fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
139            let b = self.0.lock().unwrap();
140            let start = offset as usize;
141            let end = start + buf.len();
142            if end > b.len() {
143                return Err(Error::ShortRead {
144                    offset,
145                    want: buf.len(),
146                    got: b.len().saturating_sub(start),
147                });
148            }
149            buf.copy_from_slice(&b[start..end]);
150            Ok(())
151        }
152        fn size_bytes(&self) -> u64 {
153            self.0.lock().unwrap().len() as u64
154        }
155    }
156
157    #[test]
158    fn slice_reader_rebases_offsets() {
159        let mut v = vec![0u8; 4096];
160        v[2000..2004].copy_from_slice(&[0xAB, 0xCD, 0xEF, 0x01]);
161        let dev = Bytes(Mutex::new(v));
162
163        let slice = SliceReader::new(&dev, 2000, 4);
164        assert_eq!(slice.size_bytes(), 4);
165        assert_eq!(slice.start(), 2000);
166        assert_eq!(slice.length(), 4);
167
168        let mut buf = [0u8; 4];
169        slice.read_at(0, &mut buf).unwrap();
170        assert_eq!(buf, [0xAB, 0xCD, 0xEF, 0x01]);
171    }
172
173    #[test]
174    fn slice_reader_rejects_out_of_bounds() {
175        let dev = Bytes(Mutex::new(vec![0u8; 4096]));
176        let slice = SliceReader::new(&dev, 0, 16);
177        let mut buf = [0u8; 8];
178        match slice.read_at(12, &mut buf) {
179            Err(Error::ShortRead { .. }) => {}
180            other => panic!("expected ShortRead, got {other:?}"),
181        }
182    }
183
184    #[test]
185    fn owned_slice_works_through_arc() {
186        let mut v = vec![0u8; 4096];
187        v[100..104].copy_from_slice(&[0x11, 0x22, 0x33, 0x44]);
188        let dev: Arc<dyn BlockRead> = Arc::new(Bytes(Mutex::new(v)));
189
190        let slice = OwnedSlice::new(dev, 100, 4);
191        assert_eq!(slice.size_bytes(), 4);
192        let mut buf = [0u8; 4];
193        slice.read_at(0, &mut buf).unwrap();
194        assert_eq!(buf, [0x11, 0x22, 0x33, 0x44]);
195    }
196
197    #[test]
198    fn slices_reject_writes_via_blockdevice_default() {
199        let dev = Bytes(Mutex::new(vec![0u8; 16]));
200        let slice = SliceReader::new(&dev, 0, 8);
201        let err = BlockDevice::write_at(&slice, 0, &[1u8; 4]).unwrap_err();
202        assert!(matches!(err, Error::ReadOnly));
203    }
204}