1 | //! Working with byte slices that have an associated endianity. |
2 | |
3 | #[cfg (feature = "read" )] |
4 | use alloc::borrow::Cow; |
5 | #[cfg (feature = "read" )] |
6 | use alloc::string::String; |
7 | use core::ops::{Deref, Range, RangeFrom, RangeTo}; |
8 | use core::str; |
9 | |
10 | use crate::endianity::Endianity; |
11 | use crate::read::{Error, Reader, ReaderOffsetId, Result}; |
12 | |
13 | /// A `&[u8]` slice with endianity metadata. |
14 | /// |
15 | /// This implements the `Reader` trait, which is used for all reading of DWARF sections. |
16 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] |
17 | pub struct EndianSlice<'input, Endian> |
18 | where |
19 | Endian: Endianity, |
20 | { |
21 | slice: &'input [u8], |
22 | endian: Endian, |
23 | } |
24 | |
25 | impl<'input, Endian> EndianSlice<'input, Endian> |
26 | where |
27 | Endian: Endianity, |
28 | { |
29 | /// Construct a new `EndianSlice` with the given slice and endianity. |
30 | #[inline ] |
31 | pub fn new(slice: &'input [u8], endian: Endian) -> EndianSlice<'input, Endian> { |
32 | EndianSlice { slice, endian } |
33 | } |
34 | |
35 | /// Return a reference to the raw slice. |
36 | #[inline ] |
37 | #[doc (hidden)] |
38 | #[deprecated (note = "Method renamed to EndianSlice::slice; use that instead." )] |
39 | pub fn buf(&self) -> &'input [u8] { |
40 | self.slice |
41 | } |
42 | |
43 | /// Return a reference to the raw slice. |
44 | #[inline ] |
45 | pub fn slice(&self) -> &'input [u8] { |
46 | self.slice |
47 | } |
48 | |
49 | /// Split the slice in two at the given index, resulting in the tuple where |
50 | /// the first item has range [0, idx), and the second has range [idx, |
51 | /// len). Panics if the index is out of bounds. |
52 | #[inline ] |
53 | pub fn split_at( |
54 | &self, |
55 | idx: usize, |
56 | ) -> (EndianSlice<'input, Endian>, EndianSlice<'input, Endian>) { |
57 | (self.range_to(..idx), self.range_from(idx..)) |
58 | } |
59 | |
60 | /// Find the first occurrence of a byte in the slice, and return its index. |
61 | #[inline ] |
62 | pub fn find(&self, byte: u8) -> Option<usize> { |
63 | self.slice.iter().position(|ch| *ch == byte) |
64 | } |
65 | |
66 | /// Return the offset of the start of the slice relative to the start |
67 | /// of the given slice. |
68 | #[inline ] |
69 | pub fn offset_from(&self, base: EndianSlice<'input, Endian>) -> usize { |
70 | let base_ptr = base.slice.as_ptr() as *const u8 as usize; |
71 | let ptr = self.slice.as_ptr() as *const u8 as usize; |
72 | debug_assert!(base_ptr <= ptr); |
73 | debug_assert!(ptr + self.slice.len() <= base_ptr + base.slice.len()); |
74 | ptr - base_ptr |
75 | } |
76 | |
77 | /// Converts the slice to a string using `str::from_utf8`. |
78 | /// |
79 | /// Returns an error if the slice contains invalid characters. |
80 | #[inline ] |
81 | pub fn to_string(&self) -> Result<&'input str> { |
82 | str::from_utf8(self.slice).map_err(|_| Error::BadUtf8) |
83 | } |
84 | |
85 | /// Converts the slice to a string, including invalid characters, |
86 | /// using `String::from_utf8_lossy`. |
87 | #[cfg (feature = "read" )] |
88 | #[inline ] |
89 | pub fn to_string_lossy(&self) -> Cow<'input, str> { |
90 | String::from_utf8_lossy(self.slice) |
91 | } |
92 | |
93 | #[inline ] |
94 | fn read_slice(&mut self, len: usize) -> Result<&'input [u8]> { |
95 | if self.slice.len() < len { |
96 | Err(Error::UnexpectedEof(self.offset_id())) |
97 | } else { |
98 | let val = &self.slice[..len]; |
99 | self.slice = &self.slice[len..]; |
100 | Ok(val) |
101 | } |
102 | } |
103 | } |
104 | |
105 | /// # Range Methods |
106 | /// |
107 | /// Unfortunately, `std::ops::Index` *must* return a reference, so we can't |
108 | /// implement `Index<Range<usize>>` to return a new `EndianSlice` the way we would |
109 | /// like to. Instead, we abandon fancy indexing operators and have these plain |
110 | /// old methods. |
111 | impl<'input, Endian> EndianSlice<'input, Endian> |
112 | where |
113 | Endian: Endianity, |
114 | { |
115 | /// Take the given `start..end` range of the underlying slice and return a |
116 | /// new `EndianSlice`. |
117 | /// |
118 | /// ``` |
119 | /// use gimli::{EndianSlice, LittleEndian}; |
120 | /// |
121 | /// let slice = &[0x01, 0x02, 0x03, 0x04]; |
122 | /// let endian_slice = EndianSlice::new(slice, LittleEndian); |
123 | /// assert_eq!(endian_slice.range(1..3), |
124 | /// EndianSlice::new(&slice[1..3], LittleEndian)); |
125 | /// ``` |
126 | pub fn range(&self, idx: Range<usize>) -> EndianSlice<'input, Endian> { |
127 | EndianSlice { |
128 | slice: &self.slice[idx], |
129 | endian: self.endian, |
130 | } |
131 | } |
132 | |
133 | /// Take the given `start..` range of the underlying slice and return a new |
134 | /// `EndianSlice`. |
135 | /// |
136 | /// ``` |
137 | /// use gimli::{EndianSlice, LittleEndian}; |
138 | /// |
139 | /// let slice = &[0x01, 0x02, 0x03, 0x04]; |
140 | /// let endian_slice = EndianSlice::new(slice, LittleEndian); |
141 | /// assert_eq!(endian_slice.range_from(2..), |
142 | /// EndianSlice::new(&slice[2..], LittleEndian)); |
143 | /// ``` |
144 | pub fn range_from(&self, idx: RangeFrom<usize>) -> EndianSlice<'input, Endian> { |
145 | EndianSlice { |
146 | slice: &self.slice[idx], |
147 | endian: self.endian, |
148 | } |
149 | } |
150 | |
151 | /// Take the given `..end` range of the underlying slice and return a new |
152 | /// `EndianSlice`. |
153 | /// |
154 | /// ``` |
155 | /// use gimli::{EndianSlice, LittleEndian}; |
156 | /// |
157 | /// let slice = &[0x01, 0x02, 0x03, 0x04]; |
158 | /// let endian_slice = EndianSlice::new(slice, LittleEndian); |
159 | /// assert_eq!(endian_slice.range_to(..3), |
160 | /// EndianSlice::new(&slice[..3], LittleEndian)); |
161 | /// ``` |
162 | pub fn range_to(&self, idx: RangeTo<usize>) -> EndianSlice<'input, Endian> { |
163 | EndianSlice { |
164 | slice: &self.slice[idx], |
165 | endian: self.endian, |
166 | } |
167 | } |
168 | } |
169 | |
170 | impl<'input, Endian> Deref for EndianSlice<'input, Endian> |
171 | where |
172 | Endian: Endianity, |
173 | { |
174 | type Target = [u8]; |
175 | fn deref(&self) -> &Self::Target { |
176 | self.slice |
177 | } |
178 | } |
179 | |
180 | impl<'input, Endian> Reader for EndianSlice<'input, Endian> |
181 | where |
182 | Endian: Endianity, |
183 | { |
184 | type Endian = Endian; |
185 | type Offset = usize; |
186 | |
187 | #[inline ] |
188 | fn endian(&self) -> Endian { |
189 | self.endian |
190 | } |
191 | |
192 | #[inline ] |
193 | fn len(&self) -> usize { |
194 | self.slice.len() |
195 | } |
196 | |
197 | #[inline ] |
198 | fn is_empty(&self) -> bool { |
199 | self.slice.is_empty() |
200 | } |
201 | |
202 | #[inline ] |
203 | fn empty(&mut self) { |
204 | self.slice = &[]; |
205 | } |
206 | |
207 | #[inline ] |
208 | fn truncate(&mut self, len: usize) -> Result<()> { |
209 | if self.slice.len() < len { |
210 | Err(Error::UnexpectedEof(self.offset_id())) |
211 | } else { |
212 | self.slice = &self.slice[..len]; |
213 | Ok(()) |
214 | } |
215 | } |
216 | |
217 | #[inline ] |
218 | fn offset_from(&self, base: &Self) -> usize { |
219 | self.offset_from(*base) |
220 | } |
221 | |
222 | #[inline ] |
223 | fn offset_id(&self) -> ReaderOffsetId { |
224 | ReaderOffsetId(self.slice.as_ptr() as u64) |
225 | } |
226 | |
227 | #[inline ] |
228 | fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<Self::Offset> { |
229 | let id = id.0; |
230 | let self_id = self.slice.as_ptr() as u64; |
231 | let self_len = self.slice.len() as u64; |
232 | if id >= self_id && id <= self_id + self_len { |
233 | Some((id - self_id) as usize) |
234 | } else { |
235 | None |
236 | } |
237 | } |
238 | |
239 | #[inline ] |
240 | fn find(&self, byte: u8) -> Result<usize> { |
241 | self.find(byte) |
242 | .ok_or_else(|| Error::UnexpectedEof(self.offset_id())) |
243 | } |
244 | |
245 | #[inline ] |
246 | fn skip(&mut self, len: usize) -> Result<()> { |
247 | if self.slice.len() < len { |
248 | Err(Error::UnexpectedEof(self.offset_id())) |
249 | } else { |
250 | self.slice = &self.slice[len..]; |
251 | Ok(()) |
252 | } |
253 | } |
254 | |
255 | #[inline ] |
256 | fn split(&mut self, len: usize) -> Result<Self> { |
257 | let slice = self.read_slice(len)?; |
258 | Ok(EndianSlice::new(slice, self.endian)) |
259 | } |
260 | |
261 | #[cfg (not(feature = "read" ))] |
262 | fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { |
263 | super::reader::seal_if_no_alloc::Sealed |
264 | } |
265 | |
266 | #[cfg (feature = "read" )] |
267 | #[inline ] |
268 | fn to_slice(&self) -> Result<Cow<[u8]>> { |
269 | Ok(self.slice.into()) |
270 | } |
271 | |
272 | #[cfg (feature = "read" )] |
273 | #[inline ] |
274 | fn to_string(&self) -> Result<Cow<str>> { |
275 | match str::from_utf8(self.slice) { |
276 | Ok(s) => Ok(s.into()), |
277 | _ => Err(Error::BadUtf8), |
278 | } |
279 | } |
280 | |
281 | #[cfg (feature = "read" )] |
282 | #[inline ] |
283 | fn to_string_lossy(&self) -> Result<Cow<str>> { |
284 | Ok(String::from_utf8_lossy(self.slice)) |
285 | } |
286 | |
287 | #[inline ] |
288 | fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { |
289 | let slice = self.read_slice(buf.len())?; |
290 | buf.copy_from_slice(slice); |
291 | Ok(()) |
292 | } |
293 | } |
294 | |
295 | #[cfg (test)] |
296 | mod tests { |
297 | use super::*; |
298 | use crate::endianity::NativeEndian; |
299 | |
300 | #[test] |
301 | fn test_endian_slice_split_at() { |
302 | let endian = NativeEndian; |
303 | let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; |
304 | let eb = EndianSlice::new(slice, endian); |
305 | assert_eq!( |
306 | eb.split_at(3), |
307 | ( |
308 | EndianSlice::new(&slice[..3], endian), |
309 | EndianSlice::new(&slice[3..], endian) |
310 | ) |
311 | ); |
312 | } |
313 | |
314 | #[test] |
315 | #[should_panic ] |
316 | fn test_endian_slice_split_at_out_of_bounds() { |
317 | let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; |
318 | let eb = EndianSlice::new(slice, NativeEndian); |
319 | eb.split_at(30); |
320 | } |
321 | } |
322 | |