use core::hint::unreachable_unchecked;
use imp::EncodedVarInt;
use crate::int::{BasicInt, CInt, CalcFitted, PrimaryInt, PrimarySInt, PrimaryUInt};
const DATA_BITS: u32 = u8::BITS - 1;
pub struct VarInt;
impl VarInt {
pub const fn header_bytes_for(len: usize) -> usize {
let Some(bits) = len.checked_ilog2() else { return 1 };
bits.div_ceil(DATA_BITS) as usize
}
pub const fn max_bytes_for(total_len: usize) -> usize {
let header_bytes = Self::header_bytes_for(total_len);
let max_bytes = total_len - header_bytes;
let true = header_bytes > 1 else { return max_bytes };
let nmax = total_len - header_bytes + 1;
if Self::header_bytes_for(nmax) < header_bytes { nmax } else { max_bytes }
}
pub const fn max_bytes_of<T: PrimaryInt>() -> usize {
const { (T::BITS as usize + 6) / 7 } }
pub const fn decode_unsigned<Tgt: PrimaryUInt>(data: &[u8]) -> Result<(Tgt, usize), ()> {
if let Ok((v, len)) = unsafe { decode_unsigned_imp::<Tgt::CalcFitted>(data.as_ptr(), const { Tgt::CalcFitted::BITS - Tgt::BITS }, data.len()) } {
Ok((CInt::cast_as(v), len))
} else {
Err(())
}
}
pub const unsafe fn decode_unsigned_raw<Tgt: PrimaryUInt>(data: *const u8) -> Result<(Tgt, usize), ()> {
if let Ok((v, len)) =
unsafe { decode_unsigned_imp::<Tgt::CalcFitted>(data, const { Tgt::CalcFitted::BITS - Tgt::BITS }, const { Self::max_bytes_of::<Tgt>() }) }
{
Ok((CInt::cast_as(v), len))
} else {
Err(())
}
}
pub const fn decode_slice<Tgt: PrimaryUInt>(data: &[u8]) -> Result<(&[u8], usize), ()> {
if let Ok((v, hdr_len)) = unsafe { decode_unsigned_imp::<Tgt::CalcFitted>(data.as_ptr(), const { Tgt::CalcFitted::BITS - Tgt::BITS }, data.len()) } {
let len = CInt::cast_as(v);
let total = len + hdr_len;
let true = total <= data.len() else { return Err(()) };
Ok((unsafe { core::slice::from_raw_parts(data.as_ptr().add(hdr_len), len) }, total))
} else {
Err(())
}
}
pub const unsafe fn decode_slice_raw<Tgt: PrimaryUInt>(data: *const u8) -> Result<(&'static [u8], usize), ()> {
if let Ok((v, hdr_len)) =
unsafe { decode_unsigned_imp::<Tgt::CalcFitted>(data, const { Tgt::CalcFitted::BITS - Tgt::BITS }, const { Self::max_bytes_of::<Tgt>() }) }
{
let len = CInt::cast_as(v);
Ok((unsafe { core::slice::from_raw_parts(data.add(hdr_len), len) }, len + hdr_len))
} else {
Err(())
}
}
pub const fn encode_unsigned_to<Src: PrimaryUInt>(v: Src, buf: &mut [u8]) -> Option<&mut [u8]> {
encode_unsigned_imp(CInt::cast_as_calc(v), buf)
}
pub const fn encode_unsigned<Src: PrimaryUInt>(v: Src) -> EncodedVarInt<<Src::CalcFitted as CalcFitted>::VarIntBuf> {
let mut buf = EncodedVarInt::new();
let Some(out) = encode_unsigned_imp(CInt::cast_as_calc(v), buf.as_mut()) else { unsafe { unreachable_unchecked() } };
let len = out.len();
buf.set_len(len);
buf
}
const fn encode_zigzag<Src: PrimarySInt>(src: Src) -> Src::Unsigned {
CInt::cast_as(encode_zigzag_imp(CInt::cast_as_calc(src)))
}
const fn decode_zigzag<Src: PrimaryUInt>(src: Src) -> Src::Signed {
CInt::cast_as(decode_zigzag_imp(CInt::cast_as_calc(src)))
}
pub const fn encode_to<Src: PrimaryInt>(v: Src, buf: &mut [u8]) -> Option<&mut [u8]> {
if Src::SIGNED {
Self::encode_unsigned_to(Self::encode_zigzag(CInt::cast_as_signed(v)), buf)
} else {
Self::encode_unsigned_to(CInt::cast_as_unsigned(v), buf)
}
}
pub const fn encode<Src: PrimaryInt>(v: Src) -> EncodedVarInt<<<Src::Unsigned as BasicInt>::CalcFitted as CalcFitted>::VarIntBuf> {
if Src::SIGNED { Self::encode_unsigned(Self::encode_zigzag(CInt::cast_as_signed(v))) } else { Self::encode_unsigned(CInt::cast_as_unsigned(v)) }
}
pub const fn decode<Tgt: PrimaryInt>(data: &[u8]) -> Result<(Tgt, usize), ()> {
let Ok((v, len)) = Self::decode_unsigned::<Tgt::Unsigned>(data) else { return Err(()) };
if Tgt::SIGNED { Ok((CInt::cast_from_signed(Self::decode_zigzag(v)), len)) } else { Ok((CInt::cast_from_unsigned(v), len)) }
}
pub const unsafe fn decode_raw<Tgt: PrimaryInt>(data: *const u8) -> Result<(Tgt, usize), ()> {
let Ok((v, len)) = (unsafe { Self::decode_unsigned_raw::<Tgt::Unsigned>(data) }) else { return Err(()) };
if Tgt::SIGNED { Ok((CInt::cast_from_signed(Self::decode_zigzag(v)), len)) } else { Ok((CInt::cast_from_unsigned(v), len)) }
}
}
const unsafe fn decode_unsigned_imp<Tgt: PrimaryInt + CalcFitted>(data: *const u8, zero_bits: u32, max_bytes: usize) -> Result<(Tgt, usize), ()> {
let mut v = Tgt::ZERO;
let mut i = 0;
loop {
let true = i < max_bytes else { return Err(()) };
let byte = unsafe { data.add(i).read() };
let Some(nv) = CInt::checked_shl(CInt::cast_as(byte & 0b0111_1111), DATA_BITS * i as u32) else { return Err(()) };
v = CInt::bitor(v, nv);
i += 1;
if byte & 0b1000_0000 == 0 {
break;
}
}
if CInt::leading_zeros(v) >= zero_bits { Ok((v, i)) } else { Err(()) }
}
const fn encode_unsigned_imp<Src: PrimaryInt + CalcFitted>(mut v: Src, buf: &mut [u8]) -> Option<&mut [u8]> {
let max_len = buf.len();
let out = buf.as_mut_ptr();
let mut i = 0;
while i < max_len {
let byte: u8 = CInt::cast_as(v);
v = CInt::shr(v, DATA_BITS);
let len = i + 1;
if CInt::eq(v, Src::ZERO) {
unsafe { out.add(i).write(byte) };
return Some(unsafe { core::slice::from_raw_parts_mut(out, len) });
} else {
unsafe { out.add(i).write(byte | 0b1000_0000) };
}
i = len;
}
None
}
const fn encode_zigzag_imp<Src: PrimaryInt + CalcFitted>(src: Src) -> Src::Unsigned {
CInt::cast_as_unsigned(CInt::bitxor(CInt::shl(src, 1), CInt::shr(src, const { Src::BITS - 1 })))
}
const fn decode_zigzag_imp<Src: PrimaryInt + CalcFitted>(src: Src) -> Src::Signed {
CInt::cast_as_signed(CInt::bitxor(CInt::shr(src, 1), CInt::wrapping_neg(CInt::bitand(src, Src::ONE))))
}
mod imp {
use core::{mem::MaybeUninit, ops::Deref};
#[derive(Clone, Copy)]
pub struct EncodedVarInt<Buf: Copy> {
len: u8,
data: MaybeUninit<Buf>,
}
impl<Buf: Copy> EncodedVarInt<Buf> {
pub(super) const fn new() -> Self {
Self { len: 0, data: MaybeUninit::uninit() }
}
pub(super) const fn as_mut(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.data.as_mut_ptr().cast::<u8>(), size_of::<Buf>()) }
}
pub(super) const fn set_len(&mut self, len: usize) {
debug_assert!(len <= size_of::<Buf>());
self.len = len as u8;
}
pub const fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.data.as_ptr().cast::<u8>(), self.len as usize) }
}
}
impl<Buf: Copy> Deref for EncodedVarInt<Buf> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
}
#[cfg(test)]
mod test {
use super::VarInt;
#[test]
#[cfg_attr(miri, ignore)]
fn varint() {
for i in u16::MIN..u16::MAX {
let buf = VarInt::encode_unsigned(i);
assert_eq!(VarInt::decode_unsigned::<u16>(&buf).unwrap().0, i);
}
let data = [3, 106, 97, 100];
let result = [106, 97, 100];
assert_eq!(VarInt::decode_slice::<usize>(&data), Ok((result.as_slice(), 4)));
}
#[test]
fn zigzag() {
assert_eq!(VarInt::encode_zigzag(0i8), 0u8);
assert_eq!(VarInt::encode_zigzag(-1i8), 1u8);
assert_eq!(VarInt::encode_zigzag(1i8), 2u8);
assert_eq!(VarInt::encode_zigzag(-2i8), 3u8);
assert_eq!(VarInt::encode_zigzag(2i8), 4u8);
assert_eq!(VarInt::encode_zigzag(-3i8), 5u8);
assert_eq!(VarInt::encode_zigzag(3i8), 6u8);
assert_eq!(VarInt::encode_zigzag(-4i8), 7u8);
assert_eq!(VarInt::encode_zigzag(4i8), 8u8);
assert_eq!(VarInt::encode_zigzag(-128i8), 255u8);
for i in i8::MIN..0 {
assert_eq!(VarInt::encode_zigzag(i), (i.unsigned_abs() - 1) * 2 + 1); assert_eq!(VarInt::decode_zigzag(VarInt::encode_zigzag(i)), i);
}
for i in 0..=i8::MAX {
assert_eq!(VarInt::encode_zigzag(i), (i as u8) * 2);
assert_eq!(VarInt::decode_zigzag(VarInt::encode_zigzag(i)), i);
}
}
}