gimli/read/
endian_slice.rs

1//! Working with byte slices that have an associated endianity.
2
3#[cfg(feature = "read")]
4use alloc::borrow::Cow;
5#[cfg(feature = "read")]
6use alloc::string::String;
7use core::fmt;
8use core::ops::{Deref, Range, RangeFrom, RangeTo};
9use core::str;
10
11use crate::endianity::Endianity;
12use 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)]
18pub struct EndianSlice<'input, Endian>
19where
20    Endian: Endianity,
21{
22    slice: &'input [u8],
23    endian: Endian,
24}
25
26impl<'input, Endian> EndianSlice<'input, Endian>
27where
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 usize;
72        let ptr = self.slice.as_ptr() 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.
112impl<'input, Endian> EndianSlice<'input, Endian>
113where
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
171impl<'input, Endian> Deref for EndianSlice<'input, Endian>
172where
173    Endian: Endianity,
174{
175    type Target = [u8];
176    fn deref(&self) -> &Self::Target {
177        self.slice
178    }
179}
180
181impl<'input, Endian: Endianity> fmt::Debug for EndianSlice<'input, Endian> {
182    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
183        fmt.debug_tuple("EndianSlice")
184            .field(&self.endian)
185            .field(&DebugBytes(self.slice))
186            .finish()
187    }
188}
189
190struct DebugBytes<'input>(&'input [u8]);
191
192impl<'input> core::fmt::Debug for DebugBytes<'input> {
193    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
194        let mut list = 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
203struct DebugByte(u8);
204
205impl fmt::Debug for DebugByte {
206    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
207        write!(fmt, "0x{:02x}", self.0)
208    }
209}
210
211struct DebugLen(usize);
212
213impl fmt::Debug for DebugLen {
214    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
215        write!(fmt, "...; {}", self.0)
216    }
217}
218
219impl<'input, Endian> Reader for EndianSlice<'input, Endian>
220where
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)]
335mod 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}