use alloc::vec::Vec;
use crate::tls::Error;
pub(crate) const MAX: u64 = (1u64 << 62) - 1;
pub(crate) const fn encoded_len(value: u64) -> usize {
if value < 1 << 6 {
1
} else if value < 1 << 14 {
2
} else if value < 1 << 30 {
4
} else {
8
}
}
pub(crate) fn encode(value: u64, out: &mut Vec<u8>) {
assert!(value <= MAX, "QUIC varint value out of range: {value:#x}");
if value < 1 << 6 {
out.push(value as u8);
} else if value < 1 << 14 {
let v = value as u16;
let bytes = v.to_be_bytes();
out.push(bytes[0] | 0x40);
out.push(bytes[1]);
} else if value < 1 << 30 {
let v = value as u32;
let bytes = v.to_be_bytes();
out.push(bytes[0] | 0x80);
out.push(bytes[1]);
out.push(bytes[2]);
out.push(bytes[3]);
} else {
let bytes = value.to_be_bytes();
out.push(bytes[0] | 0xC0);
out.extend_from_slice(&bytes[1..]);
}
}
pub(crate) fn decode(buf: &[u8]) -> Result<(u64, usize), Error> {
if buf.is_empty() {
return Err(Error::Decode);
}
let tag = buf[0] >> 6;
let len = 1usize << tag;
if buf.len() < len {
return Err(Error::Decode);
}
let mut value = (buf[0] & 0x3F) as u64;
for &b in &buf[1..len] {
value = (value << 8) | b as u64;
}
Ok((value, len))
}
#[cfg(test)]
mod tests {
use super::*;
const BOUNDARIES: &[(u64, usize)] = &[
(0, 1),
(63, 1),
(64, 2),
(16383, 2),
(16384, 4),
(1073741823, 4),
(1073741824, 8),
((1u64 << 62) - 1, 8),
];
#[test]
fn roundtrip_boundaries() {
for &(v, expected_len) in BOUNDARIES {
let mut buf = Vec::new();
encode(v, &mut buf);
assert_eq!(buf.len(), expected_len, "len({v}) wrong");
let (decoded, used) = decode(&buf).expect("decode");
assert_eq!(decoded, v);
assert_eq!(used, expected_len);
}
}
#[test]
fn accepts_oversized_legal_encodings() {
let (v, used) = decode(&[0x40, 0x00]).expect("decode");
assert_eq!(v, 0);
assert_eq!(used, 2);
let (v, used) = decode(&[0x80, 0x00, 0x00, 0x00]).expect("decode");
assert_eq!(v, 0);
assert_eq!(used, 4);
let (v, used) = decode(&[0xC0, 0, 0, 0, 0, 0, 0, 0]).expect("decode");
assert_eq!(v, 0);
assert_eq!(used, 8);
}
#[test]
fn rejects_truncated_buffer() {
assert!(matches!(decode(&[]), Err(Error::Decode)));
assert!(matches!(decode(&[0x40]), Err(Error::Decode)));
assert!(matches!(decode(&[0x80, 0x00, 0x00]), Err(Error::Decode)));
assert!(matches!(
decode(&[0xC0, 0, 0, 0, 0, 0, 0]),
Err(Error::Decode)
));
}
#[test]
#[should_panic]
fn panics_on_oversize() {
let mut buf = Vec::new();
encode(1u64 << 62, &mut buf);
}
#[test]
fn encoded_len_matches_actual() {
for &(v, _) in BOUNDARIES {
let mut buf = Vec::new();
encode(v, &mut buf);
assert_eq!(encoded_len(v), buf.len(), "encoded_len({v}) mismatch");
}
}
}