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