blob: 983ba3b015bf78a1b56fae4419338e0df7ab450a [file] [log] [blame]
use std::io;
use byteorder::{ByteOrder, WriteBytesExt, LittleEndian};
/// pack_uint packs the given integer in the smallest number of bytes possible,
/// and writes it to the given writer. The number of bytes written is returned
/// on success.
pub fn pack_uint<W: io::Write>(wtr: W, n: u64) -> io::Result<u8> {
let nbytes = pack_size(n);
pack_uint_in(wtr, n, nbytes).map(|_| nbytes)
}
/// pack_uint_in is like pack_uint, but always uses the number of bytes given
/// to pack the number given.
///
/// `nbytes` must be >= pack_size(n) and <= 8, where `pack_size(n)` is the
/// smallest number of bytes that can store the integer given.
pub fn pack_uint_in<W: io::Write>(
mut wtr: W,
n: u64,
nbytes: u8,
) -> io::Result<()> {
wtr.write_uint::<LittleEndian>(n, nbytes as usize).map_err(From::from)
}
/// unpack_uint is the dual of pack_uint. It unpacks the integer at the current
/// position in `slice` after reading `nbytes` bytes.
///
/// `nbytes` must be >= 1 and <= 8.
#[inline(always)]
pub fn unpack_uint(slice: &[u8], nbytes: u8) -> u64 {
LittleEndian::read_uint(slice, nbytes as usize)
}
/// pack_size returns the smallest number of bytes that can encode `n`.
pub fn pack_size(n: u64) -> u8 {
if n < 1 << 8 {
1
} else if n < 1 << 16 {
2
} else if n < 1 << 24 {
3
} else if n < 1 << 32 {
4
} else if n < 1 << 40 {
5
} else if n < 1 << 48 {
6
} else if n < 1 << 56 {
7
} else {
8
}
}
#[cfg(test)]
mod tests {
use std::io;
use quickcheck::{QuickCheck, StdGen};
use super::*;
#[test]
fn prop_pack_in_out() {
fn p(num: u64) -> bool {
let mut buf = io::Cursor::new(vec![]);
let size = pack_uint(&mut buf, num).unwrap();
buf.set_position(0);
num == unpack_uint(buf.get_ref(), size)
}
QuickCheck::new()
.gen(StdGen::new(::rand::thread_rng(), 257)) // pick byte boundary
.quickcheck(p as fn(u64) -> bool);
}
}