#[inline(always)]
pub fn encode_varint64(value: i64, buf: &mut [u8]) -> usize {
let mut val = ((value << 1) ^ (value >> 63)) as u64;
val += 1;
let mut i = 0;
while val > 0x7F {
buf[i] = ((val & 0x7F) | 0x80) as u8;
val >>= 7;
i += 1;
}
buf[i] = val as u8;
i + 1
}
#[inline(always)]
pub fn decode_varint(buf: &[u8]) -> crate::Result<(i64, usize)> {
use crate::Error;
if buf.is_empty() {
return Err(Error::Truncated("varint: empty input".into()));
}
let b0 = buf[0];
if b0 & 0x80 == 0 {
if b0 == 0 {
return Err(Error::Truncated(
"varint: NaN sentinel in non-NaN context".into(),
));
}
let uval = (b0 as u64) - 1;
let val = ((uval >> 1) as i64) ^ -((uval & 1) as i64);
return Ok((val, 1));
}
let mut uval: u64 = (b0 & 0x7f) as u64;
let mut shift: u32 = 7;
let mut i = 1usize;
loop {
if i >= buf.len() {
return Err(Error::Truncated("varint: truncated".into()));
}
let byte = buf[i];
i += 1;
if shift >= 63 && (byte & 0x7f) > 1 {
return Err(Error::Truncated("varint: value overflow".into()));
}
uval |= ((byte & 0x7f) as u64) << shift;
shift += 7;
if (byte & 0x80) == 0 {
break;
}
}
if uval == 0 {
return Err(Error::Truncated(
"varint: NaN sentinel in non-NaN context".into(),
));
}
uval -= 1;
let val = ((uval >> 1) as i64) ^ -((uval & 1) as i64);
Ok((val, i))
}
#[cfg(test)]
mod tests {
use super::*;
fn roundtrip(value: i64) {
let mut buf = [0u8; 10];
let n = encode_varint64(value, &mut buf);
let (decoded, consumed) = decode_varint(&buf[..n]).unwrap();
assert_eq!(decoded, value, "roundtrip failed for {}", value);
assert_eq!(consumed, n);
}
#[test]
fn test_varint_roundtrip() {
for v in [
0i64,
1,
-1,
2,
-2,
127,
-127,
128,
-128,
1000,
-1000,
i32::MAX as i64,
i32::MIN as i64,
i64::MAX / 2,
i64::MIN / 2,
] {
roundtrip(v);
}
}
#[test]
fn test_varint_known_values() {
let cases: &[(i64, &[u8])] = &[
(0, &[0x01]),
(1, &[0x03]),
(-1, &[0x02]),
(127, &[0xff, 0x01]),
(-127, &[0xfe, 0x01]),
(1000, &[0xd1, 0x0f]),
(-1000, &[0xd0, 0x0f]),
(100000, &[0xc1, 0x9a, 0x0c]),
(-100000, &[0xc0, 0x9a, 0x0c]),
];
for &(value, expected) in cases {
let mut buf = [0u8; 10];
let n = encode_varint64(value, &mut buf);
assert_eq!(
&buf[..n],
expected,
"varint({value}) mismatch: got {:02x?}, expected {:02x?}",
&buf[..n],
expected
);
}
}
#[test]
fn test_varint_nan_marker() {
let buf = [0u8; 1];
assert!(decode_varint(&buf).is_err());
}
}