#![deny(clippy::arithmetic_side_effects)]
pub fn push_uleb(out: &mut Vec<u8>, mut value: u64) {
loop {
let byte = (value & 0x7f) as u8;
value >>= 7;
if value == 0 {
out.push(byte);
return;
}
out.push(byte | 0x80);
}
}
pub fn push_sleb(out: &mut Vec<u8>, mut value: i64) {
loop {
let byte = (value & 0x7f) as u8;
value >>= 7;
let done = (value == 0 && byte & 0x40 == 0) || (value == -1 && byte & 0x40 != 0);
if done {
out.push(byte);
return;
}
out.push(byte | 0x80);
}
}
#[must_use]
pub fn uleb_size(value: u64) -> usize {
let bits = 64u32.saturating_sub(value.leading_zeros()).max(1);
usize::try_from(bits.div_ceil(7)).unwrap_or(10)
}
pub fn push16(out: &mut Vec<u8>, value: u16) {
out.extend_from_slice(&value.to_le_bytes());
}
pub fn push32(out: &mut Vec<u8>, value: u32) {
out.extend_from_slice(&value.to_le_bytes());
}
pub fn push64(out: &mut Vec<u8>, value: u64) {
out.extend_from_slice(&value.to_le_bytes());
}
pub fn push_name16(out: &mut Vec<u8>, name: &[u8]) {
let mut field = [0u8; 16];
for (slot, &byte) in field.iter_mut().zip(name) {
*slot = byte;
}
out.extend_from_slice(&field);
}
pub fn pad_to(out: &mut Vec<u8>, align: usize) {
let len = out.len().next_multiple_of(align.max(1));
out.resize(len, 0);
}
pub fn put32(out: &mut [u8], at: usize, value: u32) -> Option<()> {
out.get_mut(at..at.checked_add(4)?)?
.copy_from_slice(&value.to_le_bytes());
Some(())
}
pub fn put64(out: &mut [u8], at: usize, value: u64) -> Option<()> {
out.get_mut(at..at.checked_add(8)?)?
.copy_from_slice(&value.to_le_bytes());
Some(())
}
#[must_use]
pub fn get32(data: &[u8], at: usize) -> Option<u32> {
Some(u32::from_le_bytes(*data.get(at..)?.first_chunk::<4>()?))
}
#[must_use]
pub fn get64(data: &[u8], at: usize) -> Option<u64> {
Some(u64::from_le_bytes(*data.get(at..)?.first_chunk::<8>()?))
}
#[must_use]
pub fn to_usize(value: u64) -> usize {
usize::try_from(value).unwrap_or(usize::MAX)
}
#[must_use]
pub fn to_u64(value: usize) -> u64 {
u64::try_from(value).unwrap_or(u64::MAX)
}
#[must_use]
pub fn align_up(value: u64, align: u64) -> u64 {
if align <= 1 {
return value;
}
let mask = align.saturating_sub(1);
value.checked_add(mask).map_or(u64::MAX, |v| v & !mask)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::macho::read::bytes::{read_sleb, read_uleb};
#[test]
fn leb_round_trip() {
for value in [0u64, 1, 127, 128, 300, 624_485, u64::MAX] {
let mut out = Vec::new();
push_uleb(&mut out, value);
assert_eq!(out.len(), uleb_size(value));
let mut pos = 0;
assert_eq!(read_uleb(&out, &mut pos), Some(value));
}
for value in [0i64, 1, -1, 63, 64, -64, -65, -123_456, i64::MIN, i64::MAX] {
let mut out = Vec::new();
push_sleb(&mut out, value);
let mut pos = 0;
assert_eq!(read_sleb(&out, &mut pos), Some(value));
}
assert_eq!(align_up(13, 8), 16);
assert_eq!(align_up(16, 8), 16);
}
}