diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -18,3 +18,4 @@ subinclude:cfastmanifest/.hgignore subinclude:linelog/.hgignore +subinclude:rust/.hgignore diff --git a/rust/.hgignore b/rust/.hgignore new file mode 100644 --- /dev/null +++ b/rust/.hgignore @@ -0,0 +1,2 @@ +target/ +vlq/Cargo.lock diff --git a/rust/vlq/Cargo.toml b/rust/vlq/Cargo.toml new file mode 100644 --- /dev/null +++ b/rust/vlq/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "vlq" +version = "0.1.0" + +[dependencies] +num-traits = "0.1" + +[dev-dependencies] +quickcheck = "0.4" diff --git a/rust/vlq/src/lib.rs b/rust/vlq/src/lib.rs new file mode 100644 --- /dev/null +++ b/rust/vlq/src/lib.rs @@ -0,0 +1,175 @@ +//! VLQ (Variable-length quantity) encoding. + +extern crate num_traits; + +#[cfg(test)] +#[macro_use] +extern crate quickcheck; + +use num_traits::{PrimInt, Unsigned}; +use std::io::{self, Read, Write}; + +#[derive(Debug, PartialEq)] +pub enum DecodeError { + IncompleteBuffer, + Overflow, +} + +pub trait VLQEncode { + /// Encode an integer to a VLQ byte array and write it directly to a stream. + /// + /// # Examples + /// + /// ``` + /// use vlq::VLQEncode; + /// let mut v = vec![]; + /// + /// let x = 120u8; + /// assert!(x.write_vlq(&mut v).is_ok()); + /// assert_eq!(v, vec![120]); + /// + /// let x = 22742734291u64; + /// assert!(x.write_vlq(&mut v).is_ok()); + /// + /// assert_eq!(v, vec![120, 211, 171, 202, 220, 84]); + /// ``` + fn write_vlq(&self, writer: &mut W) -> io::Result<()>; +} + +pub trait VLQDecode { + /// Read a VLQ byte array from stream and decode it to an integer. + /// + /// # Examples + /// + /// ``` + /// use vlq::{VLQDecode,DecodeError}; + /// use std::io::{Cursor,Seek,SeekFrom}; + /// + /// let mut c = Cursor::new(vec![120u8, 211, 171, 202, 220, 84]); + /// + /// let x = c.read_vlq(); + /// assert_eq!(x, Ok(120u8)); + /// + /// let x: Result = c.read_vlq(); + /// assert_eq!(x, Err(DecodeError::Overflow)); + /// + /// c.seek(SeekFrom::Start(1)); + /// let x = c.read_vlq(); + /// assert_eq!(x, Ok(22742734291u64)) + /// ``` + fn read_vlq(&mut self) -> Result; +} + +impl VLQEncode for T { + fn write_vlq(&self, writer: &mut W) -> io::Result<()> { + // FIXME: Replace 64 with size_of::() * 8 or an associated constant + let mut buf = [0u8; (64 + 6) / 7]; + let mut value = self.clone(); + let mut len = 0; + for i in 0..buf.len() { + let mut byte = value.bitand(T::from(127).unwrap()).to_u8().unwrap(); + let next = value.unsigned_shr(7); + if !next.is_zero() { + byte |= 128; + } + buf[i] = byte; + value = next; + if value.is_zero() { + len = i + 1; + break; + } + } + debug_assert_ne!(len, 0); + writer.write(&buf[0..len]).and_then(|r| { + if r == len { + Ok(()) + } else { + Err(io::Error::new(io::ErrorKind::Other, "write incomplete")) + } + }) + } +} + +impl VLQDecode for R { + fn read_vlq(&mut self) -> Result { + let shift_bits = T::from(1 << 7).unwrap(); + let mut buf = [0u8]; + let mut value = T::zero(); + let mut base = T::one(); + loop { + match self.read(&mut buf) { + Ok(1) => (), + _ => return Err(DecodeError::IncompleteBuffer), + }; + let byte = buf[0]; + value = match T::from(byte & 127).and_then(|v| v.checked_mul(&base)).and_then(|v| v.checked_add(&value)) { + None => return Err(DecodeError::Overflow), + Some(x) => x, + }; + if byte & 128 == 0 { + break; + } + base = match base.checked_mul(&shift_bits) { + None => return Err(DecodeError::Overflow), + Some(x) => x, + }; + } + Ok(value) + } +} + +#[cfg(test)] +mod tests { + use num_traits::{PrimInt, Unsigned}; + use std::io::{Cursor, Seek, SeekFrom}; + use {VLQDecode, VLQEncode, DecodeError}; + + fn check_round_trip(x: T) -> bool { + let mut v = vec![]; + assert!(x.write_vlq(&mut v).is_ok()); + + let mut c = Cursor::new(v); + c.read_vlq() == Ok(x) + } + + #[test] + fn test_round_trip_manual() { + for i in vec![ + 0, + 1, + 17, + 63, + 127, + 128, + 129, + 255, + 256, + 65535, + 65536, + 65537, + 12345678901234, + 12345678901234567890, + 18446744073709551615u64, + ] { + assert!(check_round_trip(i)); + } + } + + #[test] + fn test_errors() { + let mut c = Cursor::new(vec![]); + assert_eq!(c.read_vlq() as Result, Err(DecodeError::IncompleteBuffer)); + + let mut c = Cursor::new(vec![255, 129]); + assert_eq!(c.read_vlq() as Result, Err(DecodeError::IncompleteBuffer)); + + c.seek(SeekFrom::Start(0)).unwrap(); + assert_eq!(c.read_vlq() as Result, Err(DecodeError::Overflow)); + } + + quickcheck! { + fn test_round_trip_u64(x: u64) -> bool { + check_round_trip(x) + } + } +} \ No newline at end of file