use byteorder::{ByteOrder, LittleEndian};
#[inline(always)]
pub fn read_u64_le(data: &[u8]) -> u64 {
LittleEndian::read_u64(data)
}
#[inline(always)]
pub fn read_u64_le_at(data: &[u8], offset: usize) -> u64 {
LittleEndian::read_u64(&data[offset..])
}
#[inline(always)]
pub fn read_u32_le(data: &[u8]) -> u32 {
LittleEndian::read_u32(data)
}
#[inline(always)]
pub fn read_u32_le_at(data: &[u8], offset: usize) -> u32 {
LittleEndian::read_u32(&data[offset..])
}
#[inline(always)]
pub fn read_u16_le(data: &[u8]) -> u16 {
LittleEndian::read_u16(data)
}
#[inline(always)]
pub fn read_u16_le_at(data: &[u8], offset: usize) -> u16 {
LittleEndian::read_u16(&data[offset..])
}
#[inline(always)]
pub fn memchr_null(data: &[u8]) -> usize {
memchr::memchr(0, data).unwrap_or(data.len())
}
#[inline(always)]
pub fn memchr_find(needle: u8, haystack: &[u8]) -> Option<usize> {
memchr::memchr(needle, haystack)
}
pub const MIN_VALID_POINTER: u64 = 0x100000000;
pub const ADDR_MASK_48BIT: u64 = 0x0000_FFFF_FFFF_FFFF;
#[inline]
pub fn scan_pointers_in_range<'a>(
data: &'a [u8],
stride: usize,
min_addr: u64,
max_addr: u64,
) -> impl Iterator<Item = (usize, u64)> + 'a {
(0..data.len().saturating_sub(7))
.step_by(stride)
.filter_map(move |offset| {
let raw = read_u64_le(&data[offset..]);
if raw == 0 {
return None;
}
let addr = raw & ADDR_MASK_48BIT;
if addr >= min_addr && addr < max_addr {
Some((offset, raw))
} else {
None
}
})
}
#[inline]
pub fn batch_transform_u64<F>(data: &mut [u8], mut transform: F) -> usize
where
F: FnMut(usize, u64) -> Option<u64>,
{
let mut modified = 0;
for offset in (0..data.len().saturating_sub(7)).step_by(8) {
let value = read_u64_le(&data[offset..]);
if let Some(new_value) = transform(offset, value) {
if new_value != value {
LittleEndian::write_u64(&mut data[offset..], new_value);
modified += 1;
}
}
}
modified
}
#[inline(always)]
pub fn read_uleb128_fast(data: &[u8]) -> Option<(u64, usize)> {
if data.is_empty() {
return None;
}
let b0 = data[0];
if b0 < 0x80 {
return Some((b0 as u64, 1));
}
if data.len() < 2 {
return None;
}
let b1 = data[1];
if b1 < 0x80 {
let value = ((b0 & 0x7F) as u64) | ((b1 as u64) << 7);
return Some((value, 2));
}
let mut result: u64 = 0;
let mut shift = 0u32;
for (i, &byte) in data.iter().enumerate() {
if shift >= 64 {
return None; }
result |= ((byte & 0x7F) as u64) << shift;
shift += 7;
if byte < 0x80 {
return Some((result, i + 1));
}
}
None
}
#[inline(always)]
pub fn read_sleb128_fast(data: &[u8]) -> Option<(i64, usize)> {
if data.is_empty() {
return None;
}
let b0 = data[0];
if b0 < 0x80 {
let value = if (b0 & 0x40) != 0 {
(b0 as i64) | !0x7F_i64
} else {
b0 as i64
};
return Some((value, 1));
}
let mut result: i64 = 0;
let mut shift = 0u32;
for (i, &byte) in data.iter().enumerate() {
result |= ((byte & 0x7F) as i64) << shift;
shift += 7;
if byte < 0x80 {
if shift < 64 && (byte & 0x40) != 0 {
result |= !0_i64 << shift;
}
return Some((result, i + 1));
}
}
None
}
#[inline(always)]
pub const fn align_up(value: u64, alignment: u64) -> u64 {
debug_assert!(alignment.is_power_of_two());
(value + alignment - 1) & !(alignment - 1)
}
#[inline(always)]
pub const fn align_down(value: u64, alignment: u64) -> u64 {
debug_assert!(alignment.is_power_of_two());
value & !(alignment - 1)
}
#[inline(always)]
pub const fn is_aligned(value: u64, alignment: u64) -> bool {
debug_assert!(alignment.is_power_of_two());
(value & (alignment - 1)) == 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_read_u64_le() {
let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
assert_eq!(read_u64_le(&data), 0x0807060504030201);
}
#[test]
fn test_read_u32_le() {
let data = [0x01, 0x02, 0x03, 0x04];
assert_eq!(read_u32_le(&data), 0x04030201);
}
#[test]
fn test_memchr_null() {
assert_eq!(memchr_null(b"hello\0world"), 5);
assert_eq!(memchr_null(b"\0"), 0);
assert_eq!(memchr_null(b"hello"), 5);
}
#[test]
fn test_uleb128_fast() {
assert_eq!(read_uleb128_fast(&[0x00]), Some((0, 1)));
assert_eq!(read_uleb128_fast(&[0x01]), Some((1, 1)));
assert_eq!(read_uleb128_fast(&[0x7F]), Some((127, 1)));
assert_eq!(read_uleb128_fast(&[0x80, 0x01]), Some((128, 2)));
assert_eq!(read_uleb128_fast(&[0xFF, 0x01]), Some((255, 2)));
assert_eq!(read_uleb128_fast(&[0xE5, 0x8E, 0x26]), Some((624485, 3)));
}
#[test]
fn test_sleb128_fast() {
assert_eq!(read_sleb128_fast(&[0x00]), Some((0, 1)));
assert_eq!(read_sleb128_fast(&[0x01]), Some((1, 1)));
assert_eq!(read_sleb128_fast(&[0x3F]), Some((63, 1)));
assert_eq!(read_sleb128_fast(&[0x7F]), Some((-1, 1)));
assert_eq!(read_sleb128_fast(&[0x40]), Some((-64, 1)));
}
#[test]
fn test_align_up() {
assert_eq!(align_up(0, 8), 0);
assert_eq!(align_up(1, 8), 8);
assert_eq!(align_up(7, 8), 8);
assert_eq!(align_up(8, 8), 8);
assert_eq!(align_up(9, 8), 16);
assert_eq!(align_up(0x1000, 0x4000), 0x4000);
}
}