#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Broken {
Short,
Form,
Body,
}
impl std::fmt::Display for Broken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Broken::Short => "the frozen body ends early",
Broken::Form => "the frozen body has a form this version cannot read",
Broken::Body => "the frozen body has a payload that does not parse",
})
}
}
impl std::error::Error for Broken {}
#[inline]
pub fn put_uint(out: &mut Vec<u8>, mut n: u64) {
while n >= 0x80 {
out.push((n as u8) | 0x80);
n >>= 7;
}
out.push(n as u8);
}
#[inline]
pub fn put_int(out: &mut Vec<u8>, v: i64) {
put_uint(out, ((v << 1) ^ (v >> 63)) as u64);
}
#[inline]
pub fn put_f64(out: &mut Vec<u8>, v: f64) {
out.extend_from_slice(&v.to_le_bytes());
}
#[inline]
pub fn put_bytes(out: &mut Vec<u8>, bytes: &[u8]) {
put_uint(out, bytes.len() as u64);
out.extend_from_slice(bytes);
}
pub struct Cut<'a> {
bytes: &'a [u8],
at: usize,
}
impl<'a> Cut<'a> {
#[must_use]
pub const fn new(bytes: &'a [u8]) -> Cut<'a> {
Cut { bytes, at: 0 }
}
#[inline]
pub fn byte(&mut self) -> Result<u8, Broken> {
let b = *self.bytes.get(self.at).ok_or(Broken::Short)?;
self.at += 1;
Ok(b)
}
#[inline]
pub fn uint(&mut self) -> Result<u64, Broken> {
let mut n = 0u64;
let mut shift = 0;
loop {
let b = self.byte()?;
n |= u64::from(b & 0x7f) << shift;
if b < 0x80 {
return Ok(n);
}
shift += 7;
if shift >= 64 {
return Err(Broken::Body);
}
}
}
#[inline]
pub fn int(&mut self) -> Result<i64, Broken> {
let n = self.uint()?;
Ok(((n >> 1) as i64) ^ -((n & 1) as i64))
}
#[inline]
pub fn f64(&mut self) -> Result<f64, Broken> {
let s = self.take(8)?;
let mut b = [0u8; 8];
b.copy_from_slice(s);
Ok(f64::from_le_bytes(b))
}
#[inline]
pub fn take(&mut self, n: usize) -> Result<&'a [u8], Broken> {
let end = self.at.checked_add(n).ok_or(Broken::Short)?;
let s = self.bytes.get(self.at..end).ok_or(Broken::Short)?;
self.at = end;
Ok(s)
}
#[inline]
pub fn bytes(&mut self) -> Result<&'a [u8], Broken> {
let n = self.uint()?;
let n = usize::try_from(n).map_err(|_| Broken::Short)?;
self.take(n)
}
#[must_use]
#[inline]
pub const fn rest(&self) -> &'a [u8] {
self.bytes.split_at(self.at).1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_number_comes_back_as_itself() {
for n in [0u64, 1, 127, 128, 300, 16383, 16384, u64::MAX] {
let mut out = Vec::new();
put_uint(&mut out, n);
assert_eq!(Cut::new(&out).uint(), Ok(n), "{n}");
}
}
#[test]
fn a_signed_number_comes_back_as_itself_and_a_small_one_is_short() {
for v in [0i64, 1, -1, 63, -64, 1000, -1000, i64::MIN, i64::MAX] {
let mut out = Vec::new();
put_int(&mut out, v);
assert_eq!(Cut::new(&out).int(), Ok(v), "{v}");
}
let mut out = Vec::new();
put_int(&mut out, -5);
assert_eq!(out.len(), 1);
}
#[test]
fn a_body_that_ends_early_is_short_and_not_a_panic() {
let mut out = Vec::new();
put_bytes(&mut out, b"hello");
out.truncate(3);
assert_eq!(Cut::new(&out).bytes(), Err(Broken::Short));
assert_eq!(Cut::new(&[]).byte(), Err(Broken::Short));
assert_eq!(Cut::new(&[0x80]).uint(), Err(Broken::Short));
}
#[test]
fn nothing_but_continuation_bits_stops() {
assert_eq!(Cut::new(&[0x80; 32]).uint(), Err(Broken::Body));
}
#[test]
fn the_rest_is_what_is_left() {
let mut out = vec![7u8];
out.extend_from_slice(b"the blob");
let mut cut = Cut::new(&out);
assert_eq!(cut.byte(), Ok(7));
assert_eq!(cut.rest(), b"the blob");
}
}