use crate::buf::{ReadBuf, WriteBuf};
use crate::error::{Error, Result};
pub const MAX_LEN_U16: usize = 3;
pub const MAX_LEN_U32: usize = 5;
pub const MAX_LEN_U64: usize = 10;
#[inline]
#[allow(unknown_lints, clippy::manual_div_ceil)]
pub const fn encoded_len_u32(value: u32) -> usize {
let bits = (u32::BITS - value.leading_zeros()) as usize;
if bits == 0 {
1
} else {
(bits + 6) / 7
}
}
#[inline]
#[allow(unknown_lints, clippy::manual_div_ceil)]
pub const fn encoded_len_u64(value: u64) -> usize {
let bits = (u64::BITS - value.leading_zeros()) as usize;
if bits == 0 {
1
} else {
(bits + 6) / 7
}
}
#[inline]
pub fn encode_u32(mut value: u32, buf: &mut WriteBuf<'_>) -> Result<()> {
while value >= 0x80 {
buf.write_u8(((value & 0x7F) as u8) | 0x80)?;
value >>= 7;
}
buf.write_u8(value as u8)
}
#[inline]
pub fn encode_u64(mut value: u64, buf: &mut WriteBuf<'_>) -> Result<()> {
while value >= 0x80 {
buf.write_u8(((value & 0x7F) as u8) | 0x80)?;
value >>= 7;
}
buf.write_u8(value as u8)
}
#[inline]
pub fn decode_u32(buf: &mut ReadBuf<'_>) -> Result<u32> {
let mut value: u64 = 0;
let mut shift: u32 = 0;
for i in 0..MAX_LEN_U32 {
let byte = buf.read_u8()?;
value |= u64::from(byte & 0x7F) << shift;
if byte & 0x80 == 0 {
if value > u64::from(u32::MAX) {
return Err(Error::VarintOverflow);
}
if i == MAX_LEN_U32 - 1 && byte > 0x0F {
return Err(Error::VarintOverflow);
}
return Ok(value as u32);
}
shift += 7;
}
Err(Error::VarintOverflow)
}
#[inline]
pub fn decode_u64(buf: &mut ReadBuf<'_>) -> Result<u64> {
let mut value: u64 = 0;
let mut shift: u32 = 0;
for i in 0..MAX_LEN_U64 {
let byte = buf.read_u8()?;
if i == MAX_LEN_U64 - 1 {
if byte & 0xFE != 0 {
return Err(Error::VarintOverflow);
}
value |= u64::from(byte & 0x01) << shift;
return Ok(value);
}
value |= u64::from(byte & 0x7F) << shift;
if byte & 0x80 == 0 {
return Ok(value);
}
shift += 7;
}
Err(Error::VarintOverflow)
}
#[cfg(test)]
mod tests {
use super::*;
fn round_trip_u32(value: u32) {
let mut storage = [0u8; MAX_LEN_U32];
let mut w = WriteBuf::new(&mut storage);
encode_u32(value, &mut w).unwrap();
let n = w.position();
assert_eq!(n, encoded_len_u32(value));
let mut r = ReadBuf::new(&storage[..n]);
assert_eq!(decode_u32(&mut r).unwrap(), value);
assert!(r.is_empty());
}
fn round_trip_u64(value: u64) {
let mut storage = [0u8; MAX_LEN_U64];
let mut w = WriteBuf::new(&mut storage);
encode_u64(value, &mut w).unwrap();
let n = w.position();
assert_eq!(n, encoded_len_u64(value));
let mut r = ReadBuf::new(&storage[..n]);
assert_eq!(decode_u64(&mut r).unwrap(), value);
assert!(r.is_empty());
}
#[test]
fn u32_boundaries() {
for &v in &[
0u32,
1,
127,
128,
16_383,
16_384,
2_097_151,
2_097_152,
u32::MAX,
] {
round_trip_u32(v);
}
}
#[test]
fn u64_boundaries() {
for &v in &[0u64, 127, 128, 1 << 35, 1 << 56, u64::MAX] {
round_trip_u64(v);
}
}
#[test]
fn decode_u32_overflow_on_sixth_byte() {
let mut buf = ReadBuf::new(&[0x80, 0x80, 0x80, 0x80, 0x80, 0x01]);
assert_eq!(decode_u32(&mut buf), Err(Error::VarintOverflow));
}
#[test]
fn decode_u32_overflow_in_final_byte() {
let mut buf = ReadBuf::new(&[0xFF, 0xFF, 0xFF, 0xFF, 0x10]);
assert_eq!(decode_u32(&mut buf), Err(Error::VarintOverflow));
}
#[test]
fn decode_truncated() {
let mut buf = ReadBuf::new(&[0x80]);
assert_eq!(decode_u32(&mut buf), Err(Error::UnexpectedEof));
}
}