diff --git a/src/leb.rs b/src/leb.rs new file mode 100644 index 00000000..d671840d --- /dev/null +++ b/src/leb.rs @@ -0,0 +1,107 @@ +/// # Unsafety +/// `buf` must be large enough to hold the encoded value +pub(crate) unsafe fn leb64(x: u64, buf: &mut [u8]) -> usize { + let mut low = x as u32; + let mut high = (x >> 32) as u32; + + let mut i = 0; + loop { + let mut byte = (low & 0x7f) as u8; + low >>= 7; + if low != 0 { + byte |= 0x80; + } + + *buf.get_unchecked_mut(i) = byte; + i += 1; + if low == 0 { + break; + } + } + + if high == 0 { + return i; + } + + for j in (i - 1)..4 { + *buf.get_unchecked_mut(j) = 0x80; + } + + if i != 5 { + *buf.get_unchecked_mut(4) = 0; + } + + i = 4; + *buf.get_unchecked_mut(i) |= (high as u8 & 0b111) << 4; + high >>= 3; + + if high != 0 { + *buf.get_unchecked_mut(i) |= 0x80; + } + + i += 1; + + if high == 0 { + return i; + } + + loop { + let mut byte = (high & 0x7f) as u8; + high >>= 7; + if high != 0 { + byte |= 0x80; + } + + *buf.get_unchecked_mut(i) = byte; + i += 1; + if high == 0 { + return i; + } + } +} + +#[cfg(test)] +mod tests { + #[test] + fn leb() { + let mut buf = [0x55; 10]; + + let i = unsafe { super::leb64(0, &mut buf) }; + assert_eq!(buf[..i], [0]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64(1, &mut buf) }; + assert_eq!(buf[..i], [1]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64((1 << 7) - 1, &mut buf) }; + assert_eq!(buf[..i], [0x7f]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64(1 << 7, &mut buf) }; + assert_eq!(buf[..i], [0x80, 1]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64((1 << 32) - 1, &mut buf) }; + assert_eq!(buf[..i], [0xff, 0xff, 0xff, 0xff, 0xf]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64((1 << 35) - 1, &mut buf) }; + assert_eq!(buf[..i], [0xff, 0xff, 0xff, 0xff, 0x7f]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64(1 << 35, &mut buf) }; + assert_eq!(buf[..i], [0x80, 0x80, 0x80, 0x80, 0x80, 1]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64((1 << 42) - 1, &mut buf) }; + assert_eq!(buf[..i], [0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]); + buf.iter_mut().for_each(|b| *b = 0x55); + + let i = unsafe { super::leb64(u64::max_value(), &mut buf) }; + assert_eq!( + buf[..i], + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1] + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index b96f3e60..cb1febf5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ use core::{mem::MaybeUninit, ptr::NonNull}; #[doc(hidden)] pub mod export; mod impls; +mod leb; #[cfg(test)] mod tests; @@ -147,68 +148,6 @@ pub struct Formatter { bytes: Vec, } -/// # Unsafety -/// `buf` must be large enough to hold the encoded value -unsafe fn leb64(x: u64, buf: &mut [u8]) -> usize { - let mut low = x as u32; - let mut high = (x >> 32) as u32; - - let mut i = 0; - loop { - let mut byte = (low & 0x7f) as u8; - low >>= 7; - if low != 0 { - byte |= 0x80; - } - - *buf.get_unchecked_mut(i) = byte; - i += 1; - if low == 0 { - break; - } - } - - if high == 0 { - return i; - } - - for j in (i - 1)..4 { - *buf.get_unchecked_mut(j) = 0x80; - } - - if i != 5 { - *buf.get_unchecked_mut(4) = 0; - } - - i = 4; - *buf.get_unchecked_mut(i) |= (high as u8 & 0b111) << 4; - high >>= 3; - - if high != 0 { - *buf.get_unchecked_mut(i) |= 0x80; - } - - i += 1; - - if high == 0 { - return i; - } - - loop { - let mut byte = (high & 0x7f) as u8; - high >>= 7; - if high != 0 { - byte |= 0x80; - } - - *buf.get_unchecked_mut(i) = byte; - i += 1; - if high == 0 { - return i; - } - } -} - impl Formatter { /// Only for testing on x86_64 #[cfg(target_arch = "x86_64")] @@ -273,7 +212,7 @@ impl Formatter { #[doc(hidden)] pub fn leb64(&mut self, x: u64) { let mut buf: [u8; 10] = unsafe { MaybeUninit::uninit().assume_init() }; - let i = unsafe { leb64(x, &mut buf) }; + let i = unsafe { leb::leb64(x, &mut buf) }; self.write(unsafe { buf.get_unchecked(..i) }) } diff --git a/src/tests.rs b/src/tests.rs index 81633ffe..842e2cf3 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,48 +1,5 @@ use crate as binfmt; -#[test] -fn leb() { - let mut buf = [0x55; 10]; - - let i = unsafe { super::leb64(0, &mut buf) }; - assert_eq!(buf[..i], [0]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64(1, &mut buf) }; - assert_eq!(buf[..i], [1]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64((1 << 7) - 1, &mut buf) }; - assert_eq!(buf[..i], [0x7f]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64(1 << 7, &mut buf) }; - assert_eq!(buf[..i], [0x80, 1]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64((1 << 32) - 1, &mut buf) }; - assert_eq!(buf[..i], [0xff, 0xff, 0xff, 0xff, 0xf]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64((1 << 35) - 1, &mut buf) }; - assert_eq!(buf[..i], [0xff, 0xff, 0xff, 0xff, 0x7f]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64(1 << 35, &mut buf) }; - assert_eq!(buf[..i], [0x80, 0x80, 0x80, 0x80, 0x80, 1]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64((1 << 42) - 1, &mut buf) }; - assert_eq!(buf[..i], [0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]); - buf.iter_mut().for_each(|b| *b = 0x55); - - let i = unsafe { super::leb64(u64::max_value(), &mut buf) }; - assert_eq!( - buf[..i], - [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1] - ); -} - #[test] fn log_levels() { // just make sure they build OK for now