const MAX_VARINT_LEN64: usize = 10;
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[inline]
pub fn append_uvarint(mut buf: alloc::vec::Vec<u8>, x: impl Unsigned) -> alloc::vec::Vec<u8> {
let mut x = x.to_u64();
while x >= 0x80 {
buf.push((x as u8) | 0x80);
x >>= 7;
}
buf.push(x as u8);
buf
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub fn write_uvarint<W: std::io::Write>(
mut buf: W,
x: impl Unsigned,
) -> Result<usize, std::io::Error> {
let mut x = x.to_u64();
let mut n = 0;
while x >= 0x80 {
n += buf.write(&[(x as u8) | 0x80])?;
x >>= 7;
}
buf.write(&[x as u8]).map(|nn| {
n += nn;
n
})
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub fn read_uvarint<R: std::io::Read, I: Unsigned>(mut r: R) -> std::io::Result<(I, usize)> {
let mut x = 0;
let mut s = 0;
for i in 0..MAX_VARINT_LEN64 {
let mut b = [0; 1];
r.read_exact(&mut b)?;
if b[0] < 0x80 {
if i == MAX_VARINT_LEN64 - 1 && b[0] > 1 {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
Error::Overflow,
));
}
return Ok((I::from_u64(x | ((b[0] as u64) << s)), i));
}
x |= ((b[0] & 0x7f) as u64) << s;
s += 7;
}
Err(std::io::Error::new(
std::io::ErrorKind::Other,
Error::Overflow,
))
}
#[inline]
pub fn put_uvarint(buf: &mut [u8], x: impl Unsigned) -> usize {
let mut i = 0;
let mut x = x.to_u64();
while x >= 0x80 {
buf[i] = (x as u8) | 0x80;
x >>= 7;
i += 1;
}
buf[i] = x as u8;
i + 1
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Error {
SmallBuffer,
Overflow,
}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::SmallBuffer => write!(f, "binary: the buffer is too small"),
Self::Overflow => write!(f, "binary: varint overflows a 64-bit integer"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[inline]
pub fn uvarint<R: Unsigned>(buf: &[u8]) -> Result<(R, usize), Error> {
let (mut x, mut s) = (0, 0);
for (i, b) in buf.iter().enumerate() {
if i == MAX_VARINT_LEN64 {
return Err(Error::Overflow);
}
let b = *b;
if b < 0x80 {
if i == MAX_VARINT_LEN64 - 1 && b > 1 {
return Err(Error::Overflow);
}
return Ok((R::from_u64(x | (b as u64) << s), i + 1));
}
x |= ((b & 0x7f) as u64) << s;
s += 7;
}
Err(Error::SmallBuffer)
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[inline]
pub fn append_varint(buf: alloc::vec::Vec<u8>, x: impl Signed) -> alloc::vec::Vec<u8> {
let x = x.to_i64();
let mut ux = (x as u64) << 1;
if x < 0 {
ux = !ux;
}
append_uvarint(buf, ux)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub fn write_varint<W: std::io::Write>(buf: W, x: impl Signed) -> std::io::Result<usize> {
let x = x.to_i64();
let mut ux = (x as u64) << 1;
if x < 0 {
ux = !ux;
}
write_uvarint(buf, ux)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[inline]
pub fn read_varint<R: std::io::Read, I: Signed>(r: R) -> std::io::Result<(I, usize)> {
let (ux, readed) = read_uvarint::<R, u64>(r)?;
let mut x = (ux >> 1) as i64;
if ux & 1 != 0 {
x = !x;
}
Ok((I::from_i64(x), readed))
}
#[inline]
pub fn put_varint(buf: &mut [u8], x: impl Signed) -> usize {
let x = x.to_i64();
let mut ux = (x as u64) << 1;
if x < 0 {
ux = !ux;
}
put_uvarint(buf, ux)
}
#[inline]
pub fn varint<R: Signed>(buf: &[u8]) -> Result<(R, usize), Error> {
let (ux, n) = uvarint::<u64>(buf)?;
let mut x = (ux >> 1) as i64;
if ux & 1 != 0 {
x = !x;
}
Ok((R::from_i64(x), n))
}
macro_rules! impl_ {
($trait: ident::<$ret:ident>::$fn: ident::$from_fn: ident { $($x:ident),+ $(,)? }) => {
$(
impl $trait for $x {
fn $fn(&self) -> $ret {
*self as $ret
}
fn $from_fn(val: $ret) -> Self {
val as Self
}
}
)*
};
}
pub trait Unsigned {
fn to_u64(&self) -> u64;
fn from_u64(val: u64) -> Self;
}
impl_! {
Unsigned::<u64>::to_u64::from_u64 {
u8,
u16,
u32,
usize,
u64,
u128,
}
}
pub trait Signed {
fn to_i64(&self) -> i64;
fn from_i64(val: i64) -> Self;
}
impl_! {
Signed::<i64>::to_i64::from_i64 {
i8,
i16,
i32,
isize,
i64,
i128,
}
}
#[cfg(test)]
mod tests {
use super::*;
const MAX_VARINT_LEN16: usize = 3;
const MAX_VARINT_LEN32: usize = 5;
fn test_constant(w: u64, max: usize) {
let mut buf = vec![0; MAX_VARINT_LEN64];
let n = put_uvarint(&mut buf, 1u64 << (w - 1));
assert_eq!(n, max);
}
#[test]
fn test_constants() {
test_constant(16, MAX_VARINT_LEN16);
test_constant(32, MAX_VARINT_LEN32);
test_constant(64, MAX_VARINT_LEN64);
}
}