macro_rules! varint_read {
($primitive: ty, $read_varint: ident, $inside_type: ty, $read_raw: ident) => {
fn $read_varint(&mut self) -> Result<$primitive> {
const SIZE: usize = std::mem::size_of::<$primitive>() << 3;const NUM_BITS: $inside_type = <$inside_type>::MAX >> 1;
const SIGN_BIT: $inside_type = NUM_BITS + 1;
const POS_OFFSET: usize = (<$inside_type>::BITS - 1) as usize;
let mut value = 0;
let mut position = 0;
loop {
let current = self.$read_raw()?;
value |= ((current & NUM_BITS) as $primitive) << position;
if current & SIGN_BIT == 0 {
break;
}
position += POS_OFFSET;
if position >= SIZE {
return Err(Error::new(ErrorKind::InvalidData, format!("Varint {} in stream is too long.", stringify!($read_varint))));
}
}
Ok(value)
}
};
}
pub(crate) use varint_read;
macro_rules! define_varint_read {
() => {
varint::varint_read!(u16, read_u16_varint, u8, read_u8_ne);
varint::varint_read!(u32, read_u32_varint, u8, read_u8_ne);
#[cfg(feature = "long_varint")]
varint::varint_read!(u32, read_u32_varint_2_le, u16, read_u16_le);
#[cfg(feature = "long_varint")]
varint::varint_read!(u32, read_u32_varint_2_be, u16, read_u16_be);
varint::varint_read!(u64, read_u64_varint, u8, read_u8_ne);
#[cfg(feature = "long_varint")]
varint::varint_read!(u64, read_u64_varint_2_le, u16, read_u16_le);
#[cfg(feature = "long_varint")]
varint::varint_read!(u64, read_u64_varint_2_be, u16, read_u16_be);
#[cfg(feature = "long_varint")]
varint::varint_read!(u64, read_u64_varint_4_le, u32, read_u32_le);
#[cfg(feature = "long_varint")]
varint::varint_read!(u64, read_u64_varint_4_be, u32, read_u32_be);
varint::varint_read!(u128, read_u128_varint, u8, read_u8_ne);
#[cfg(feature = "long_varint")]
varint::varint_read!(u128, read_u128_varint_2_le, u16, read_u16_le);
#[cfg(feature = "long_varint")]
varint::varint_read!(u128, read_u128_varint_2_be, u16, read_u16_be);
#[cfg(feature = "long_varint")]
varint::varint_read!(u128, read_u128_varint_4_le, u32, read_u32_le);
#[cfg(feature = "long_varint")]
varint::varint_read!(u128, read_u128_varint_4_be, u32, read_u32_be);
#[cfg(feature = "long_varint")]
varint::varint_read!(u128, read_u128_varint_8_le, u64, read_u64_le);
#[cfg(feature = "long_varint")]
varint::varint_read!(u128, read_u128_varint_8_be, u64, read_u64_be);
};
}
pub(crate) use define_varint_read;
macro_rules! varint_write {
($primitive: ty, $write_varint: ident, $inside_type: ty, $write_raw: ident) => {
fn $write_varint(&mut self, num: $primitive) -> Result<usize> {
const NUM_BITS: $inside_type = <$inside_type>::MAX >> 1;
const SIGN_BIT: $inside_type = NUM_BITS + 1;
const POS_OFFSET: usize = (<$inside_type>::BITS - 1) as usize;
let mut size = 0;
let mut value = num;
while value >= SIGN_BIT as $primitive {
size += self.$write_raw(((value & (NUM_BITS as $primitive)) as $inside_type) | SIGN_BIT)?;
value >>= POS_OFFSET;
}
size += self.$write_raw((value & (NUM_BITS as $primitive)) as $inside_type)?;
Ok(size)
}
};
}
pub(crate) use varint_write;
macro_rules! define_varint_write {
() => {
varint::varint_write!(u16, write_u16_varint, u8, write_u8_ne);
varint::varint_write!(u32, write_u32_varint, u8, write_u8_ne);
#[cfg(feature = "long_varint")]
varint::varint_write!(u32, write_u32_varint_2_le, u16, write_u16_le);
#[cfg(feature = "long_varint")]
varint::varint_write!(u32, write_u32_varint_2_be, u16, write_u16_be);
varint::varint_write!(u64, write_u64_varint, u8, write_u8_ne);
#[cfg(feature = "long_varint")]
varint::varint_write!(u64, write_u64_varint_2_le, u16, write_u16_le);
#[cfg(feature = "long_varint")]
varint::varint_write!(u64, write_u64_varint_2_be, u16, write_u16_be);
#[cfg(feature = "long_varint")]
varint::varint_write!(u64, write_u64_varint_4_le, u32, write_u32_le);
#[cfg(feature = "long_varint")]
varint::varint_write!(u64, write_u64_varint_4_be, u32, write_u32_be);
varint::varint_write!(u128, write_u128_varint, u8, write_u8_ne);
#[cfg(feature = "long_varint")]
varint::varint_write!(u128, write_u128_varint_2_le, u16, write_u16_le);
#[cfg(feature = "long_varint")]
varint::varint_write!(u128, write_u128_varint_2_be, u16, write_u16_be);
#[cfg(feature = "long_varint")]
varint::varint_write!(u128, write_u128_varint_4_le, u32, write_u32_le);
#[cfg(feature = "long_varint")]
varint::varint_write!(u128, write_u128_varint_4_be, u32, write_u32_be);
#[cfg(feature = "long_varint")]
varint::varint_write!(u128, write_u128_varint_8_le, u64, write_u64_le);
#[cfg(feature = "long_varint")]
varint::varint_write!(u128, write_u128_varint_8_be, u64, write_u64_be);
};
}
pub(crate) use define_varint_write;
#[cfg(test)]
mod test {
use std::io::Cursor;
use crate::{VariableReadable, VariableWritable};
macro_rules! varint_test_0 {
($tester: ident, $primitive: ty, $reader: ident, $writer: ident, $num: expr) => {
let p = $num;
let mut cursor = Cursor::new(Vec::new());
cursor.$writer(p).expect(&format!("Failed to write {} at {}.", p, stringify!($tester)));
cursor.set_position(0);
let q = cursor.$reader().expect(&format!("Failed to read {} at {}.", p, stringify!($tester)));
assert_eq!(p, q, "Not same: {} != {} at {}. bytes: {:?}", p, q, stringify!($tester), cursor.into_inner());
};
}
macro_rules! varint_test {
($tester: ident, $primitive: ty, $reader: ident, $writer: ident) => {
#[test]
fn $tester() {
varint_test_0!($tester, $primitive, $reader, $writer, <$primitive>::MIN);
varint_test_0!($tester, $primitive, $reader, $writer, <$primitive>::MAX);
varint_test_0!($tester, $primitive, $reader, $writer, 0);
for p in <$primitive>::MIN..=<$primitive>::MAX {
varint_test_0!($tester, $primitive, $reader, $writer, p);
}
}
};
}
varint_test!(u16_ne, u16, read_u16_varint, write_u16_varint);
}