use bnum_0_13::{BInt, BIntD8, BIntD16, BIntD32, BUint, BUintD8, BUintD16, BUintD32};
use crate::{ConstDecodeError, ConstEncodeError, Varint};
use core::num::NonZeroUsize;
macro_rules! unsigned {
($($base:ident($storage:literal)), +$(,)?) => {
paste::paste! {
$(
/// Returns the encoded length of the value in LEB128 variable length format.
#[doc = "The returned value will be in range of [`" $base "::<N>::ENCODED_LEN_RANGE`](Varint::ENCODED_LEN_RANGE)."]
#[inline]
pub const fn [< encoded_uint_d $storage _len >]<const N: usize>(val: &$base<N>) -> NonZeroUsize {
if N == 0 {
return crate::NON_ZERO_USIZE_ONE;
}
if val.is_zero() {
return crate::NON_ZERO_USIZE_ONE;
}
let highest_bit = ((N * $storage) as u32) - val.leading_zeros();
unsafe { NonZeroUsize::new_unchecked(highest_bit.div_ceil(7) as usize) }
}
#[doc = "Encodes an `" $base "<N>` value into LEB128 variable length format, and writes it to the buffer."]
#[inline]
pub const fn [< encode_uint_d $storage _to >]<const N: usize>(
mut value: $base<N>,
buf: &mut [u8],
) -> Result<NonZeroUsize, ConstEncodeError> {
let len = [< encoded_uint_d $storage _len >](&value);
let buf_len = buf.len();
if buf_len < len.get() {
return Err(ConstEncodeError::insufficient_space(len, buf_len));
}
if N == 0 {
buf[0] = 0;
return Ok(crate::NON_ZERO_USIZE_ONE);
}
let mut bytes_written = 0;
loop {
let mut byte = value.bitand($base::<N>::from_digit([< 0x7fu $storage >].to_le())).digits()[0] as u8;
value = value.shr(7);
if !value.is_zero() {
byte |= 0x80;
}
buf[bytes_written] = byte;
bytes_written += 1;
if value.is_zero() {
break;
}
}
Ok(unsafe { NonZeroUsize::new_unchecked(bytes_written) })
}
#[doc = "Decodes an `" $base "<N>` in LEB128 encoded format from the buffer."]
pub const fn [< decode_uint_d $storage >]<const N: usize>(
buf: &[u8],
) -> Result<(NonZeroUsize, $base<N>), ConstDecodeError> {
if buf.is_empty() {
return Err(ConstDecodeError::insufficient_data(buf.len()));
}
if N == 0 {
return if buf[0] == 0 {
Ok((crate::NON_ZERO_USIZE_ONE, $base::<N>::ZERO))
} else {
Err(ConstDecodeError::Overflow)
};
}
let mut result = $base::<N>::ZERO;
let mut shift = 0;
let mut bytes_read = 0;
while bytes_read < buf.len() {
let byte = buf[bytes_read];
let value = $base::<N>::from_digit((byte & 0x7f).to_le() as [< u$storage >]);
if shift >= N * $storage {
return Err(ConstDecodeError::Overflow);
}
if let Some(shifted) = value.checked_shl(shift as u32) {
result = result.bitor(shifted);
} else {
return Err(ConstDecodeError::Overflow);
}
bytes_read += 1;
if byte & 0x80 == 0 {
return Ok((unsafe { NonZeroUsize::new_unchecked(bytes_read) }, result));
}
shift += 7;
}
Err(ConstDecodeError::insufficient_data(buf.len()))
}
impl<const N: usize> Varint for $base<N> {
const MIN_ENCODED_LEN: NonZeroUsize = crate::NON_ZERO_USIZE_ONE;
const MAX_ENCODED_LEN: NonZeroUsize = { if N == 0 {
crate::NON_ZERO_USIZE_ONE
} else {
NonZeroUsize::new((N * $storage).div_ceil(7)).unwrap()
} };
fn encoded_len(&self) -> NonZeroUsize {
[< encoded_uint_d $storage _len >](self)
}
fn encode(&self, buf: &mut [u8]) -> Result<NonZeroUsize, crate::EncodeError> {
[< encode_uint_d $storage _to >](*self, buf).map_err(Into::into)
}
fn decode(buf: &[u8]) -> Result<(NonZeroUsize, Self), crate::DecodeError>
where
Self: Sized,
{
[< decode_uint_d $storage >](buf).map_err(Into::into)
}
}
)*
}
};
}
macro_rules! signed {
($($storage:literal($i:ident <=> $u:ident)), +$(,)?) => {
paste::paste! {
$(
#[doc = "Zigzag encode `" $i "<N>` value"]
#[inline]
pub const fn [< zigzag_encode_int_d $storage >]<const N: usize>(value: &$i<N>) -> $u<N> {
if N == 0 {
return $u::<N>::ZERO;
}
let bits = match (N * $storage).checked_sub(1) {
Some(val) => val,
None => 0,
};
value.shl(1).bitxor(value.shr(bits as u32)).to_bits()
}
#[doc = "Zigzag decode `" $i "<N>` value"]
#[inline]
pub const fn [< zigzag_decode_int_d $storage >]<const N: usize>(value: &$u<N>) -> $i<N> {
if N == 0 {
return $i::<N>::ZERO;
}
let a = $i::<N>::from_bits(value.shr(1));
let b = $i::<N>::from_bits(value.bitand($u::<N>::from_digit(1))).neg();
a.bitxor(b)
}
#[doc = "The returned value will be in range of [`" $i "::<N>::ENCODED_LEN_RANGE`](Varint::ENCODED_LEN_RANGE)."]
#[inline]
pub const fn [< encoded_int_d $storage _len >]<const N: usize>(val: &$i<N>) -> NonZeroUsize {
if N == 0 {
return crate::NON_ZERO_USIZE_ONE;
}
[< encoded_uint_d $storage _len >](&[< zigzag_encode_int_d $storage>](&val))
}
#[doc = "Encodes an `" $i "<N>` value into LEB128 variable length format, and writes it to the buffer."]
#[inline]
pub const fn [< encode_int_d $storage _to >]<const N: usize>(
value: $i<N>,
buf: &mut [u8],
) -> Result<NonZeroUsize, ConstEncodeError> {
if N == 0 {
if buf.is_empty() {
return Err(ConstEncodeError::insufficient_space(crate::NON_ZERO_USIZE_ONE, 0));
}
buf[0] = 0;
return Ok(crate::NON_ZERO_USIZE_ONE);
}
[< encode_uint_d $storage _to>]([< zigzag_encode_int_d $storage>](&value), buf)
}
#[doc = "Decodes an `" $i "<N>` in LEB128 encoded format from the buffer."]
pub const fn [< decode_int_d $storage >]<const N: usize>(
buf: &[u8],
) -> Result<(NonZeroUsize, $i<N>), ConstDecodeError> {
if N == 0 {
if buf.is_empty() {
return Err(ConstDecodeError::insufficient_data(buf.len()));
}
if buf[0] != 0 {
return Err(ConstDecodeError::Overflow);
}
return Ok((crate::NON_ZERO_USIZE_ONE, $i::<N>::ZERO));
}
if buf.is_empty() {
return Err(ConstDecodeError::insufficient_data(buf.len()));
}
match [< decode_uint_d $storage >]::<N>(buf) {
Ok((read, val)) => {
let val = [<zigzag_decode_int_d $storage>](&val);
Ok((read, val))
},
Err(e) => Err(e),
}
}
impl<const N: usize> Varint for $i<N> {
const MIN_ENCODED_LEN: NonZeroUsize = $u::<N>::MIN_ENCODED_LEN;
const MAX_ENCODED_LEN: NonZeroUsize = $u::<N>::MAX_ENCODED_LEN;
fn encoded_len(&self) -> NonZeroUsize {
[< encoded_int_d $storage _len >](self)
}
fn encode(&self, buf: &mut [u8]) -> Result<NonZeroUsize, crate::EncodeError> {
[< encode_int_d $storage _to >](*self, buf).map_err(Into::into)
}
fn decode(buf: &[u8]) -> Result<(NonZeroUsize, Self), crate::DecodeError>
where
Self: Sized,
{
[< decode_int_d $storage >](buf).map_err(Into::into)
}
}
)*
}
};
}
unsigned!(BUintD8(8), BUintD16(16), BUintD32(32), BUint(64));
signed!(8(BIntD8 <=> BUintD8), 16(BIntD16 <=> BUintD16), 32(BIntD32 <=> BUintD32), 64(BInt <=> BUint));
#[cfg(test)]
mod tests {
use super::*;
macro_rules! fuzzy {
($base:ident($($ty:ident), +$(,)?)) => {
$(
paste::paste! {
#[quickcheck_macros::quickcheck]
fn [< fuzzy_$base:snake _ $ty:snake >](value: $ty) -> bool {
let mut buf = [0; <$ty>::MAX_ENCODED_LEN.get()];
let Ok(encoded_len) = value.encode(&mut buf) else { return false; };
if encoded_len != value.encoded_len() || !(value.encoded_len() <= <$ty>::MAX_ENCODED_LEN) {
return false;
}
let Some(consumed) = crate::consume_varint_checked(&buf) else {
return false;
};
if consumed != encoded_len {
return false;
}
if let Ok((bytes_read, decoded)) = <$ty>::decode(&buf) {
value == decoded && encoded_len == bytes_read
} else {
false
}
}
}
)*
};
}
macro_rules! define_aliases {
($sign:ident::$base:ident ($($ty:literal), +$(,)?)) => {
paste::paste! {
$(
type [< $sign:camel $ty >] = $base<$ty>;
)*
}
};
}
macro_rules! fuzzy_mod {
($(mod $mod_name:ident ($sign:ident::$base:ident($start:literal..=$end:literal))),+$(,)?) => {
paste::paste! {
$(
mod $mod_name {
use super::*;
seq_macro::seq!(
N in $start..=$end {
define_aliases!($sign::$base(#(N,)*));
fuzzy!($base(#([< $sign:camel >]~N,)*));
}
);
}
)*
}
};
}
fuzzy_mod! {
mod buint_d8 (u::BUintD8(0..=64)),
mod buint_d16 (u::BUintD16(0..=64)),
mod buint_d32 (u::BUintD32(0..=64)),
mod buint(u::BUint(0..=64)),
mod bint_d8 (i::BIntD8(1..=64)),
mod bint_d16 (i::BIntD16(1..=64)),
mod bint_d32 (i::BIntD32(1..=64)),
mod bint(i::BInt(1..=64)),
}
}