use super::{
scalar_varint::{
read_scalar_varint, read_u256_varint, scalar_varint_size, u256_varint_size,
write_scalar_varint, write_u256_varint,
},
U256,
};
use crate::base::scalar::MontScalar;
#[cfg(test)]
use alloc::{vec, vec::Vec};
use ark_ff::MontConfig;
pub const MSB: u8 = 0b1000_0000;
const DROP_MSB: u8 = 0b0111_1111;
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;
#[cfg(test)]
fn encode_var_vec(self) -> Vec<u8> {
let mut v = vec![0; self.required_space()];
self.encode_var(&mut v);
v
}
}
#[expect(clippy::cast_sign_loss)]
#[inline]
fn zigzag_encode(from: i64) -> u64 {
((from << 1) ^ (from >> 63)) as u64
}
#[expect(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
#[inline]
fn zigzag_decode(from: u64) -> i64 {
((from >> 1) ^ (-((from & 1) as i64)) as u64) as i64
}
macro_rules! impl_varint {
($t:ty, unsigned) => {
impl VarInt for $t {
#[allow(clippy::cast_lossless, clippy::allow_attributes, reason = "In a macro different instances can differ hence allow can not be replaced with expect")]
fn required_space(self) -> usize {
(self as u64).required_space()
}
#[expect(clippy::cast_possible_truncation)]
#[allow(clippy::cast_lossless, clippy::allow_attributes, reason = "In a macro different instances can differ hence allow can not be replaced with expect")]
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let (n, s) = u64::decode_var(src)?;
if n > (Self::MAX as u64) {
None
} else {
Some((n as Self, s))
}
}
#[allow(clippy::cast_lossless, clippy::allow_attributes, reason = "In a macro different instances can differ hence allow can not be replaced with expect")]
fn encode_var(self, dst: &mut [u8]) -> usize {
(self as u64).encode_var(dst)
}
}
};
($t:ty, signed) => {
impl VarInt for $t {
#[allow(clippy::cast_lossless, clippy::allow_attributes, reason = "In a macro different instances can differ hence allow can not be replaced with expect")]
fn required_space(self) -> usize {
(self as i64).required_space()
}
#[expect(clippy::cast_possible_truncation)]
#[allow(clippy::cast_lossless, clippy::allow_attributes, reason = "In a macro different instances can differ hence allow can not be replaced with expect")]
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let (n, s) = i64::decode_var(src)?;
if n > (Self::MAX as i64) || n < (Self::MIN as i64) {
None
} else {
Some((n as Self, s))
}
}
#[allow(clippy::cast_lossless, clippy::allow_attributes, reason = "In a macro different instances can differ hence allow can not be replaced with expect")]
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 bool {
fn required_space(self) -> usize {
u64::from(self).required_space()
}
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let (n, s) = u64::decode_var(src)?;
match n {
0 => Some((false, s)),
1 => Some((true, s)),
_ => None,
}
}
fn encode_var(self, dst: &mut [u8]) -> usize {
u64::from(self).encode_var(dst)
}
}
impl VarInt for u64 {
fn required_space(self) -> usize {
let bits = 64 - self.leading_zeros() as usize;
core::cmp::max(1, bits.div_ceil(7))
}
#[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
}
}
#[expect(clippy::cast_possible_truncation)]
#[inline]
fn encode_var(self, dst: &mut [u8]) -> usize {
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 {
zigzag_encode(self).required_space()
}
#[inline]
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
let (result, size) = u64::decode_var(src)?;
Some((zigzag_decode(result), size))
}
#[inline]
fn encode_var(self, dst: &mut [u8]) -> usize {
zigzag_encode(self).encode_var(dst)
}
}
impl VarInt for U256 {
fn required_space(self) -> usize {
u256_varint_size(self)
}
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
read_u256_varint(src)
}
fn encode_var(self, dst: &mut [u8]) -> usize {
write_u256_varint(dst, self)
}
}
impl VarInt for u128 {
fn required_space(self) -> usize {
U256 { low: self, high: 0 }.required_space()
}
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
match U256::decode_var(src)? {
(U256 { high: 0, low }, s) => Some((low, s)),
_ => None,
}
}
fn encode_var(self, dst: &mut [u8]) -> usize {
U256 { low: self, high: 0 }.encode_var(dst)
}
}
#[expect(clippy::cast_sign_loss)]
#[inline]
fn zigzag_encode_i128(from: i128) -> u128 {
((from << 1) ^ (from >> 127)) as u128
}
#[expect(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
#[inline]
fn zigzag_decode_i128(from: u128) -> i128 {
((from >> 1) ^ (-((from & 1) as i128)) as u128) as i128
}
impl VarInt for i128 {
fn required_space(self) -> usize {
u128::required_space(zigzag_encode_i128(self))
}
#[inline]
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
u128::decode_var(src).map(|(v, s)| (zigzag_decode_i128(v), s))
}
#[inline]
fn encode_var(self, dst: &mut [u8]) -> usize {
zigzag_encode_i128(self).encode_var(dst)
}
}
impl<T: MontConfig<4>> VarInt for MontScalar<T> {
fn required_space(self) -> usize {
scalar_varint_size(&self)
}
fn decode_var(src: &[u8]) -> Option<(Self, usize)> {
read_scalar_varint(src)
}
fn encode_var(self, dst: &mut [u8]) -> usize {
write_scalar_varint(dst, &self)
}
}