1#![allow(clippy::len_without_is_empty)]
2
3use core::convert::TryInto;
4use core::ops::Range;
5use core::{mem, result};
6
7use crate::pod::{from_bytes, slice_from_bytes, Pod};
8
9type Result<T> = result::Result<T, ()>;
10
11/// A trait for reading references to [`Pod`] types from a block of data.
12///
13/// This allows parsers to handle both of these cases:
14/// - the block of data exists in memory, and it is desirable
15/// to use references to this block instead of copying it,
16/// - the block of data exists in storage, and it is desirable
17/// to read on demand to minimize I/O and memory usage.
18///
19/// The methods accept `self` by value because `Self` is expected to behave
20/// similar to a reference: it may be a reference with a lifetime of `'a`,
21/// or it may be a wrapper of a reference.
22///
23/// The `Clone` and `Copy` bounds are for convenience, and since `Self` is
24/// expected to be similar to a reference, these are easily satisfied.
25///
26/// Object file parsers typically use offsets to locate the structures
27/// in the block, and will most commonly use the `*_at` methods to
28/// read a structure at a known offset.
29///
30/// Occasionally file parsers will need to treat the block as a stream,
31/// and so convenience methods are provided that update an offset with
32/// the size that was read.
33//
34// An alternative would be for methods to accept `&mut self` and use a
35// `seek` method instead of the `offset` parameters, but this is less
36// convenient for implementers.
37pub trait ReadRef<'a>: Clone + Copy {
38 /// The total size of the block of data.
39 fn len(self) -> Result<u64>;
40
41 /// Get a reference to a `u8` slice at the given offset.
42 ///
43 /// Returns an error if offset or size are out of bounds.
44 fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]>;
45
46 /// Get a reference to a delimited `u8` slice which starts at range.start.
47 ///
48 /// Does not include the delimiter.
49 ///
50 /// Returns an error if the range is out of bounds or the delimiter is
51 /// not found in the range.
52 fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8]>;
53
54 /// Get a reference to a `u8` slice at the given offset, and update the offset.
55 ///
56 /// Returns an error if offset or size are out of bounds.
57 fn read_bytes(self, offset: &mut u64, size: u64) -> Result<&'a [u8]> {
58 let bytes = self.read_bytes_at(*offset, size)?;
59 *offset = offset.wrapping_add(size);
60 Ok(bytes)
61 }
62
63 /// Get a reference to a `Pod` type at the given offset, and update the offset.
64 ///
65 /// Returns an error if offset or size are out of bounds.
66 ///
67 /// The default implementation uses `read_bytes`, and returns an error if
68 /// `read_bytes` does not return bytes with the correct alignment for `T`.
69 /// Implementors may want to provide their own implementation that ensures
70 /// the alignment can be satisfied. Alternatively, only use this method with
71 /// types that do not need alignment (see the `unaligned` feature of this crate).
72 fn read<T: Pod>(self, offset: &mut u64) -> Result<&'a T> {
73 let size = mem::size_of::<T>().try_into().map_err(|_| ())?;
74 let bytes = self.read_bytes(offset, size)?;
75 let (t, _) = from_bytes(bytes)?;
76 Ok(t)
77 }
78
79 /// Get a reference to a `Pod` type at the given offset.
80 ///
81 /// Returns an error if offset or size are out of bounds.
82 ///
83 /// Also see the `read` method for information regarding alignment of `T`.
84 fn read_at<T: Pod>(self, mut offset: u64) -> Result<&'a T> {
85 self.read(&mut offset)
86 }
87
88 /// Get a reference to a slice of a `Pod` type at the given offset, and update the offset.
89 ///
90 /// Returns an error if offset or size are out of bounds.
91 ///
92 /// Also see the `read` method for information regarding alignment of `T`.
93 fn read_slice<T: Pod>(self, offset: &mut u64, count: usize) -> Result<&'a [T]> {
94 let size = count
95 .checked_mul(mem::size_of::<T>())
96 .ok_or(())?
97 .try_into()
98 .map_err(|_| ())?;
99 let bytes = self.read_bytes(offset, size)?;
100 let (t, _) = slice_from_bytes(bytes, count)?;
101 Ok(t)
102 }
103
104 /// Get a reference to a slice of a `Pod` type at the given offset.
105 ///
106 /// Returns an error if offset or size are out of bounds.
107 ///
108 /// Also see the `read` method for information regarding alignment of `T`.
109 fn read_slice_at<T: Pod>(self, mut offset: u64, count: usize) -> Result<&'a [T]> {
110 self.read_slice(&mut offset, count)
111 }
112}
113
114impl<'a> ReadRef<'a> for &'a [u8] {
115 fn len(self) -> Result<u64> {
116 self.len().try_into().map_err(|_| ())
117 }
118
119 fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8]> {
120 let offset: usize = offset.try_into().map_err(|_| ())?;
121 let size: usize = size.try_into().map_err(|_| ())?;
122 self.get(offset..).ok_or(())?.get(..size).ok_or(())
123 }
124
125 fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8]> {
126 let start: usize = range.start.try_into().map_err(|_| ())?;
127 let end: usize = range.end.try_into().map_err(|_| ())?;
128 let bytes: &[u8] = self.get(start..end).ok_or(())?;
129 match memchr::memchr(needle:delimiter, haystack:bytes) {
130 Some(len: usize) => {
131 // This will never fail.
132 bytes.get(..len).ok_or(())
133 }
134 None => Err(()),
135 }
136 }
137}
138