extern crate alloc;
use alloc::vec::Vec;
use crate::error::Error;
pub fn encode_int(out: &mut Vec<u8>, value: usize, n: u32, flags: u8) {
debug_assert!((1..=8).contains(&n));
let max_prefix = (1usize << n) - 1;
if value < max_prefix {
out.push(flags | value as u8);
return;
}
out.push(flags | max_prefix as u8);
let mut v = value - max_prefix;
while v >= 128 {
out.push((v & 0x7f) as u8 | 0x80);
v >>= 7;
}
out.push(v as u8);
}
pub fn decode_int(buf: &[u8], pos: usize, n: u32) -> Result<(usize, usize), Error> {
debug_assert!((1..=8).contains(&n));
let max_prefix = (1usize << n) - 1;
let first = *buf.get(pos).ok_or(Error::UnexpectedEnd)? as usize;
let mut value = first & max_prefix;
let mut p = pos + 1;
if value < max_prefix {
return Ok((value, p));
}
let mut shift = 0u32;
loop {
let b = *buf.get(p).ok_or(Error::UnexpectedEnd)? as usize;
p += 1;
if shift >= usize::BITS {
return Err(Error::Corrupt);
}
let add = (b & 0x7f).checked_shl(shift).ok_or(Error::Corrupt)?;
value = value.checked_add(add).ok_or(Error::Corrupt)?;
if b & 0x80 == 0 {
break;
}
shift += 7;
}
Ok((value, p))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rfc_c1_examples() {
let mut out = Vec::new();
encode_int(&mut out, 10, 5, 0);
assert_eq!(out, [0x0a]);
assert_eq!(decode_int(&out, 0, 5).unwrap(), (10, 1));
let mut out = Vec::new();
encode_int(&mut out, 1337, 5, 0);
assert_eq!(out, [0x1f, 0x9a, 0x0a]);
assert_eq!(decode_int(&out, 0, 5).unwrap(), (1337, 3));
let mut out = Vec::new();
encode_int(&mut out, 42, 8, 0);
assert_eq!(out, [0x2a]);
assert_eq!(decode_int(&out, 0, 8).unwrap(), (42, 1));
}
#[test]
fn flags_preserved_and_ignored() {
let mut out = Vec::new();
encode_int(&mut out, 2, 6, 0b1100_0000);
assert_eq!(out, [0b1100_0010]);
assert_eq!(decode_int(&out, 0, 6).unwrap(), (2, 1));
}
#[test]
fn overlong_continuation_rejected() {
let mut buf = alloc::vec![0xffu8]; buf.extend(core::iter::repeat_n(0x80, 64));
buf.push(0x00);
assert!(matches!(decode_int(&buf, 0, 5), Err(Error::Corrupt)));
}
#[test]
fn truncated_continuation_rejected() {
let buf = [0x1fu8, 0x9a]; assert!(matches!(decode_int(&buf, 0, 5), Err(Error::UnexpectedEnd)));
}
}