use bytes::{Buf, BufMut};
use crate::{decoder, encoder};
pub const VU29_MAX_1BYTE: u32 = (1 << 7) - 1;
pub const VU29_MIN_2BYTE: u32 = VU29_MAX_1BYTE + 1;
pub const VU29_MAX_2BYTE: u32 = (1 << 14) - 1;
pub const VU29_MIN_3BYTE: u32 = VU29_MAX_2BYTE + 1;
pub const VU29_MAX_3BYTE: u32 = (1 << 21) - 1;
pub const VU29_MIN_4BYTE: u32 = VU29_MAX_3BYTE + 1;
pub const VU29_MAX_4BYTE: u32 = (1 << 29) - 1;
pub fn encode_vu29(buf: &mut impl BufMut, val: u32) -> Result<(), encoder::EncError> {
match val {
0..=VU29_MAX_1BYTE => {
buf.put_u8(val as u8);
Ok(())
}
VU29_MIN_2BYTE..=VU29_MAX_2BYTE => {
let bytes = [
(val & 0b0011_1111) as u8 | 0b1000_0000,
((val >> 6) & 0xFF) as u8,
];
buf.put_slice(&bytes[..]);
Ok(())
}
VU29_MIN_3BYTE..=VU29_MAX_3BYTE => {
let bytes = [
(val & 0b0001_1111) as u8 | 0b1100_0000,
((val >> 5) & 0xFF) as u8,
((val >> 13) & 0xFF) as u8,
];
buf.put_slice(&bytes[..]);
Ok(())
}
VU29_MIN_4BYTE..=VU29_MAX_4BYTE => {
let bytes = [
(val & 0b0001_1111) as u8 | 0b1110_0000,
((val >> 5) & 0xFF) as u8,
((val >> 13) & 0xFF) as u8,
((val >> 21) & 0xFF) as u8,
];
buf.put_slice(&bytes[..]);
Ok(())
}
_ => Err(encoder::EncError::VarUintOverflow),
}
}
pub fn encode_vu29_with_backing(
backing: &mut [u8; 4],
val: u32,
) -> Result<&[u8], encoder::EncError> {
let mut cursor = &mut backing[..];
encode_vu29(&mut cursor, val)?;
let vu29_length = 4 - cursor.len();
Ok(&backing[0..vu29_length])
}
pub fn decode_vu29(buf: &mut impl Buf) -> Result<u32, decoder::DecError> {
let first_byte = buf.try_get_u8()?;
Ok(match first_byte {
0b0000_0000..=0b0111_1111 => first_byte as u32,
0b1000_0000..=0b1011_1111 => {
let supplemental = buf.try_get_u8()?;
let out = ((supplemental as u32) << 6) | (first_byte as u32 & 0b0011_1111);
if out <= VU29_MAX_1BYTE {
return Err(decoder::DecError::VarUintNotCanonical);
}
out
}
0b1100_0000..=0b1101_1111 => {
let supplemental = buf.try_get_u16_le()?;
let out = ((supplemental as u32) << 5) | (first_byte as u32 & 0b0001_1111);
if out <= VU29_MAX_2BYTE {
return Err(decoder::DecError::VarUintNotCanonical);
}
out
}
0b1110_0000..=0b1111_1111 => {
let supplemental1 = buf.try_get_u16_le()?;
let supplemental2 = buf.try_get_u8()?;
let out = ((supplemental1 as u32) << 5)
| ((supplemental2 as u32) << 21)
| (first_byte as u32 & 0b0001_1111);
if out <= VU29_MAX_3BYTE {
return Err(decoder::DecError::VarUintNotCanonical);
}
out
}
})
}
#[cfg(test)]
mod test {
use alloc::vec::Vec;
use crate::vu29::{decode_vu29, encode_vu29, VU29_MAX_3BYTE, VU29_MAX_4BYTE};
#[test]
fn test_encode_vu29() {
#[track_caller]
fn expect(bytes: &[u8], decoded: u32) {
let mut target: Vec<u8> = Vec::new();
encode_vu29(&mut target, decoded).unwrap();
assert_eq!(bytes, &target[..]);
}
#[track_caller]
fn expect_error(decoded: u32) {
let mut target: Vec<u8> = Vec::new();
encode_vu29(&mut target, decoded).unwrap_err();
}
expect(&[0], 0);
expect(&[42], 42);
expect(&[0b0100_0000], 64);
expect(&[0b0111_1111], 127);
expect(&[0b1000_0000, 0b0000_0010], 128);
expect(&[0b1000_0000, 0b1000_0000], 8192);
expect(&[0b1101_1111, 0b1111_1111, 0b1111_1111], VU29_MAX_3BYTE);
expect(
&[0b1110_0000, 0b0000_0000, 0b0000_0000, 0b0000_0001],
VU29_MAX_3BYTE + 1,
);
expect(
&[0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111],
VU29_MAX_4BYTE,
);
expect_error(1 << 29);
}
#[test]
fn test_decode_vu29() {
#[track_caller]
fn expect(mut bytes: &[u8], decoded: u32) {
assert_eq!(decode_vu29(&mut bytes).unwrap(), decoded);
assert!(bytes.is_empty());
}
#[track_caller]
fn expect_error(mut bytes: &[u8]) {
decode_vu29(&mut bytes).unwrap_err();
}
expect(&[0], 0);
expect(&[42], 42);
expect(&[0b0100_0000], 64);
expect(&[0b0111_1111], 127);
expect_error(&[0b1000_0000]);
expect_error(&[0b1000_0000, 0b0000_0000]);
expect_error(&[0b1000_0000, 0b0000_0001]);
expect(&[0b1000_0000, 0b0000_0010], 128);
expect(&[0b1000_0000, 0b1000_0000], 8192);
expect_error(&[0b1100_0000]);
expect_error(&[0b1100_0000, 0b0000_0001]);
expect_error(&[0b1100_0000, 0b0000_0001, 0b0000_0000]); expect_error(&[0b1100_0000, 0b0000_0000, 0b0000_0001]);
expect(&[0b1101_1111, 0b1111_1111, 0b1111_1111], VU29_MAX_3BYTE);
expect_error(&[0b1110_0000, 0b0000_0001, 0b0000_0000, 0b0000_0000]);
expect(
&[0b1110_0000, 0b0000_0000, 0b0000_0000, 0b0000_0001],
VU29_MAX_3BYTE + 1,
);
expect(
&[0b1111_1111, 0b1111_1111, 0b1111_1111, 0b1111_1111],
VU29_MAX_4BYTE,
);
}
}