use crate::error::{Error, Result};
pub const SIZE_INDICATOR_MASK: u8 = 0x80;
pub fn decode(bytes: &[u8]) -> Result<(usize, usize)> {
let first = *bytes
.first()
.ok_or(Error::InvalidLength("empty length_field"))?;
if first & SIZE_INDICATOR_MASK == 0 {
return Ok(((first & 0x7F) as usize, 1));
}
let n = (first & 0x7F) as usize;
if n == 0 {
return Err(Error::InvalidLength("indefinite length form not allowed"));
}
if n > 3 {
return Err(Error::InvalidLength("length_field_size exceeds 3 bytes"));
}
if bytes.len() < 1 + n {
return Err(Error::BufferTooShort {
need: 1 + n,
have: bytes.len(),
what: "length_field (long form)",
});
}
let mut value = 0usize;
for &b in &bytes[1..1 + n] {
value = (value << 8) | b as usize;
}
Ok((value, 1 + n))
}
#[must_use]
pub fn encoded_len(value: usize) -> usize {
if value < 0x80 {
1
} else if value <= 0xFF {
2
} else if value <= 0xFFFF {
3
} else {
4
}
}
pub fn encode_into(value: usize, buf: &mut [u8]) -> Result<usize> {
let need = encoded_len(value);
if value > 0xFFFF {
return Err(Error::LengthTooLarge(value));
}
if buf.len() < need {
return Err(Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
if value < 0x80 {
buf[0] = value as u8;
} else if value <= 0xFF {
buf[0] = SIZE_INDICATOR_MASK | 1;
buf[1] = value as u8;
} else {
buf[0] = SIZE_INDICATOR_MASK | 2;
buf[1] = (value >> 8) as u8;
buf[2] = value as u8;
}
Ok(need)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn short_form_round_trip() {
for v in [0usize, 1, 0x7F] {
let mut buf = [0u8; 4];
let n = encode_into(v, &mut buf).unwrap();
assert_eq!(n, 1);
assert_eq!(encoded_len(v), 1);
let (decoded, hdr) = decode(&buf).unwrap();
assert_eq!((decoded, hdr), (v, 1));
}
}
#[test]
fn two_byte_form_round_trip() {
for v in [0x80usize, 0xFF] {
let mut buf = [0u8; 4];
let n = encode_into(v, &mut buf).unwrap();
assert_eq!(n, 2);
assert_eq!(buf[0], SIZE_INDICATOR_MASK | 1);
let (decoded, hdr) = decode(&buf).unwrap();
assert_eq!((decoded, hdr), (v, 2));
}
}
#[test]
fn three_byte_form_round_trip() {
for v in [0x100usize, 0x1234, 0xFFFF] {
let mut buf = [0u8; 4];
let n = encode_into(v, &mut buf).unwrap();
assert_eq!(n, 3);
assert_eq!(buf[0], SIZE_INDICATOR_MASK | 2);
let (decoded, hdr) = decode(&buf).unwrap();
assert_eq!((decoded, hdr), (v, 3));
}
}
#[test]
fn rejects_oversize() {
let mut buf = [0u8; 4];
assert!(matches!(
encode_into(0x1_0000, &mut buf),
Err(Error::LengthTooLarge(0x1_0000))
));
}
#[test]
fn rejects_indefinite_and_wide() {
assert!(decode(&[0x80]).is_err()); assert!(decode(&[0x84, 0, 0, 0, 0]).is_err()); assert!(decode(&[]).is_err());
assert!(decode(&[0x82, 0x12]).is_err()); }
#[test]
fn mutating_a_byte_changes_decode() {
let mut buf = [0u8; 4];
encode_into(0x1234, &mut buf).unwrap();
let (a, _) = decode(&buf).unwrap();
buf[2] ^= 0xFF;
let (b, _) = decode(&buf).unwrap();
assert_ne!(a, b);
}
}