//! Working with byte slices that have an associated endianity. #[cfg(feature = "read")] use alloc::borrow::Cow; #[cfg(feature = "read")] use alloc::string::String; use core::fmt; use core::ops::{Deref, Range, RangeFrom, RangeTo}; use core::str; use crate::endianity::Endianity; use crate::read::{Error, Reader, ReaderOffsetId, Result}; /// A `&[u8]` slice with endianity metadata. /// /// This implements the `Reader` trait, which is used for all reading of DWARF sections. #[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] pub struct EndianSlice<'input, Endian> where Endian: Endianity, { slice: &'input [u8], endian: Endian, } impl<'input, Endian> EndianSlice<'input, Endian> where Endian: Endianity, { /// Construct a new `EndianSlice` with the given slice and endianity. #[inline] pub fn new(slice: &'input [u8], endian: Endian) -> EndianSlice<'input, Endian> { EndianSlice { slice, endian } } /// Return a reference to the raw slice. #[inline] #[doc(hidden)] #[deprecated(note = "Method renamed to EndianSlice::slice; use that instead.")] pub fn buf(&self) -> &'input [u8] { self.slice } /// Return a reference to the raw slice. #[inline] pub fn slice(&self) -> &'input [u8] { self.slice } /// Split the slice in two at the given index, resulting in the tuple where /// the first item has range [0, idx), and the second has range [idx, /// len). Panics if the index is out of bounds. #[inline] pub fn split_at( &self, idx: usize, ) -> (EndianSlice<'input, Endian>, EndianSlice<'input, Endian>) { (self.range_to(..idx), self.range_from(idx..)) } /// Find the first occurrence of a byte in the slice, and return its index. #[inline] pub fn find(&self, byte: u8) -> Option { self.slice.iter().position(|ch| *ch == byte) } /// Return the offset of the start of the slice relative to the start /// of the given slice. #[inline] pub fn offset_from(&self, base: EndianSlice<'input, Endian>) -> usize { let base_ptr = base.slice.as_ptr() as *const u8 as usize; let ptr = self.slice.as_ptr() as *const u8 as usize; debug_assert!(base_ptr <= ptr); debug_assert!(ptr + self.slice.len() <= base_ptr + base.slice.len()); ptr - base_ptr } /// Converts the slice to a string using `str::from_utf8`. /// /// Returns an error if the slice contains invalid characters. #[inline] pub fn to_string(&self) -> Result<&'input str> { str::from_utf8(self.slice).map_err(|_| Error::BadUtf8) } /// Converts the slice to a string, including invalid characters, /// using `String::from_utf8_lossy`. #[cfg(feature = "read")] #[inline] pub fn to_string_lossy(&self) -> Cow<'input, str> { String::from_utf8_lossy(self.slice) } #[inline] fn read_slice(&mut self, len: usize) -> Result<&'input [u8]> { if self.slice.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { let val = &self.slice[..len]; self.slice = &self.slice[len..]; Ok(val) } } } /// # Range Methods /// /// Unfortunately, `std::ops::Index` *must* return a reference, so we can't /// implement `Index>` to return a new `EndianSlice` the way we would /// like to. Instead, we abandon fancy indexing operators and have these plain /// old methods. impl<'input, Endian> EndianSlice<'input, Endian> where Endian: Endianity, { /// Take the given `start..end` range of the underlying slice and return a /// new `EndianSlice`. /// /// ``` /// use gimli::{EndianSlice, LittleEndian}; /// /// let slice = &[0x01, 0x02, 0x03, 0x04]; /// let endian_slice = EndianSlice::new(slice, LittleEndian); /// assert_eq!(endian_slice.range(1..3), /// EndianSlice::new(&slice[1..3], LittleEndian)); /// ``` pub fn range(&self, idx: Range) -> EndianSlice<'input, Endian> { EndianSlice { slice: &self.slice[idx], endian: self.endian, } } /// Take the given `start..` range of the underlying slice and return a new /// `EndianSlice`. /// /// ``` /// use gimli::{EndianSlice, LittleEndian}; /// /// let slice = &[0x01, 0x02, 0x03, 0x04]; /// let endian_slice = EndianSlice::new(slice, LittleEndian); /// assert_eq!(endian_slice.range_from(2..), /// EndianSlice::new(&slice[2..], LittleEndian)); /// ``` pub fn range_from(&self, idx: RangeFrom) -> EndianSlice<'input, Endian> { EndianSlice { slice: &self.slice[idx], endian: self.endian, } } /// Take the given `..end` range of the underlying slice and return a new /// `EndianSlice`. /// /// ``` /// use gimli::{EndianSlice, LittleEndian}; /// /// let slice = &[0x01, 0x02, 0x03, 0x04]; /// let endian_slice = EndianSlice::new(slice, LittleEndian); /// assert_eq!(endian_slice.range_to(..3), /// EndianSlice::new(&slice[..3], LittleEndian)); /// ``` pub fn range_to(&self, idx: RangeTo) -> EndianSlice<'input, Endian> { EndianSlice { slice: &self.slice[idx], endian: self.endian, } } } impl<'input, Endian> Deref for EndianSlice<'input, Endian> where Endian: Endianity, { type Target = [u8]; fn deref(&self) -> &Self::Target { self.slice } } impl<'input, Endian: Endianity> fmt::Debug for EndianSlice<'input, Endian> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { fmt.debug_tuple("EndianSlice") .field(&self.endian) .field(&DebugBytes(self.slice)) .finish() } } struct DebugBytes<'input>(&'input [u8]); impl<'input> core::fmt::Debug for DebugBytes<'input> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> { let mut list = fmt.debug_list(); list.entries(self.0.iter().take(8).copied().map(DebugByte)); if self.0.len() > 8 { list.entry(&DebugLen(self.0.len())); } list.finish() } } struct DebugByte(u8); impl fmt::Debug for DebugByte { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "0x{:02x}", self.0) } } struct DebugLen(usize); impl fmt::Debug for DebugLen { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { write!(fmt, "...; {}", self.0) } } impl<'input, Endian> Reader for EndianSlice<'input, Endian> where Endian: Endianity, { type Endian = Endian; type Offset = usize; #[inline] fn endian(&self) -> Endian { self.endian } #[inline] fn len(&self) -> usize { self.slice.len() } #[inline] fn is_empty(&self) -> bool { self.slice.is_empty() } #[inline] fn empty(&mut self) { self.slice = &[]; } #[inline] fn truncate(&mut self, len: usize) -> Result<()> { if self.slice.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { self.slice = &self.slice[..len]; Ok(()) } } #[inline] fn offset_from(&self, base: &Self) -> usize { self.offset_from(*base) } #[inline] fn offset_id(&self) -> ReaderOffsetId { ReaderOffsetId(self.slice.as_ptr() as u64) } #[inline] fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option { let id = id.0; let self_id = self.slice.as_ptr() as u64; let self_len = self.slice.len() as u64; if id >= self_id && id <= self_id + self_len { Some((id - self_id) as usize) } else { None } } #[inline] fn find(&self, byte: u8) -> Result { self.find(byte) .ok_or_else(|| Error::UnexpectedEof(self.offset_id())) } #[inline] fn skip(&mut self, len: usize) -> Result<()> { if self.slice.len() < len { Err(Error::UnexpectedEof(self.offset_id())) } else { self.slice = &self.slice[len..]; Ok(()) } } #[inline] fn split(&mut self, len: usize) -> Result { let slice = self.read_slice(len)?; Ok(EndianSlice::new(slice, self.endian)) } #[cfg(not(feature = "read"))] fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { super::reader::seal_if_no_alloc::Sealed } #[cfg(feature = "read")] #[inline] fn to_slice(&self) -> Result> { Ok(self.slice.into()) } #[cfg(feature = "read")] #[inline] fn to_string(&self) -> Result> { match str::from_utf8(self.slice) { Ok(s) => Ok(s.into()), _ => Err(Error::BadUtf8), } } #[cfg(feature = "read")] #[inline] fn to_string_lossy(&self) -> Result> { Ok(String::from_utf8_lossy(self.slice)) } #[inline] fn read_slice(&mut self, buf: &mut [u8]) -> Result<()> { let slice = self.read_slice(buf.len())?; buf.copy_from_slice(slice); Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::endianity::NativeEndian; #[test] fn test_endian_slice_split_at() { let endian = NativeEndian; let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; let eb = EndianSlice::new(slice, endian); assert_eq!( eb.split_at(3), ( EndianSlice::new(&slice[..3], endian), EndianSlice::new(&slice[3..], endian) ) ); } #[test] #[should_panic] fn test_endian_slice_split_at_out_of_bounds() { let slice = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; let eb = EndianSlice::new(slice, NativeEndian); eb.split_at(30); } }