diff --git a/benches/bench.rs b/benches/bench.rs index fb29df77..18b83413 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -3,10 +3,10 @@ extern crate test; use gimli::{ - AttributeValue, DebugAbbrev, DebugAddr, DebugAddrBase, DebugAranges, DebugInfo, DebugLine, - DebugLineOffset, DebugLoc, DebugLocLists, DebugPubNames, DebugPubTypes, DebugRanges, + leb128, AttributeValue, DebugAbbrev, DebugAddr, DebugAddrBase, DebugAranges, DebugInfo, + DebugLine, DebugLineOffset, DebugLoc, DebugLocLists, DebugPubNames, DebugPubTypes, DebugRanges, DebugRngLists, Encoding, EndianSlice, EntriesTreeNode, Expression, LittleEndian, LocationLists, - Operation, RangeLists, RangeListsOffset, Reader, ReaderOffset, + NativeEndian, Operation, RangeLists, RangeListsOffset, Reader, ReaderOffset, }; use std::env; use std::fs::File; @@ -14,6 +14,90 @@ use std::io::Read; use std::path::PathBuf; use std::rc::Rc; +/// Benchmark reading of small (one or two byte in encoded form) +/// unsigned LEB128 values. +#[bench] +fn bench_reading_leb128_unsigned_small(b: &mut test::Bencher) { + let data = (0..255) + .map(|n| { + let mut buf = Vec::new(); + leb128::write::unsigned(&mut buf, n).unwrap(); + + let mut slice = EndianSlice::new(buf.as_slice(), NativeEndian); + assert_eq!(leb128::read::unsigned(&mut slice).unwrap(), n); + + (buf.into_boxed_slice(), n) + }) + .collect::>(); + + let () = b.iter(|| { + for (data, _) in &data { + let mut slice = test::black_box(EndianSlice::new(data, NativeEndian)); + let v = leb128::read::unsigned(&mut slice).unwrap(); + test::black_box(v); + } + }); +} + +/// Benchmark reading of large unsigned LEB128 values. +#[bench] +fn bench_reading_leb128_unsigned_large(b: &mut test::Bencher) { + #[rustfmt::skip] + let data = [ + (&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01][..], u64::MAX), + (&[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00][..], u64::MAX / 2), + (&[0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0x55, 0x00][..], u64::MAX / 3), + (&[0xb3, 0xe6, 0xcc, 0x99, 0xb3, 0xe6, 0xcc, 0x99, 0x33, 0x00][..], u64::MAX / 5), + (&[0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0x2a, 0x00][..], u64::MAX / 6), + (&[0x92, 0xc9, 0xa4, 0x92, 0xc9, 0xa4, 0x92, 0xc9, 0x24, 0x00][..], u64::MAX / 7), + (&[0xf1, 0xb8, 0x9c, 0x8e, 0xc7, 0xe3, 0xf1, 0xb8, 0x1c, 0x00][..], u64::MAX / 9), + (&[0x99, 0xb3, 0xe6, 0xcc, 0x99, 0xb3, 0xe6, 0xcc, 0x19, 0x00][..], u64::MAX / 10), + (&[0xd1, 0x8b, 0xdd, 0xe8, 0xc5, 0xae, 0xf4, 0xa2, 0x17, 0x00][..], u64::MAX / 11), + (&[0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0x15, 0x00][..], u64::MAX / 12), + (&[0xb1, 0xa7, 0xec, 0x89, 0xbb, 0xe2, 0xce, 0xd8, 0x13, 0x00][..], u64::MAX / 13), + (&[0xc9, 0xa4, 0x92, 0xc9, 0xa4, 0x92, 0xc9, 0xa4, 0x12, 0x00][..], u64::MAX / 14), + (&[0x91, 0xa2, 0xc4, 0x88, 0x91, 0xa2, 0xc4, 0x88, 0x11, 0x00][..], u64::MAX / 15), + ]; + + for (data, expected) in data { + let mut slice = test::black_box(EndianSlice::new(data, NativeEndian)); + let v = leb128::read::unsigned(&mut slice).unwrap(); + assert_eq!(v, expected); + } + + let () = b.iter(|| { + for (data, _) in data { + let mut slice = test::black_box(EndianSlice::new(data, NativeEndian)); + let v = leb128::read::unsigned(&mut slice).unwrap(); + test::black_box(v); + } + }); +} + +/// Benchmark reading of small u16 LEB128 values. +#[bench] +fn bench_reading_leb128_u16_small(b: &mut test::Bencher) { + let data = (0u16..255) + .map(|n| { + let mut buf = Vec::new(); + leb128::write::unsigned(&mut buf, u64::from(n)).unwrap(); + + let mut slice = EndianSlice::new(buf.as_slice(), NativeEndian); + assert_eq!(leb128::read::u16(&mut slice).unwrap(), n); + + (buf.into_boxed_slice(), n) + }) + .collect::>(); + + let () = b.iter(|| { + for (data, _) in &data { + let mut slice = test::black_box(EndianSlice::new(data, NativeEndian)); + let v = leb128::read::unsigned(&mut slice).unwrap(); + test::black_box(v); + } + }); +} + pub fn read_section(section: &str) -> Vec { let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into())); path.push("./fixtures/self/"); diff --git a/src/leb128.rs b/src/leb128.rs index c0ee303a..25b4d7cd 100644 --- a/src/leb128.rs +++ b/src/leb128.rs @@ -80,8 +80,14 @@ pub mod read { /// Read an unsigned LEB128 number from the given `Reader` and /// return it or an error if reading failed. pub fn unsigned(r: &mut R) -> Result { - let mut result = 0; - let mut shift = 0; + // The first iteration of this loop is unpeeled for better code + // gen for the statistically likely case of single byte values. + let byte = r.read_u8()?; + if byte & CONTINUATION_BIT == 0 { + return Ok(u64::from(byte)); + } + let mut result = u64::from(low_bits_of_byte(byte)); + let mut shift = 7; loop { let byte = r.read_u8()?; @@ -104,11 +110,11 @@ pub mod read { /// return it or an error if reading failed. pub fn u16(r: &mut R) -> Result { let byte = r.read_u8()?; - let mut result = u16::from(low_bits_of_byte(byte)); if byte & CONTINUATION_BIT == 0 { - return Ok(result); + return Ok(u16::from(byte)); } + let mut result = u16::from(low_bits_of_byte(byte)); let byte = r.read_u8()?; result |= u16::from(low_bits_of_byte(byte)) << 7; if byte & CONTINUATION_BIT == 0 { @@ -334,6 +340,14 @@ mod tests { ); } + #[test] + fn test_read_unsigned_invalid() { + let buf = &[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 2][..]; + let mut readable = EndianSlice::new(buf, NativeEndian); + let err = read::unsigned(&mut readable).unwrap_err(); + assert!(matches!(err, Error::BadUnsignedLeb128), "{}", err); + } + // Examples from the DWARF 4 standard, section 7.6, figure 23. #[test] fn test_read_signed() {