#![allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum VarintError {
BufferTooShort,
Overflow,
TrailingBytes,
}
impl std::fmt::Display for VarintError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BufferTooShort => write!(f, "buffer too short for varint"),
Self::Overflow => write!(f, "varint overflows u64"),
Self::TrailingBytes => write!(f, "unexpected trailing bytes"),
}
}
}
pub fn encode_varint(mut value: u64, buf: &mut Vec<u8>) {
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
buf.push(byte);
if value == 0 {
break;
}
}
}
pub fn decode_varint(buf: &[u8]) -> Result<(u64, usize), VarintError> {
let mut result: u64 = 0;
let mut shift = 0u32;
for (i, &byte) in buf.iter().enumerate() {
if shift >= 64 {
return Err(VarintError::Overflow);
}
result |= ((byte & 0x7F) as u64) << shift;
if byte & 0x80 == 0 {
return Ok((result, i + 1));
}
shift += 7;
}
Err(VarintError::BufferTooShort)
}
pub fn encode_zigzag(value: i64, buf: &mut Vec<u8>) {
let zz = ((value << 1) ^ (value >> 63)) as u64;
encode_varint(zz, buf);
}
pub fn decode_zigzag(buf: &[u8]) -> Result<(i64, usize), VarintError> {
let (zz, n) = decode_varint(buf)?;
let value = ((zz >> 1) as i64) ^ (-((zz & 1) as i64));
Ok((value, n))
}
pub fn varint_size(mut value: u64) -> usize {
let mut n = 1;
loop {
value >>= 7;
if value == 0 {
break;
}
n += 1;
}
n
}
pub fn varint_roundtrip_ok(value: u64) -> bool {
let mut buf = vec![];
encode_varint(value, &mut buf);
decode_varint(&buf)
.map(|(v, _)| v == value)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_zero() {
let mut buf = vec![];
encode_varint(0, &mut buf);
assert_eq!(buf, &[0x00]);
}
#[test]
fn test_encode_one() {
let mut buf = vec![];
encode_varint(1, &mut buf);
assert_eq!(buf, &[0x01]);
}
#[test]
fn test_decode_single_byte() {
let (v, n) = decode_varint(&[0x05]).expect("should succeed");
assert_eq!(v, 5);
assert_eq!(n, 1);
}
#[test]
fn test_roundtrip_small() {
assert!(varint_roundtrip_ok(42));
}
#[test]
fn test_roundtrip_large() {
assert!(varint_roundtrip_ok(u64::MAX));
}
#[test]
fn test_varint_size_one_byte() {
assert_eq!(varint_size(127), 1);
}
#[test]
fn test_varint_size_two_bytes() {
assert_eq!(varint_size(128), 2);
}
#[test]
fn test_zigzag_positive() {
let mut buf = vec![];
encode_zigzag(100, &mut buf);
let (v, _) = decode_zigzag(&buf).expect("should succeed");
assert_eq!(v, 100);
}
#[test]
fn test_zigzag_negative() {
let mut buf = vec![];
encode_zigzag(-50, &mut buf);
let (v, _) = decode_zigzag(&buf).expect("should succeed");
assert_eq!(v, -50);
}
#[test]
fn test_buffer_too_short() {
assert_eq!(
decode_varint(&[0x80]).unwrap_err(),
VarintError::BufferTooShort
);
}
}