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::ops::{Deref, Range, RangeFrom, RangeTo};
8use core::str;
9
10use crate::endianity::Endianity;
11use 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)]
17pub struct EndianSlice<'input, Endian>
18where
19 Endian: Endianity,
20{
21 slice: &'input [u8],
22 endian: Endian,
23}
24
25impl<'input, Endian> EndianSlice<'input, Endian>
26where
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.
111impl<'input, Endian> EndianSlice<'input, Endian>
112where
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
170impl<'input, Endian> Deref for EndianSlice<'input, Endian>
171where
172 Endian: Endianity,
173{
174 type Target = [u8];
175 fn deref(&self) -> &Self::Target {
176 self.slice
177 }
178}
179
180impl<'input, Endian> Reader for EndianSlice<'input, Endian>
181where
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)]
296mod 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