#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DecodeError {
Truncated,
Invalid(&'static str),
}
impl core::fmt::Display for DecodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
DecodeError::Truncated => write!(f, "bitstream truncated"),
DecodeError::Invalid(what) => write!(f, "invalid bitstream: {what}"),
}
}
}
impl std::error::Error for DecodeError {}
#[derive(Default, Debug, Clone)]
pub struct ByteWriter {
pub buf: Vec<u8>,
}
impl ByteWriter {
pub fn new() -> Self {
Self::default()
}
pub fn u8(&mut self, v: u8) {
self.buf.push(v);
}
pub fn varint(&mut self, mut v: u64) {
loop {
let b = (v & 0x7f) as u8;
v >>= 7;
if v == 0 {
self.buf.push(b);
return;
}
self.buf.push(b | 0x80);
}
}
pub fn svarint(&mut self, v: i64) {
self.varint(zigzag(v));
}
pub fn f64(&mut self, v: f64) {
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn block(&mut self, bytes: &[u8]) {
self.varint(bytes.len() as u64);
self.buf.extend_from_slice(bytes);
}
pub fn bytes(&mut self, bytes: &[u8]) {
self.buf.extend_from_slice(bytes);
}
}
#[derive(Debug, Clone)]
pub struct ByteReader<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> ByteReader<'a> {
pub fn new(buf: &'a [u8]) -> Self {
Self { buf, pos: 0 }
}
pub fn position(&self) -> usize {
self.pos
}
pub fn u8(&mut self) -> Result<u8, DecodeError> {
let b = *self.buf.get(self.pos).ok_or(DecodeError::Truncated)?;
self.pos += 1;
Ok(b)
}
pub fn varint(&mut self) -> Result<u64, DecodeError> {
let mut v = 0u64;
let mut shift = 0;
loop {
let b = self.u8()?;
if shift >= 64 {
return Err(DecodeError::Invalid("varint overflow"));
}
v |= ((b & 0x7f) as u64) << shift;
if b & 0x80 == 0 {
return Ok(v);
}
shift += 7;
}
}
pub fn svarint(&mut self) -> Result<i64, DecodeError> {
Ok(unzigzag(self.varint()?))
}
pub fn f64(&mut self) -> Result<f64, DecodeError> {
let b = self.take(8)?;
Ok(f64::from_le_bytes(b.try_into().unwrap()))
}
pub fn take(&mut self, n: usize) -> Result<&'a [u8], DecodeError> {
let end = self.pos.checked_add(n).ok_or(DecodeError::Truncated)?;
let s = self.buf.get(self.pos..end).ok_or(DecodeError::Truncated)?;
self.pos = end;
Ok(s)
}
pub fn block(&mut self) -> Result<&'a [u8], DecodeError> {
let n = self.varint()? as usize;
self.take(n)
}
}
pub fn zigzag(v: i64) -> u64 {
((v << 1) ^ (v >> 63)) as u64
}
pub fn unzigzag(u: u64) -> i64 {
((u >> 1) as i64) ^ -((u & 1) as i64)
}
#[derive(Default, Debug, Clone)]
pub struct BitWriter {
buf: Vec<u8>,
acc: u64,
n: u32,
}
impl BitWriter {
pub fn new() -> Self {
Self::default()
}
pub fn put(&mut self, v: u64, nbits: u32) {
debug_assert!(nbits <= 57);
if nbits == 0 {
return;
}
self.acc |= (v & ((1u64 << nbits) - 1)) << self.n;
self.n += nbits;
while self.n >= 8 {
self.buf.push(self.acc as u8);
self.acc >>= 8;
self.n -= 8;
}
}
pub fn finish(mut self) -> Vec<u8> {
if self.n > 0 {
self.buf.push(self.acc as u8);
}
self.buf
}
}
#[derive(Debug, Clone)]
pub struct BitReader<'a> {
buf: &'a [u8],
pos: usize,
acc: u64,
n: u32,
}
impl<'a> BitReader<'a> {
pub fn new(buf: &'a [u8]) -> Self {
Self {
buf,
pos: 0,
acc: 0,
n: 0,
}
}
pub fn get(&mut self, nbits: u32) -> Result<u64, DecodeError> {
if nbits == 0 {
return Ok(0);
}
while self.n < nbits {
let b = *self.buf.get(self.pos).ok_or(DecodeError::Truncated)?;
self.pos += 1;
self.acc |= (b as u64) << self.n;
self.n += 8;
}
let v = self.acc & ((1u64 << nbits) - 1);
self.acc >>= nbits;
self.n -= nbits;
Ok(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn varint_and_zigzag_roundtrip() {
let mut w = ByteWriter::new();
let vals = [0i64, -1, 1, 63, -64, 1 << 40, i64::MIN + 1, i64::MAX];
for &v in &vals {
w.svarint(v);
}
w.f64(-3.25);
let mut r = ByteReader::new(&w.buf);
for &v in &vals {
assert_eq!(r.svarint().unwrap(), v);
}
assert_eq!(r.f64().unwrap(), -3.25);
assert_eq!(r.u8(), Err(DecodeError::Truncated));
}
#[test]
fn bits_roundtrip() {
let mut w = BitWriter::new();
for i in 0..200u64 {
w.put(i * 7919, (i % 30) as u32);
}
let buf = w.finish();
let mut r = BitReader::new(&buf);
for i in 0..200u64 {
let nb = (i % 30) as u32;
let mask = if nb == 0 { 0 } else { (1u64 << nb) - 1 };
assert_eq!(r.get(nb).unwrap(), (i * 7919) & mask);
}
}
}