#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct VarInt(u64);
impl VarInt {
pub(crate) const MAX_VALUE: u64 = (1u64 << 62) - 1;
pub(crate) const MAX: VarInt = VarInt(Self::MAX_VALUE);
pub(crate) const fn new(v: u64) -> Option<VarInt> {
if v > Self::MAX_VALUE {
None
} else {
Some(VarInt(v))
}
}
pub(crate) const fn from_u32(v: u32) -> VarInt {
VarInt(v as u64)
}
#[allow(dead_code)]
pub(crate) const fn from_const(v: u64) -> VarInt {
assert!(
v <= Self::MAX_VALUE,
"varint literal exceeds the 62-bit value space"
);
VarInt(v)
}
pub(crate) const fn into_inner(self) -> u64 {
self.0
}
pub(crate) const fn encoded_len(self) -> usize {
if self.0 < (1 << 6) {
1
} else if self.0 < (1 << 14) {
2
} else if self.0 < (1 << 30) {
4
} else {
8
}
}
}
impl From<VarInt> for u64 {
fn from(v: VarInt) -> u64 {
v.0
}
}
impl From<u32> for VarInt {
fn from(v: u32) -> VarInt {
VarInt::from_u32(v)
}
}
pub(crate) fn encode(v: VarInt, out: &mut Vec<u8>) {
let mut buf = [0u8; 8];
let n = encode_to(v, &mut buf).expect("eight bytes always suffice");
out.extend_from_slice(&buf[..n]);
}
pub(crate) fn encode_to(v: VarInt, out: &mut [u8]) -> Option<usize> {
let n = v.encoded_len();
if out.len() < n {
return None;
}
let x = v.0;
match n {
1 => out[0] = x as u8,
2 => out[..2].copy_from_slice(&((x as u16) | 0x4000).to_be_bytes()),
4 => out[..4].copy_from_slice(&((x as u32) | 0x8000_0000).to_be_bytes()),
_ => out[..8].copy_from_slice(&(x | 0xc000_0000_0000_0000).to_be_bytes()),
}
Some(n)
}
pub(crate) fn decode(buf: &[u8]) -> Option<(VarInt, usize)> {
let first = *buf.first()?;
let n = 1usize << (first >> 6);
if buf.len() < n {
return None;
}
let mut v = u64::from(first & 0x3f);
for byte in &buf[1..n] {
v = (v << 8) | u64::from(*byte);
}
Some((VarInt(v), n))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rfc_9000_appendix_a1_vectors() {
let vectors: &[(&[u8], u64)] = &[
(
&[0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c],
151_288_809_941_952_652,
),
(&[0x9d, 0x7f, 0x3e, 0x7d], 494_878_333),
(&[0x7b, 0xbd], 15_293),
(&[0x25], 37),
];
for (bytes, value) in vectors {
assert_eq!(
decode(bytes),
Some((VarInt::new(*value).unwrap(), bytes.len())),
"decoding {bytes:02x?}"
);
let mut out = Vec::new();
encode(VarInt::new(*value).unwrap(), &mut out);
assert_eq!(out.as_slice(), *bytes, "encoding {value}");
}
assert_eq!(decode(&[0x40, 0x25]), Some((VarInt::new(37).unwrap(), 2)));
let mut out = Vec::new();
encode(VarInt::new(37).unwrap(), &mut out);
assert_eq!(out, vec![0x25]);
}
#[test]
fn boundaries_round_trip() {
let cases: &[(u64, usize, u8)] = &[
(0, 1, 0b00),
(63, 1, 0b00),
(64, 2, 0b01),
(16_383, 2, 0b01),
(16_384, 4, 0b10),
(1_073_741_823, 4, 0b10),
(1_073_741_824, 8, 0b11),
(VarInt::MAX_VALUE, 8, 0b11),
];
for (value, len, prefix) in cases {
let v = VarInt::new(*value).expect("in range");
assert_eq!(v.encoded_len(), *len, "encoded_len of {value}");
let mut out = Vec::new();
encode(v, &mut out);
assert_eq!(out.len(), *len, "encoded length of {value}");
assert_eq!(out[0] >> 6, *prefix, "prefix of {value}");
assert_eq!(decode(&out), Some((v, *len)), "round trip of {value}");
}
}
#[test]
fn above_max_is_rejected() {
assert_eq!(VarInt::new(1u64 << 62), None);
assert_eq!(VarInt::new(u64::MAX), None);
assert_eq!(VarInt::new(VarInt::MAX_VALUE), Some(VarInt::MAX));
assert_eq!(VarInt::MAX.into_inner(), VarInt::MAX_VALUE);
assert_eq!(VarInt::MAX_VALUE, (1u64 << 62) - 1);
}
#[test]
fn truncated_input_is_none() {
assert_eq!(decode(&[]), None);
assert_eq!(decode(&[0x40]), None);
assert_eq!(decode(&[0x80, 0x00, 0x00]), None);
assert_eq!(decode(&[0xc0, 0, 0, 0, 0, 0, 0]), None);
}
#[test]
fn decode_accepts_every_non_minimal_encoding_of_a_small_value() {
let thirty_seven: &[(&[u8], usize)] = &[
(&[0x25], 1),
(&[0x40, 0x25], 2),
(&[0x80, 0x00, 0x00, 0x25], 4),
(&[0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25], 8),
];
for (bytes, consumed) in thirty_seven {
assert_eq!(
decode(bytes),
Some((VarInt::new(37).unwrap(), *consumed)),
"decoding {bytes:02x?}"
);
}
}
#[test]
fn exhaustive_round_trip_near_boundaries() {
let mut values: Vec<u64> = (0..=1024).collect();
for boundary in [
63u64,
64,
16_383,
16_384,
1_073_741_823,
1_073_741_824,
VarInt::MAX_VALUE,
] {
for delta in 0..=4u64 {
values.push(boundary.saturating_sub(delta));
if let Some(v) = boundary.checked_add(delta)
&& v <= VarInt::MAX_VALUE
{
values.push(v);
}
}
}
for value in values {
let v = VarInt::new(value).expect("in range");
let mut out = Vec::new();
encode(v, &mut out);
assert_eq!(out.len(), v.encoded_len(), "length of {value}");
assert_eq!(decode(&out), Some((v, out.len())), "round trip of {value}");
}
}
#[test]
fn encode_to_respects_a_short_buffer() {
let cases: &[(u64, usize)] = &[
(37, 1),
(16_383, 2),
(1_073_741_823, 4),
(VarInt::MAX_VALUE, 8),
];
for (value, len) in cases {
let v = VarInt::new(*value).expect("in range");
let mut out = vec![0xaa; len - 1];
assert_eq!(encode_to(v, &mut out), None, "short buffer for {value}");
assert!(
out.iter().all(|b| *b == 0xaa),
"a failed encode_to wrote into the buffer for {value}"
);
let mut exact = vec![0xaa; *len];
assert_eq!(encode_to(v, &mut exact), Some(*len));
assert_eq!(decode(&exact), Some((v, *len)));
}
let mut empty: [u8; 0] = [];
assert_eq!(encode_to(VarInt::new(0).unwrap(), &mut empty), None);
}
#[test]
fn conversions_are_consistent() {
assert_eq!(u64::from(VarInt::from_u32(u32::MAX)), u64::from(u32::MAX));
assert_eq!(VarInt::from(7u32), VarInt::new(7).unwrap());
assert_eq!(VarInt::from_const(42).into_inner(), 42);
assert_eq!(VarInt::default(), VarInt::new(0).unwrap());
const LIMIT: VarInt = VarInt::from_const(1_048_576);
assert_eq!(LIMIT.into_inner(), 1_048_576);
}
}