use alloc::{vec::Vec, vec};
#[cfg(feature = "std")]
use std::io;
use super::Error;
const VARINT_VALUE_MASK: u8 = (1 << 7) - 1;
pub(super) fn encode_varint(mut value: usize) -> Vec<u8> {
if value == 0 {
return vec![0];
}
let mut result = vec![];
while value != 0 {
let mut next_byte = u8::try_from(value & usize::from(VARINT_VALUE_MASK)).unwrap();
value >>= 7;
next_byte |= u8::from(value != 0) << 7;
result.push(next_byte);
}
result
}
pub(super) fn decode_varint(mut reader: impl io::Read) -> Result<usize, Error> {
let mut result = 0;
let mut i = 0;
while i < usize::BITS {
let mut next_byte = [0xff];
reader.read_exact(&mut next_byte).map_err(|_| Error::UnexpectedEof)?;
let next_byte = next_byte[0];
let to_shift = next_byte & VARINT_VALUE_MASK;
{
let bits_remaining = usize::BITS - i;
if let Some(expected_not_set_bits) = u8::BITS.checked_sub(bits_remaining) {
if to_shift.leading_zeros() < expected_not_set_bits {
Err(Error::Overflow)?;
}
}
}
result |= usize::from(to_shift) << i;
if (next_byte >> 7) == 0 {
if (next_byte == 0) && (i != 0) {
Err(Error::NonCanonical)?;
}
return Ok(result);
}
i += 7;
}
Err(Error::Overflow)
}
#[test]
fn varint() {
use rand::Rng as _;
let mut rng = rand::rand_core::UnwrapErr(rand::rngs::SysRng);
let test = |value| {
let encoding = encode_varint(value);
{
let mut encoding = encoding.as_slice();
assert_eq!(decode_varint(&mut encoding).unwrap(), value);
assert!(encoding.is_empty());
}
encoding
};
assert_eq!(test(0), vec![0]);
assert_eq!(test(1), vec![1]);
assert_eq!(test(usize::from(u8::MAX >> 1)), vec![u8::MAX >> 1]);
assert_eq!(test(usize::from((u8::MAX >> 1) + 1)), vec![(1 << 7), 1]);
for _ in 0 .. 256 {
#[expect(clippy::as_conversions, clippy::cast_possible_truncation)]
test(rng.next_u64() as usize);
}
}