use std::mem::size_of;
pub const MSB: u8 = 0b1000_0000;
const DROP_MSB: u8 = 0b0111_1111;
#[inline]
fn required_encoded_space_unsigned(mut v: u64) -> usize {
if v == 0 {
return 1;
}
let mut logcounter = 0;
while v > 0 {
logcounter += 1;
v >>= 7;
}
logcounter
}
#[inline]
fn required_encoded_space_signed(v: i64) -> usize {
required_encoded_space_unsigned(zigzag_encode(v))
}
pub trait VarInt: Sized + Copy {
fn required_space(self) -> usize;
fn decode_var(src: &[u8]) -> Option<(Self, usize)>;
fn encode_var(self, src: &mut [u8]) -> usize;
fn encode_var_vec(self) -> Vec<u8> {
let mut v = vec![0; self.required_space()];
self.encode_var(&mut v);
v
}
}
#[inline]
fn zigzag_encode(from: i64) -> u64 {
((from << 1) ^ (from >> 63)) as u64
}
#[inline]
fn zigzag_decode(from: u64) -> i64 {
((from >> 1) ^ (-((from & 1) as i64)) as u64) as i64
}
pub(crate) trait VarIntMaxSize {
fn varint_max_size() -> usize;
}
impl<VI: VarInt> VarIntMaxSize for VI {
fn varint_max_size() -> usize {
(size_of::<VI>() * 8 + 7) / 7
}
}
macro_rules! impl_varint {
($t:ty, unsigned) => {
impl VarInt for $t {
fn required_space(self) -> usize {
required_encoded_space_unsigned(self as u64)
}
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let (n, s) = u64::decode_var(src)?;
Some((<Self as std::convert::TryFrom<u64>>::try_from(n).ok()?, s))
}
fn encode_var(self, dst: &mut [u8]) -> usize {
(self as u64).encode_var(dst)
}
}
};
($t:ty, signed) => {
impl VarInt for $t {
fn required_space(self) -> usize {
required_encoded_space_signed(self as i64)
}
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let (n, s) = i64::decode_var(src)?;
Some((<Self as std::convert::TryFrom<i64>>::try_from(n).ok()?, s))
}
fn encode_var(self, dst: &mut [u8]) -> usize {
(self as i64).encode_var(dst)
}
}
};
}
impl_varint!(usize, unsigned);
impl_varint!(u32, unsigned);
impl_varint!(u16, unsigned);
impl_varint!(u8, unsigned);
impl_varint!(isize, signed);
impl_varint!(i32, signed);
impl_varint!(i16, signed);
impl_varint!(i8, signed);
impl VarInt for u64 {
fn required_space(self) -> usize {
required_encoded_space_unsigned(self)
}
#[inline]
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let mut result: u64 = 0;
let mut shift = 0;
let mut success = false;
for b in src {
let msb_dropped = b & DROP_MSB;
result |= u64::from(msb_dropped) << shift;
shift += 7;
if shift > (9 * 7) {
success = *b < 2;
break;
} else if b & MSB == 0 {
success = true;
break;
}
}
if success {
Some((result, shift / 7))
} else {
None
}
}
#[inline]
fn encode_var(self, dst: &mut [u8]) -> usize {
debug_assert!(dst.len() >= self.required_space());
let mut n = self;
let mut i = 0;
while n >= 0x80 {
dst[i] = MSB | (n as u8);
i += 1;
n >>= 7;
}
dst[i] = n as u8;
i + 1
}
}
impl VarInt for i64 {
fn required_space(self) -> usize {
required_encoded_space_signed(self)
}
#[inline]
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
if let Some((result, size)) = u64::decode_var(src) {
Some((zigzag_decode(result), size))
} else {
None
}
}
#[inline]
fn encode_var(self, dst: &mut [u8]) -> usize {
debug_assert!(dst.len() >= self.required_space());
let mut n: u64 = zigzag_encode(self);
let mut i = 0;
while n >= 0x80 {
dst[i] = MSB | (n as u8);
i += 1;
n >>= 7;
}
dst[i] = n as u8;
i + 1
}
}