pub use crate::bitstream::CountPrefix;
pub use crate::wirelen::WireLen;
pub mod leb128 {
use crate::bitstream::{BitError, Sink, Source, sealed};
use crate::field::Bits;
use alloc::format;
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be read/written as a LEB128 varint",
note = "supported widths: u8, u16, u32, u64, u128 — declare a `uN` field as the next wider primitive",
note = "this trait is sealed — the supported widths are built in"
)]
pub trait Varint: Bits + sealed::Sealed {}
impl Varint for u8 {}
impl Varint for u16 {}
impl Varint for u32 {}
impl Varint for u64 {}
impl Varint for u128 {}
pub fn parse<S: Source, T: Varint>(r: &mut S) -> Result<T, BitError> {
let start = r.bit_pos();
let max_bytes = T::BITS.div_ceil(7);
let mut value: u128 = 0;
let mut shift: u32 = 0;
for _ in 0..max_bytes {
let byte: u8 = r.read()?;
let group = u128::from(byte & 0x7F);
if shift + 7 > T::BITS && (group >> (T::BITS - shift)) != 0 {
return Err(BitError::convert(
format!("LEB128 value overflows u{}", T::BITS),
start,
));
}
value |= group << shift;
if byte & 0x80 == 0 {
return Ok(T::from_bits(value));
}
shift += 7;
}
Err(BitError::convert(
format!(
"unterminated LEB128: no final byte within {max_bytes} bytes (u{})",
T::BITS
),
start,
))
}
pub fn write<K: Sink, T: Varint>(v: &T, w: &mut K) -> Result<(), BitError> {
let mut value = v.into_bits();
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
w.write(byte)?;
if value == 0 {
return Ok(());
}
}
}
}
pub mod cstring {
use crate::bitstream::{BitError, Sink, Source};
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
pub fn parse<S: Source>(r: &mut S) -> Result<Vec<u8>, BitError> {
let mut v = Vec::new();
loop {
let b: u8 = r.read()?;
if b == 0 {
return Ok(v);
}
v.push(b);
}
}
pub fn write<K: Sink>(v: &[u8], w: &mut K) -> Result<(), BitError> {
if let Some(i) = v.iter().position(|&b| b == 0) {
return Err(BitError::convert(
format!("embedded NUL at byte {i}: a NUL-terminated string cannot represent it"),
w.bit_pos(),
));
}
w.write_bytes(v)?;
w.write(0u8)
}
pub fn parse_utf8<S: Source>(r: &mut S) -> Result<String, BitError> {
let start = r.bit_pos();
let bytes = parse(r)?;
String::from_utf8(bytes)
.map_err(|e| BitError::convert(format!("invalid UTF-8 in string field: {e}"), start))
}
pub fn write_utf8<K: Sink>(s: &str, w: &mut K) -> Result<(), BitError> {
write(s.as_bytes(), w)
}
}
pub mod prefixed {
use crate::bitstream::{BitError, CountPrefix, Sink, Source};
use alloc::format;
use alloc::string::{String, ToString};
pub fn parse_string<S: Source, P: CountPrefix>(r: &mut S) -> Result<String, BitError> {
let start = r.bit_pos();
let n = r.read::<P>()?.to_count();
let bytes = r.read_bytes(n)?;
String::from_utf8(bytes)
.map_err(|e| BitError::convert(format!("invalid UTF-8 in string field: {e}"), start))
}
pub fn write_string<K: Sink, P: CountPrefix>(s: &str, w: &mut K) -> Result<(), BitError> {
let prefix =
P::try_from_len(s.len()).map_err(|e| BitError::convert(e.to_string(), w.bit_pos()))?;
w.write(prefix)?;
w.write_bytes(s.as_bytes())
}
}
#[cfg(test)]
mod unit {
use super::*;
use crate::bitstream::{BitReader, BitWriter, ErrorKind};
use crate::u12;
use alloc::vec;
use alloc::vec::Vec;
fn encode_with<T: ?Sized>(
f: impl Fn(&T, &mut BitWriter) -> Result<(), crate::BitError>,
v: &T,
) -> Vec<u8> {
let mut w = BitWriter::new();
f(v, &mut w).unwrap();
w.into_bytes()
}
fn leb_round_trip<T: leb128::Varint + PartialEq + core::fmt::Debug>(v: T) {
let mut w = BitWriter::new();
leb128::write(&v, &mut w).unwrap();
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
assert_eq!(leb128::parse::<_, T>(&mut r).unwrap(), v);
assert_eq!(r.remaining_bits(), 0, "consumed exactly the varint");
}
#[test]
fn leb128_round_trips_per_width() {
for v in [0u8, 1, 127, 128, u8::MAX] {
leb_round_trip(v);
}
for v in [0u16, 127, 128, 0x3FFF, u16::MAX] {
leb_round_trip(v);
}
for v in [0u32, 300, u32::MAX] {
leb_round_trip(v);
}
for v in [0u64, 300, 1_000_000, u64::MAX] {
leb_round_trip(v);
}
for v in [0u128, u128::from(u64::MAX) + 1, u128::MAX] {
leb_round_trip(v);
}
}
#[test]
fn leb128_golden_bytes() {
assert_eq!(encode_with(leb128::write, &300u64), [0xAC, 0x02]);
assert_eq!(encode_with(leb128::write, &1u32), [0x01]);
}
#[test]
fn leb128_accepts_non_minimal() {
let mut r = BitReader::new(&[0x80, 0x00]);
assert_eq!(leb128::parse::<_, u8>(&mut r).unwrap(), 0);
}
#[test]
fn leb128_overflow_is_an_error_not_a_panic() {
let mut r = BitReader::new(&[0xFF, 0x01]);
assert_eq!(leb128::parse::<_, u8>(&mut r).unwrap(), 255);
let mut r = BitReader::new(&[0xFF, 0x02]);
let err = leb128::parse::<_, u8>(&mut r).unwrap_err();
assert!(
matches!(&err.kind, ErrorKind::Convert { message } if message.contains("overflows u8")),
"got {err:?}"
);
assert_eq!(err.at, 0, "error points at the varint's start");
let mut r = BitReader::new(&[0xFF, 0xFF, 0xFF, 0xFF, 0x1F]);
let err = leb128::parse::<_, u32>(&mut r).unwrap_err();
assert!(
matches!(&err.kind, ErrorKind::Convert { message } if message.contains("overflows u32"))
);
let mut ok = vec![0xFF; 18];
ok.push(0x03);
let mut r = BitReader::new(&ok);
assert_eq!(leb128::parse::<_, u128>(&mut r).unwrap(), u128::MAX);
let mut over = vec![0xFF; 18];
over.push(0x04);
let mut r = BitReader::new(&over);
assert!(leb128::parse::<_, u128>(&mut r).is_err());
}
#[test]
fn leb128_hostile_continuation_run_is_bounded() {
let bytes = [0x80u8; 11];
let mut r = BitReader::new(&bytes);
let err = leb128::parse::<_, u64>(&mut r).unwrap_err();
assert!(
matches!(&err.kind, ErrorKind::Convert { message } if message.contains("unterminated")),
"got {err:?}"
);
}
#[test]
fn leb128_eof_mid_varint() {
let mut r = BitReader::new(&[0x80, 0x80]);
let err = leb128::parse::<_, u64>(&mut r).unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnexpectedEof { .. }));
}
#[test]
fn cstring_round_trips() {
for s in [&b""[..], b"a", b"alpha", &[0xFF, 0xFE]] {
let bytes = encode_with(cstring::write, s);
assert_eq!(bytes.last(), Some(&0));
let mut r = BitReader::new(&bytes);
assert_eq!(cstring::parse(&mut r).unwrap(), s);
}
}
#[test]
fn cstring_eof_before_terminator() {
let mut r = BitReader::new(b"hi");
let err = cstring::parse(&mut r).unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnexpectedEof { .. }));
}
#[test]
fn cstring_write_rejects_embedded_nul() {
let mut w = BitWriter::new();
let err = cstring::write(&[1, 0, 2], &mut w).unwrap_err();
assert!(
matches!(&err.kind, ErrorKind::Convert { message } if message.contains("byte 1")),
"got {err:?}"
);
let mut w = BitWriter::new();
assert!(cstring::write_utf8("a\0b", &mut w).is_err());
}
#[test]
fn cstring_utf8_forms() {
let bytes = encode_with(cstring::write_utf8, "héllo");
let mut r = BitReader::new(&bytes);
assert_eq!(cstring::parse_utf8(&mut r).unwrap(), "héllo");
let mut r = BitReader::new(&[0xFF, 0x00]);
let err = cstring::parse_utf8(&mut r).unwrap_err();
assert!(matches!(&err.kind, ErrorKind::Convert { message } if message.contains("UTF-8")));
}
fn prefixed_round_trip<P: CountPrefix>(s: &str) {
let mut w = BitWriter::new();
prefixed::write_string::<_, P>(s, &mut w).unwrap();
let bytes = w.into_bytes();
let mut r = BitReader::new(&bytes);
assert_eq!(prefixed::parse_string::<_, P>(&mut r).unwrap(), s);
}
#[test]
fn prefixed_round_trips_across_prefix_types() {
for s in ["", "x", "hello", "héllo wörld"] {
prefixed_round_trip::<u8>(s);
prefixed_round_trip::<u16>(s);
prefixed_round_trip::<u32>(s);
prefixed_round_trip::<u12>(s); }
}
#[test]
fn prefixed_u12_is_bit_native() {
let mut w = BitWriter::new();
prefixed::write_string::<_, u12>("hi", &mut w).unwrap();
let bytes = w.into_bytes();
assert_eq!(bytes[0], 0x00); assert_eq!(bytes[1] >> 4, 0x2); }
#[test]
fn prefixed_write_overflow_is_checked() {
let long = "x".repeat(256);
let mut w = BitWriter::new();
let err = prefixed::write_string::<_, u8>(&long, &mut w).unwrap_err();
assert!(
matches!(&err.kind, ErrorKind::Convert { message } if message.contains("256")),
"got {err:?}"
);
let long = "x".repeat(4096);
let mut w = BitWriter::new();
assert!(prefixed::write_string::<_, u12>(&long, &mut w).is_err());
}
#[test]
fn prefixed_hostile_length_is_eof_not_alloc() {
let mut wire = vec![0xFF, 0xFF, 0xFF, 0xFF];
wire.extend_from_slice(b"abc");
let mut r = BitReader::new(&wire);
let err = prefixed::parse_string::<_, u32>(&mut r).unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnexpectedEof { .. }));
}
#[test]
fn prefixed_invalid_utf8_is_convert() {
let wire = [0x02, 0xFF, 0xFE];
let mut r = BitReader::new(&wire);
let err = prefixed::parse_string::<_, u8>(&mut r).unwrap_err();
assert!(matches!(&err.kind, ErrorKind::Convert { message } if message.contains("UTF-8")));
}
#[test]
fn error_display_texts() {
let mut w = BitWriter::new();
let e = prefixed::write_string::<_, u8>(&"x".repeat(300), &mut w).unwrap_err();
assert_eq!(
e.to_string(),
"conversion failed: value 300 does not fit in 8 bits at bit 0"
);
}
}