#[inline]
pub fn encode_prefix_integer(
out: &mut Vec<u8>,
value: u64,
prefix_bits: u8,
prefix_pattern: u8,
) {
if prefix_bits == 0 || prefix_bits > 8 {
return;
}
let mask = (1u64 << prefix_bits) - 1;
if value < mask {
out.push(prefix_pattern | (value as u8));
return;
}
out.push(prefix_pattern | mask as u8);
let mut v = value - mask;
while v >= 0x80 {
out.push(((v as u8) & 0x7F) | 0x80);
v >>= 7;
}
out.push(v as u8);
}
#[inline]
pub fn decode_prefix_integer(data: &[u8], prefix_bits: u8) -> Option<(u64, usize)> {
if data.is_empty() || prefix_bits == 0 || prefix_bits > 8 {
return None;
}
let mask = ((1u16 << prefix_bits as u16) - 1) as u8;
let first = data[0] & mask;
let mut value = first as u64;
if first < mask {
return Some((value, 1));
}
let mut m: u32 = 0;
let mut i: usize = 1;
loop {
if m > 56 {
return None;
}
let Some(&b) = data.get(i) else {
return None; };
i += 1;
let shift = 1u64.checked_shl(m)?;
let contribution = ((b & 0x7F) as u64).checked_mul(shift)?;
value = value.checked_add(contribution)?;
m = m.checked_add(7)?;
if b & 0x80 == 0 {
break;
}
}
Some((value, i))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_small_single_byte() {
assert_eq!(decode_prefix_integer(&[0x0A], 5), Some((10, 1)));
assert_eq!(decode_prefix_integer(&[0x0A], 8), Some((10, 1)));
}
#[test]
fn decode_max_prefix() {
assert_eq!(decode_prefix_integer(&[0x2A], 8), Some((42, 1)));
}
#[test]
fn decode_multi_byte() {
let data = [0x1F, 0x9A, 0x0A];
assert_eq!(decode_prefix_integer(&data, 5), Some((1337, 3)));
}
#[test]
fn decode_empty_rejected() {
assert_eq!(decode_prefix_integer(&[], 5), None);
assert_eq!(decode_prefix_integer(&[0x00], 0), None);
assert_eq!(decode_prefix_integer(&[0x00], 9), None);
}
#[test]
fn decode_overflow_rejected() {
let data = [0x1F, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01];
assert_eq!(decode_prefix_integer(&data, 5), None);
}
#[test]
fn decode_never_terminating_rejected() {
assert_eq!(decode_prefix_integer(&[0xFF; 16], 7), None);
}
#[test]
fn decode_truncated_rejected() {
assert_eq!(decode_prefix_integer(&[0xFF], 7), None);
}
#[test]
fn encode_decode_roundtrip() {
let cases: &[(u64, u8, u8)] = &[
(5, 7, 0x00),
(10, 5, 0x00),
(127, 7, 0x00),
(128, 7, 0x00),
(1337, 7, 0x00),
(65535, 7, 0x00),
(u32::MAX as u64, 7, 0x00),
(i64::MAX as u64, 7, 0x00),
(42, 8, 0x00),
(1, 4, 0x10),
(2, 6, 0x40),
(3, 6, 0x80),
];
for &(value, prefix_bits, pattern) in cases {
let mut out = Vec::new();
encode_prefix_integer(&mut out, value, prefix_bits, pattern);
let (decoded, consumed) = decode_prefix_integer(&out, prefix_bits).unwrap();
assert_eq!(decoded, value, "value {value} (prefix {prefix_bits})");
assert_eq!(consumed, out.len());
}
}
#[test]
fn encode_preserves_prefix_pattern() {
let mut out = Vec::new();
encode_prefix_integer(&mut out, 5, 7, 0x80);
assert_eq!(out, vec![0x85]); let mut out = Vec::new();
encode_prefix_integer(&mut out, 1, 6, 0x40);
assert_eq!(out, vec![0x41]); }
}