use super::{write_into, WriteInto};
use std::io;
use std::mem::{size_of, MaybeUninit};
pub struct Uleb128<T>(pub T);
pub struct Sleb128<T>(pub T);
macro_rules! impl_write_into {
($($wrapper:ident => { $($primitive:ident)* }),*,) => {
$(
$(
impl_impl!($wrapper, $primitive);
)*
)*
}
}
macro_rules! impl_impl {
(Uleb128, $primitive:ident) => {
impl WriteInto for Uleb128<$primitive> {
type Output = usize;
fn write_into(mut self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
let mut buffer = unsafe {
MaybeUninit::<[MaybeUninit<u8>; max_leb128_size(size_of::<Self>())]>::uninit()
.assume_init()
};
let mut written = 0;
for byte in buffer.iter_mut() {
let mut value = self.0 as u8 & 0x7F;
self.0 >>= 7;
if self.0 != 0 {
value |= 0x80;
}
*byte = MaybeUninit::new(value);
written += 1;
if self.0 == 0 {
break;
}
}
let bytes = unsafe {
&*(&buffer[..written] as *const [MaybeUninit<u8>] as *const [u8])
};
sink.write_all(bytes)?;
Ok(written)
}
}
impl WriteInto for &Uleb128<$primitive> {
type Output = usize;
fn write_into(self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
write_into(sink, Uleb128(self.0))
}
}
};
(Sleb128, $primitive:ident) => {
impl WriteInto for Sleb128<$primitive> {
type Output = usize;
fn write_into(mut self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
let mut buffer = unsafe {
MaybeUninit::<[MaybeUninit<u8>; max_leb128_size(size_of::<Self>())]>::uninit()
.assume_init()
};
let mut written = 0;
for byte in buffer.iter_mut() {
let mut value = self.0 as u8;
self.0 >>= 6; let done = self.0 == 0 || self.0 == -1;
if done {
value &= 0x7F;
} else {
self.0 >>= 1;
value |= 0x80;
}
*byte = MaybeUninit::new(value);
written += 1;
if done {
break;
}
}
let bytes = unsafe {
&*(&buffer[..written] as *const [MaybeUninit<u8>] as *const [u8])
};
sink.write_all(bytes)?;
Ok(written)
}
}
impl WriteInto for &Sleb128<$primitive> {
type Output = usize;
fn write_into(self, sink: &mut impl io::Write) -> io::Result<Self::Output> {
write_into(sink, Sleb128(self.0))
}
}
};
}
impl_write_into! {
Uleb128 => {
u8 u16 u32 u64 u128 usize
},
Sleb128 => {
i8 i16 i32 i64 i128 isize
},
}
const fn max_leb128_size(bytes: usize) -> usize {
let bits = bytes * 8;
let septets = count_bits_in_chunks(bits, 7);
let bits_for_septents = septets * 7;
let bits_for_continutation_bits = septets * 1;
count_bits_in_chunks(bits_for_septents + bits_for_continutation_bits, 8)
}
const fn count_bits_in_chunks(bits: usize, chunk_size: usize) -> usize {
let chunks = bits / chunk_size;
let remaining = bits % chunk_size;
chunks + if remaining != 0 { 1 } else { 0 }
}
#[cfg(test)]
mod tests {
use super::super::*;
use super::*;
use test_case::test_case;
use validators::vec;
mod validators {
pub fn vec(expected: &[u8]) -> impl FnOnce(Vec<u8>) {
let expected = expected.to_vec();
move |actual| assert_eq!(&expected, &actual)
}
}
#[test_case( 1 => 2; "when u8" )]
#[test_case( 2 => 3; "when u16" )]
#[test_case( 4 => 5; "when u32" )]
#[test_case( 8 => 10; "when u64" )]
#[test_case( 16 => 19; "when u128" )]
fn max_leb128_size_for_primitive_types(bytes: usize) -> usize {
max_leb128_size(bytes)
}
#[test_case( 0 => using vec(&[ 0x00 ]); "when 0" )]
#[test_case( 69 => using vec(&[ 0x45 ]); "when 69" )]
#[test_case( 123 => using vec(&[ 0x7B ]); "when 123" )]
#[test_case( 127 => using vec(&[ 0x7F ]); "when 127" )]
#[test_case( 128 => using vec(&[ 0x80, 0x01 ]); "when 128" )]
#[test_case( 228 => using vec(&[ 0xE4, 0x01 ]); "when 228" )]
#[test_case( 255 => using vec(&[ 0xFF, 0x01 ]); "when 255" )]
#[test_case( 4200 => using vec(&[ 0xE8, 0x20 ]); "when 4200" )]
#[test_case( 16383 => using vec(&[ 0xFF, 0x7F ]); "when 16383" )]
#[test_case( 32767 => using vec(&[ 0xFF, 0xFF, 0x01 ]); "when 32767" )]
#[test_case( 42000 => using vec(&[ 0x90, 0xC8, 0x02 ]); "when 42000" )]
#[test_case( 65535 => using vec(&[ 0xFF, 0xFF, 0x03 ]); "when 65535" )]
fn write_u16(number: u16) -> Vec<u8> {
let mut buffer = Vec::new();
write_into(&mut buffer, Uleb128(number)).unwrap();
buffer
}
#[test_case( -32768 => using vec(&[ 0x80, 0x80, 0x7E ]); "when minus 32768" )]
#[test_case( -8192 => using vec(&[ 0x80, 0x40 ]); "when minus 8192" )]
#[test_case( -4200 => using vec(&[ 0x98, 0x5F ]); "when minus 4200" )]
#[test_case( -128 => using vec(&[ 0x80, 0x7F ]); "when minus 128" )]
#[test_case( -123 => using vec(&[ 0x85, 0x7F ]); "when minus 123" )]
#[test_case( -69 => using vec(&[ 0xBB, 0x7F ]); "when minus 69" )]
#[test_case( -34 => using vec(&[ 0x5E ]); "when minus 34" )]
#[test_case( 0 => using vec(&[ 0x00 ]); "when 0" )]
#[test_case( 34 => using vec(&[ 0x22 ]); "when 34" )]
#[test_case( 69 => using vec(&[ 0xC5, 0x00 ]); "when 69" )]
#[test_case( 123 => using vec(&[ 0xFB, 0x00 ]); "when 123" )]
#[test_case( 127 => using vec(&[ 0xFF, 0x00 ]); "when 127" )]
#[test_case( 4200 => using vec(&[ 0xE8, 0x20 ]); "when 4200" )]
#[test_case( 8191 => using vec(&[ 0xFF, 0x3F ]); "when 8191" )]
#[test_case( 32767 => using vec(&[ 0xFF, 0xFF, 0x01 ]); "when 32767" )]
fn write_i16(number: i16) -> Vec<u8> {
let mut buffer = Vec::new();
write_into(&mut buffer, Sleb128(number)).unwrap();
buffer
}
}