Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 87 additions & 3 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,101 @@
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;
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::<Vec<_>>();

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::<Vec<_>>();

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<u8> {
let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into()));
path.push("./fixtures/self/");
Expand Down
22 changes: 18 additions & 4 deletions src/leb128.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: Reader>(r: &mut R) -> Result<u64> {
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()?;
Expand All @@ -104,11 +110,11 @@ pub mod read {
/// return it or an error if reading failed.
pub fn u16<R: Reader>(r: &mut R) -> Result<u16> {
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 {
Expand Down Expand Up @@ -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() {
Expand Down