Skip to main content

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