From 694f17754c1e6d9ab0af3987ae972cb03ae828ad Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Sun, 29 Apr 2018 09:40:42 -0700 Subject: [PATCH 1/3] Add EndianReader An easy way to define a custom `Reader` implementation with a reference to a generic buffer of bytes and an associated endianity. Fix #294 --- src/endian_reader.rs | 579 +++++++++++++++++++++++++++++++++++++++++++ src/endian_slice.rs | 60 ----- src/lib.rs | 19 +- src/reader.rs | 84 ++++++- 4 files changed, 663 insertions(+), 79 deletions(-) create mode 100644 src/endian_reader.rs diff --git a/src/endian_reader.rs b/src/endian_reader.rs new file mode 100644 index 000000000..e03825c04 --- /dev/null +++ b/src/endian_reader.rs @@ -0,0 +1,579 @@ +//! Defining custom `Reader`s quickly. + +use borrow::Cow; +use endianity::Endianity; +use parser::{Error, Result}; +use rc::Rc; +use reader::Reader; +use std::fmt::Debug; +use std::mem; +use std::ops::{Deref, Index, Range, RangeFrom, RangeTo}; +use std::str; +use string::String; +use Arc; + +/// A reference counted, non-thread-safe slice of bytes and associated +/// endianity. +/// +/// ``` +/// use std::rc::Rc; +/// +/// let buf = Rc::from(&[1, 2, 3, 4][..]); +/// let reader = gimli::EndianRcSlice::new(buf, gimli::NativeEndian); +/// # let _ = reader; +/// ``` +pub type EndianRcSlice = EndianReader>; + +/// An atomically reference counted, thread-safe slice of bytes and associated +/// endianity. +/// +/// ``` +/// use std::sync::Arc; +/// +/// let buf = Arc::from(&[1, 2, 3, 4][..]); +/// let reader = gimli::EndianArcSlice::new(buf, gimli::NativeEndian); +/// # let _ = reader; +/// ``` +pub type EndianArcSlice = EndianReader>; + +/// An easy way to define a custom `Reader` implementation with a reference to a +/// generic buffer of bytes and an associated endianity. +/// +/// Note that the whole original buffer is kept alive in memory even if there is +/// only one reader that references only a handful of bytes from that original +/// buffer. That is, `EndianReader` will not do any copying, moving, or +/// compacting in order to free up unused regions of the original buffer. If you +/// require this kind of behavior, it is up to you to implement `Reader` +/// directly by-hand. +/// +/// # Example +/// +/// Say you have an `mmap`ed file that you want to serve as a `gimli::Reader`. +/// You can wrap that `mmap`ed file up in a `MmapFile` type and use +/// `EndianReader>` or `EndianReader>` as readers as +/// long as `MmapFile` dereferences to the underlying `[u8]` data. +/// +/// ``` +/// extern crate gimli; +/// use std::io; +/// use std::ops::Deref; +/// use std::path::Path; +/// use std::slice; +/// use std::sync::Arc; +/// +/// /// A type that represents an `mmap`ed file. +/// #[derive(Debug)] +/// pub struct MmapFile { +/// ptr: *const u8, +/// len: usize, +/// } +/// +/// impl MmapFile { +/// pub fn new(path: &Path) -> io::Result { +/// // Call `mmap` and check for errors and all that... +/// # unimplemented!() +/// } +/// } +/// +/// impl Drop for MmapFile { +/// fn drop(&mut self) { +/// // Call `munmap` to clean up after ourselves... +/// # unimplemented!() +/// } +/// } +/// +/// // And `MmapFile` can deref to a slice of the `mmap`ed region of memory. +/// impl Deref for MmapFile { +/// type Target = [u8]; +/// fn deref(&self) -> &[u8] { +/// unsafe { +/// slice::from_raw_parts(self.ptr, self.len) +/// } +/// } +/// } +/// +/// /// A `gimli::Reader` that is backed by an `mmap`ed file! +/// pub type MmapFileReader = gimli::EndianReader>; +/// # fn main() {} +/// ``` +#[derive(Debug, Clone, Copy, Hash)] +pub struct EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + range: SubRange, + endian: Endian, +} + +impl PartialEq> for EndianReader +where + Endian: Endianity, + T1: Deref + Clone + Debug, + T2: Deref + Clone + Debug, +{ + fn eq(&self, rhs: &EndianReader) -> bool { + self.range.bytes() == rhs.range.bytes() + } +} + +impl Eq for EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ +} + +// This is separated out from `EndianReader` so that we can avoid running afoul +// of borrowck. We need to `read_slice(&mut self, ...) -> &[u8]` and then call +// `self.endian.read_whatever` on the result. The problem is that the returned +// slice keeps the `&mut self` borrow active, so we wouldn't be able to access +// `self.endian`. Splitting the sub-range out from the endian lets us work +// around this, making it so that only the `self.range` borrow is held active, +// not all of `self`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +struct SubRange +where + T: Deref + Clone + Debug, +{ + bytes: T, + start: usize, + end: usize, +} + +impl SubRange +where + T: Deref + Clone + Debug, +{ + #[inline] + fn bytes(&self) -> &[u8] { + &self.bytes[self.start..self.end] + } + + #[inline] + fn len(&self) -> usize { + self.end - self.start + } + + #[inline] + fn find(&self, byte: u8) -> Option { + self.bytes().iter().position(|x| *x == byte) + } + + #[inline] + fn read_slice(&mut self, len: usize) -> Result<&[u8]> { + if self.len() < len { + Err(Error::UnexpectedEof) + } else { + let start = self.start; + self.start += len; + Ok(&self.bytes[start..start + len]) + } + } +} + +impl EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + /// Construct a new `EndianReader` with the given bytes. + #[inline] + pub fn new(bytes: T, endian: Endian) -> EndianReader { + let start = 0; + let end = bytes.len(); + EndianReader { + range: SubRange { bytes, start, end }, + endian, + } + } + + /// Return a reference to the raw bytes underlying this reader. + #[inline] + pub fn bytes(&self) -> &[u8] { + self.range.bytes() + } +} + +/// # Range Methods +/// +/// Unfortunately, `std::ops::Index` *must* return a reference, so we can't +/// implement `Index>` to return a new `EndianReader` the way we +/// would like to. Instead, we abandon fancy indexing operators and have these +/// plain old methods. +impl EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + /// Take the given `start..end` range of the underlying buffer and return a + /// new `EndianReader`. + /// + /// ``` + /// use gimli::{EndianReader, LittleEndian}; + /// use std::sync::Arc; + /// + /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); + /// let reader = EndianReader::new(buf.clone(), LittleEndian); + /// assert_eq!(reader.range(1..3), + /// EndianReader::new(&buf[1..3], LittleEndian)); + /// ``` + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + pub fn range(&self, idx: Range) -> EndianReader { + let mut r = self.clone(); + r.range.end = r.range.start + idx.end; + r.range.start += idx.start; + assert!(r.range.start <= r.range.end); + assert!(r.range.end <= self.range.end); + r + } + + /// Take the given `start..` range of the underlying buffer and return a new + /// `EndianReader`. + /// + /// ``` + /// use gimli::{EndianReader, LittleEndian}; + /// use std::sync::Arc; + /// + /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); + /// let reader = EndianReader::new(buf.clone(), LittleEndian); + /// assert_eq!(reader.range_from(2..), + /// EndianReader::new(&buf[2..], LittleEndian)); + /// ``` + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + pub fn range_from(&self, idx: RangeFrom) -> EndianReader { + let mut r = self.clone(); + r.range.start += idx.start; + assert!(r.range.start <= r.range.end); + r + } + + /// Take the given `..end` range of the underlying buffer and return a new + /// `EndianReader`. + /// + /// ``` + /// use gimli::{EndianReader, LittleEndian}; + /// use std::sync::Arc; + /// + /// let buf = Arc::<[u8]>::from(&[0x01, 0x02, 0x03, 0x04][..]); + /// let reader = EndianReader::new(buf.clone(), LittleEndian); + /// assert_eq!(reader.range_to(..3), + /// EndianReader::new(&buf[..3], LittleEndian)); + /// ``` + /// + /// # Panics + /// + /// Panics if the range is out of bounds. + pub fn range_to(&self, idx: RangeTo) -> EndianReader { + let mut r = self.clone(); + r.range.end = r.range.start + idx.end; + assert!(r.range.end <= self.range.end); + r + } +} + +impl Index for EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + type Output = u8; + fn index(&self, idx: usize) -> &Self::Output { + &self.range.bytes()[idx] + } +} + +impl Index> for EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + type Output = [u8]; + fn index(&self, idx: RangeFrom) -> &Self::Output { + &self.bytes()[idx] + } +} + +impl Deref for EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + type Target = [u8]; + fn deref(&self) -> &Self::Target { + self.bytes() + } +} + +impl Reader for EndianReader +where + Endian: Endianity, + T: Deref + Clone + Debug, +{ + type Endian = Endian; + type Offset = usize; + + #[inline] + fn endian(&self) -> Endian { + self.endian + } + + #[inline] + fn len(&self) -> usize { + self.range.len() + } + + #[inline] + fn empty(&mut self) { + self.range.start = self.range.end; + } + + #[inline] + fn truncate(&mut self, len: usize) -> Result<()> { + if self.len() < len { + Err(Error::UnexpectedEof) + } else { + self.range.end = self.range.start + len; + Ok(()) + } + } + + #[inline] + fn offset_from(&self, base: &EndianReader) -> usize { + let base_ptr = base.bytes().as_ptr() as *const u8 as usize; + let ptr = self.bytes().as_ptr() as *const u8 as usize; + debug_assert!(base_ptr <= ptr); + debug_assert!(ptr + self.bytes().len() <= base_ptr + base.bytes().len()); + ptr - base_ptr + } + + #[inline] + fn find(&self, byte: u8) -> Result { + self.range.find(byte).ok_or(Error::UnexpectedEof) + } + + #[inline] + fn skip(&mut self, len: usize) -> Result<()> { + if self.len() < len { + Err(Error::UnexpectedEof) + } else { + self.range.start += len; + Ok(()) + } + } + + #[inline] + fn split(&mut self, len: usize) -> Result { + if self.len() < len { + Err(Error::UnexpectedEof) + } else { + let mut r = self.clone(); + r.range.end = r.range.start + len; + self.range.start += len; + Ok(r) + } + } + + #[inline] + fn to_slice(&self) -> Result> { + Ok(self.bytes().into()) + } + + #[inline] + fn to_string(&self) -> Result> { + match str::from_utf8(self.bytes()) { + Ok(s) => Ok(s.into()), + _ => Err(Error::BadUtf8), + } + } + + #[inline] + fn to_string_lossy(&self) -> Result> { + Ok(String::from_utf8_lossy(self.bytes())) + } + + #[inline] + fn read_u8_array(&mut self) -> Result + where + A: Sized + Default + AsMut<[u8]>, + { + let len = mem::size_of::(); + let slice = self.range.read_slice(len)?; + let mut val = Default::default(); + >::as_mut(&mut val).clone_from_slice(slice); + Ok(val) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use endianity::NativeEndian; + use reader::Reader; + + fn native_reader + Clone + Debug>( + bytes: T, + ) -> EndianReader { + EndianReader::new(bytes, NativeEndian) + } + + const BUF: &'static [u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + + #[test] + fn test_reader_split() { + let mut reader = native_reader(BUF); + let left = reader.split(3).unwrap(); + assert_eq!(left, native_reader(&BUF[..3])); + assert_eq!(reader, native_reader(&BUF[3..])); + } + + #[test] + fn test_reader_split_out_of_bounds() { + let mut reader = native_reader(BUF); + assert!(reader.split(30).is_err()); + } + + #[test] + fn bytes_and_len_and_range_and_eq() { + let reader = native_reader(BUF); + assert_eq!(reader.len(), BUF.len()); + assert_eq!(reader.bytes(), BUF); + assert_eq!(reader, native_reader(BUF)); + + let range = reader.range(2..8); + let buf_range = &BUF[2..8]; + assert_eq!(range.len(), buf_range.len()); + assert_eq!(range.bytes(), buf_range); + assert_ne!(range, native_reader(BUF)); + assert_eq!(range, native_reader(buf_range)); + + let range_from = range.range_from(1..); + let buf_range_from = &buf_range[1..]; + assert_eq!(range_from.len(), buf_range_from.len()); + assert_eq!(range_from.bytes(), buf_range_from); + assert_ne!(range_from, native_reader(BUF)); + assert_eq!(range_from, native_reader(buf_range_from)); + + let range_to = range_from.range_to(..4); + let buf_range_to = &buf_range_from[..4]; + assert_eq!(range_to.len(), buf_range_to.len()); + assert_eq!(range_to.bytes(), buf_range_to); + assert_ne!(range_to, native_reader(BUF)); + assert_eq!(range_to, native_reader(buf_range_to)); + } + + #[test] + fn find() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + assert_eq!( + reader.find(5), + Ok(BUF[2..].iter().position(|x| *x == 5).unwrap()) + ); + } + + #[test] + fn indexing() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + assert_eq!(reader[0], BUF[2]); + } + + #[test] + #[should_panic] + fn indexing_out_of_bounds() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + reader[900]; + } + + #[test] + fn endian() { + let reader = native_reader(BUF); + assert_eq!(reader.endian(), NativeEndian); + } + + #[test] + fn empty() { + let mut reader = native_reader(BUF); + assert!(!reader.is_empty()); + reader.empty(); + assert!(reader.is_empty()); + assert!(reader.bytes().is_empty()); + } + + #[test] + fn truncate() { + let reader = native_reader(BUF); + let mut reader = reader.range(2..8); + reader.truncate(2).unwrap(); + assert_eq!(reader.bytes(), &BUF[2..4]); + } + + #[test] + fn offset_from() { + let reader = native_reader(BUF); + let sub = reader.range(2..8); + assert_eq!(sub.offset_from(&reader), 2); + } + + #[test] + fn skip() { + let mut reader = native_reader(BUF); + reader.skip(2).unwrap(); + assert_eq!(reader.bytes(), &BUF[2..]); + } + + #[test] + fn to_slice() { + assert_eq!( + native_reader(BUF).range(2..5).to_slice(), + Ok(Cow::from(&BUF[2..5])) + ); + } + + #[test] + fn to_string_ok() { + let buf = b"hello, world!"; + let reader = native_reader(&buf[..]); + let reader = reader.range_from(7..); + assert_eq!( + reader.to_string(), + Ok(Cow::from("world!")) + ); + } + + // The rocket emoji (🚀 = [0xf0, 0x9f, 0x9a, 0x80]) but rotated left by one + // to make it invalid UTF-8. + const BAD_UTF8: &'static [u8] = &[0x9f, 0x9a, 0x80, 0xf0]; + + #[test] + fn to_string_err() { + let reader = native_reader(BAD_UTF8); + assert!(reader.to_string().is_err()); + } + + #[test] + fn to_string_lossy() { + let reader = native_reader(BAD_UTF8); + assert_eq!( + reader.to_string_lossy(), + Ok(Cow::from("����")) + ); + } + + #[test] + fn read_u8_array() { + let mut reader = native_reader(BAD_UTF8); + reader.skip(1).unwrap(); + let arr: [u8; 2] = reader.read_u8_array().unwrap(); + assert_eq!(arr, &BAD_UTF8[1..3]); + assert_eq!(reader.bytes(), &BAD_UTF8[3..]); + } +} diff --git a/src/endian_slice.rs b/src/endian_slice.rs index db833e2e8..686fc5888 100644 --- a/src/endian_slice.rs +++ b/src/endian_slice.rs @@ -294,66 +294,6 @@ where >::as_mut(&mut val).clone_from_slice(slice); Ok(val) } - - #[inline] - fn read_u8(&mut self) -> Result { - let slice = self.read_slice(1)?; - Ok(slice[0]) - } - - #[inline] - fn read_i8(&mut self) -> Result { - let slice = self.read_slice(1)?; - Ok(slice[0] as i8) - } - - #[inline] - fn read_u16(&mut self) -> Result { - let slice = self.read_slice(2)?; - Ok(self.endian.read_u16(slice)) - } - - #[inline] - fn read_i16(&mut self) -> Result { - let slice = self.read_slice(2)?; - Ok(self.endian.read_i16(slice)) - } - - #[inline] - fn read_u32(&mut self) -> Result { - let slice = self.read_slice(4)?; - Ok(self.endian.read_u32(slice)) - } - - #[inline] - fn read_i32(&mut self) -> Result { - let slice = self.read_slice(4)?; - Ok(self.endian.read_i32(slice)) - } - - #[inline] - fn read_u64(&mut self) -> Result { - let slice = self.read_slice(8)?; - Ok(self.endian.read_u64(slice)) - } - - #[inline] - fn read_i64(&mut self) -> Result { - let slice = self.read_slice(8)?; - Ok(self.endian.read_i64(slice)) - } - - #[inline] - fn read_f32(&mut self) -> Result { - let slice = self.read_slice(4)?; - Ok(self.endian.read_f32(slice)) - } - - #[inline] - fn read_f64(&mut self) -> Result { - let slice = self.read_slice(8)?; - Ok(self.endian.read_f64(slice)) - } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index bf62aa271..bea1646a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,20 +192,24 @@ extern crate fallible_iterator; #[cfg(feature = "std")] mod imports { - pub use std::boxed; - pub use std::vec; - pub use std::string; pub use std::borrow; + pub use std::boxed; pub use std::collections::btree_map; + pub use std::rc; + pub use std::string; + pub use std::sync::Arc; + pub use std::vec; } #[cfg(not(feature = "std"))] mod imports { - pub use alloc::boxed; - pub use alloc::vec; - pub use alloc::string; + pub use alloc::arc::Arc; pub use alloc::borrow; + pub use alloc::boxed; pub use alloc::btree_map; + pub use alloc::rc; + pub use alloc::string; + pub use alloc::vec; } use imports::*; @@ -222,6 +226,9 @@ pub use endianity::{BigEndian, Endianity, LittleEndian, NativeEndian, RunTimeEnd mod endian_slice; pub use endian_slice::EndianSlice; +mod endian_reader; +pub use endian_reader::{EndianArcSlice, EndianRcSlice, EndianReader}; + /// `EndianBuf` has been renamed to `EndianSlice`. For ease of upgrading across /// `gimli` versions, we export this type alias. #[deprecated(note = "EndianBuf has been renamed to EndianSlice, use that instead.")] diff --git a/src/reader.rs b/src/reader.rs index 9e430f9bd..492703234 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -180,6 +180,21 @@ impl ReaderOffset for usize { /// /// All read operations advance the section offset of the reader /// unless specified otherwise. +/// +/// ## Choosing a `Reader` Implementation +/// +/// `gimli` comes with a few different `Reader` implementations and lets you +/// choose the one that is right for your use case. A `Reader` is essentially a +/// view into the raw bytes that make up some DWARF, but this view might borrow +/// the underlying data or use reference counting ownership, and it might be +/// thread safe or not. +/// +/// | Implementation | Ownership | Thread Safe | Notes | +/// |:------------------|:------------------|:------------|:------| +/// | [`EndianSlice`](./struct.EndianSlice.html) | Borrowed | Yes | Fastest, but requires that all of your code work with borrows. | +/// | [`EndianRcSlice`](./struct.EndianRcSlice.html) | Reference counted | No | Shared ownership via reference counting, which alleviates the borrow restrictions of `EndianSlice` but imposes reference counting increments and decrements. Cannot be sent across threads, because the reference count is not atomic. | +/// | [`EndianArcSlice`](./struct.EndianArcSlice.html) | Reference counted | Yes | The same as `EndianRcSlice`, but uses atomic reference counting, and therefore reference counting operations are slower but `EndianArcSlice`s may be sent across threads. | +/// | [`EndianReader`](./struct.EndianReader.html) | Same as `T` | Same as `T` | Escape hatch for easily defining your own type of `Reader`. | pub trait Reader: Debug + Clone { /// The endianity of bytes that are read. type Endian: Endianity; @@ -193,9 +208,6 @@ pub trait Reader: Debug + Clone { /// Return the number of bytes remaining. fn len(&self) -> Self::Offset; - /// Return true if the number of bytes remaining is zero. - fn is_empty(&self) -> bool; - /// Set the number of bytes remaining to zero. fn empty(&mut self); @@ -253,35 +265,81 @@ pub trait Reader: Debug + Clone { where A: Sized + Default + AsMut<[u8]>; + /// Return true if the number of bytes remaining is zero. + #[inline] + fn is_empty(&self) -> bool { + self.len() == Self::Offset::from_u8(0) + } + /// Read a u8. - fn read_u8(&mut self) -> Result; + #[inline] + fn read_u8(&mut self) -> Result { + let a: [u8; 1] = self.read_u8_array()?; + Ok(a[0]) + } /// Read an i8. - fn read_i8(&mut self) -> Result; + #[inline] + fn read_i8(&mut self) -> Result { + let a: [u8; 1] = self.read_u8_array()?; + Ok(a[0] as i8) + } /// Read a u16. - fn read_u16(&mut self) -> Result; + #[inline] + fn read_u16(&mut self) -> Result { + let a: [u8; 2] = self.read_u8_array()?; + Ok(self.endian().read_u16(&a)) + } /// Read an i16. - fn read_i16(&mut self) -> Result; + #[inline] + fn read_i16(&mut self) -> Result { + let a: [u8; 2] = self.read_u8_array()?; + Ok(self.endian().read_i16(&a)) + } /// Read a u32. - fn read_u32(&mut self) -> Result; + #[inline] + fn read_u32(&mut self) -> Result { + let a: [u8; 4] = self.read_u8_array()?; + Ok(self.endian().read_u32(&a)) + } /// Read an i32. - fn read_i32(&mut self) -> Result; + #[inline] + fn read_i32(&mut self) -> Result { + let a: [u8; 4] = self.read_u8_array()?; + Ok(self.endian().read_i32(&a)) + } /// Read a u64. - fn read_u64(&mut self) -> Result; + #[inline] + fn read_u64(&mut self) -> Result { + let a: [u8; 8] = self.read_u8_array()?; + Ok(self.endian().read_u64(&a)) + } /// Read an i64. - fn read_i64(&mut self) -> Result; + #[inline] + fn read_i64(&mut self) -> Result { + let a: [u8; 8] = self.read_u8_array()?; + Ok(self.endian().read_i64(&a)) + } /// Read a f32. - fn read_f32(&mut self) -> Result; + #[inline] + fn read_f32(&mut self) -> Result { + let a: [u8; 4] = self.read_u8_array()?; + Ok(self.endian().read_f32(&a)) + } /// Read a f64. - fn read_f64(&mut self) -> Result; + #[inline] + fn read_f64(&mut self) -> Result { + let a: [u8; 8] = self.read_u8_array()?; + Ok(self.endian().read_f64(&a)) + } /// Read a null-terminated slice, and return it (excluding the null). fn read_null_terminated_slice(&mut self) -> Result { From b850eb87ffe60429a32ec23bad7ffdd2d610f827 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 15 May 2018 11:11:55 -0700 Subject: [PATCH 2/3] Add a self-parse of `.debug_info` test using `EndianRcSlice` --- tests/parse_self.rs | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/tests/parse_self.rs b/tests/parse_self.rs index 7a9011b95..aad9d6181 100644 --- a/tests/parse_self.rs +++ b/tests/parse_self.rs @@ -8,6 +8,7 @@ use std::collections::hash_map::HashMap; use std::fs::File; use std::io::Read; use std::path::PathBuf; +use std::rc::Rc; fn read_section(section: &str) -> Vec { let mut path = PathBuf::new(); @@ -38,14 +39,7 @@ fn parse_expression(expr: Expression, address_size: u8, format: Fo eval.evaluate().expect("Should evaluate expression"); } -#[test] -fn test_parse_self_debug_info() { - let debug_info = read_section("debug_info"); - let debug_info = DebugInfo::new(&debug_info, LittleEndian); - - let debug_abbrev = read_section("debug_abbrev"); - let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian); - +fn impl_parse_self_debug_info(debug_info: DebugInfo, debug_abbrev: DebugAbbrev) { let mut iter = debug_info.units(); while let Some(unit) = iter.next().expect("Should parse compilation unit") { let abbrevs = unit.abbreviations(&debug_abbrev) @@ -66,6 +60,32 @@ fn test_parse_self_debug_info() { } } +#[test] +fn test_parse_self_debug_info() { + let debug_info = read_section("debug_info"); + let debug_info = DebugInfo::new(&debug_info, LittleEndian); + + let debug_abbrev = read_section("debug_abbrev"); + let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian); + + impl_parse_self_debug_info(debug_info, debug_abbrev); +} + +#[test] +fn test_parse_self_debug_info_with_endian_rc_slice() { + let debug_info = read_section("debug_info"); + let debug_info = Rc::from(&debug_info[..]); + let debug_info = gimli::EndianRcSlice::new(debug_info, LittleEndian); + let debug_info = DebugInfo::from(debug_info); + + let debug_abbrev = read_section("debug_abbrev"); + let debug_abbrev = Rc::from(&debug_abbrev[..]); + let debug_abbrev = gimli::EndianRcSlice::new(debug_abbrev, LittleEndian); + let debug_abbrev = DebugAbbrev::from(debug_abbrev); + + impl_parse_self_debug_info(debug_info, debug_abbrev); +} + #[test] fn test_parse_self_debug_line() { let debug_info = read_section("debug_info"); From 6f5e0494d062f8723108d20dcffc8930993f1440 Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 15 May 2018 11:19:13 -0700 Subject: [PATCH 3/3] Add a parse-self benchmark for `.debug_info` and `EndianRcSlice Looks like it takes about 1.58x the time that using `EndianSlice` does: ``` test bench_parsing_debug_info ... bench: 2,261,953 ns/iter (+/- 142,827) test bench_parsing_debug_info_with_endian_rc_slice ... bench: 3,564,292 ns/iter (+/- 208,038) ``` --- benches/bench.rs | 50 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) mode change 100644 => 100755 benches/bench.rs diff --git a/benches/bench.rs b/benches/bench.rs old mode 100644 new mode 100755 index 5d2ff69fd..9bf3f9eb8 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -11,6 +11,7 @@ use std::env; use std::fs::File; use std::io::Read; use std::path::PathBuf; +use std::rc::Rc; pub fn read_section(section: &str) -> Vec { let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into())); @@ -46,30 +47,47 @@ fn bench_parsing_debug_abbrev(b: &mut test::Bencher) { }); } +#[inline] +fn impl_bench_parsing_debug_info(debug_info: DebugInfo, debug_abbrev: DebugAbbrev) { + let mut iter = debug_info.units(); + while let Some(unit) = iter.next().expect("Should parse compilation unit") { + let abbrevs = unit.abbreviations(&debug_abbrev) + .expect("Should parse abbreviations"); + + let mut cursor = unit.entries(&abbrevs); + while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") { + let mut attrs = entry.attrs(); + while let Some(attr) = attrs.next().expect("Should parse entry's attribute") { + test::black_box(&attr); + } + } + } +} + #[bench] fn bench_parsing_debug_info(b: &mut test::Bencher) { + let debug_info = read_section("debug_info"); + let debug_info = DebugInfo::new(&debug_info, LittleEndian); + let debug_abbrev = read_section("debug_abbrev"); let debug_abbrev = DebugAbbrev::new(&debug_abbrev, LittleEndian); - let debug_info = read_section("debug_info"); + b.iter(|| impl_bench_parsing_debug_info(debug_info, debug_abbrev)); +} - b.iter(|| { - let debug_info = DebugInfo::new(&debug_info, LittleEndian); +#[bench] +fn bench_parsing_debug_info_with_endian_rc_slice(b: &mut test::Bencher) { + let debug_info = read_section("debug_info"); + let debug_info = Rc::from(&debug_info[..]); + let debug_info = gimli::EndianRcSlice::new(debug_info, LittleEndian); + let debug_info = DebugInfo::from(debug_info); - let mut iter = debug_info.units(); - while let Some(unit) = iter.next().expect("Should parse compilation unit") { - let abbrevs = unit.abbreviations(&debug_abbrev) - .expect("Should parse abbreviations"); + let debug_abbrev = read_section("debug_abbrev"); + let debug_abbrev = Rc::from(&debug_abbrev[..]); + let debug_abbrev = gimli::EndianRcSlice::new(debug_abbrev, LittleEndian); + let debug_abbrev = DebugAbbrev::from(debug_abbrev); - let mut cursor = unit.entries(&abbrevs); - while let Some((_, entry)) = cursor.next_dfs().expect("Should parse next dfs") { - let mut attrs = entry.attrs(); - while let Some(attr) = attrs.next().expect("Should parse entry's attribute") { - test::black_box(&attr); - } - } - } - }); + b.iter(|| impl_bench_parsing_debug_info(debug_info.clone(), debug_abbrev.clone())); } #[bench]