#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(warnings)]
#![deny(clippy::nursery)]
#![deny(clippy::pedantic)]
#![deny(clippy::all)]
use std::io::{Read, Result, Write};
pub const MAX_BYTES_PACKED: usize = 5;
#[inline]
pub fn pack<T: Write + ?Sized>(dst: &mut T, mut value: i32) -> Result<()> {
let mut current_byte: u8 = 0;
if value < 0 {
current_byte = 0b0100_0000;
value = !value;
}
current_byte |= u8::try_from(value & 0b0011_1111).expect("should always be inside the range");
value >>= 6;
while value != 0 {
current_byte |= 0b1000_0000;
dst.write_all(std::slice::from_ref(¤t_byte))?;
current_byte =
u8::try_from(value & 0b0111_1111).expect("should always be inside the range");
value >>= 7;
}
dst.write_all(std::slice::from_ref(¤t_byte))?;
Ok(())
}
#[inline]
pub fn unpack<T: Read + ?Sized>(src: &mut T) -> Result<i32> {
const MASKS: [i32; 4] = [0x7F, 0x7F, 0x7F, 0x0F];
const SHIFTS: [i32; 4] = [6, 6 + 7, 6 + 7 + 7, 6 + 7 + 7 + 7];
let mut result: i32;
let mut current_byte: u8 = 0;
src.read_exact(std::slice::from_mut(&mut current_byte))?;
let sign = (current_byte >> 6) & 1;
result = i32::from(current_byte & 0x3F);
for (mask, shift) in MASKS.into_iter().zip(SHIFTS.into_iter()) {
if (current_byte & 0x80) == 0 {
break;
}
src.read_exact(std::slice::from_mut(&mut current_byte))?;
result |= (i32::from(current_byte) & mask) << shift;
}
result ^= -i32::from(sign);
Ok(result)
}
pub trait PackTwInt {
fn pack<T: Write + ?Sized>(self, dst: &mut T) -> Result<()>;
}
impl PackTwInt for i32 {
#[inline]
fn pack<T: Write + ?Sized>(self, dst: &mut T) -> Result<()> {
pack(dst, self)
}
}
pub trait UnPackTwInt: Read {
fn unpack(&mut self) -> Result<i32>;
}
impl<T: Read + ?Sized> UnPackTwInt for T {
#[inline]
fn unpack(&mut self) -> Result<i32> {
unpack(self)
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
#[test]
pub fn unpack_0() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, 0).is_ok());
buff.set_position(0);
assert_eq!(0, unpack(&mut buff).unwrap());
}
#[test]
pub fn pack_0() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, 0).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b0000_0000);
}
#[test]
pub fn pack_1() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, 1).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b0000_0001);
}
#[test]
pub fn unpack_1() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, 1).is_ok());
buff.set_position(0);
assert_eq!(1, unpack(&mut buff).unwrap());
}
#[test]
pub fn pack_2() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, 2).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b0000_0010);
}
#[test]
pub fn unpack_2() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, 2).is_ok());
buff.set_position(0);
assert_eq!(2, unpack(&mut buff).unwrap());
}
#[test]
pub fn pack_minus_2() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, -2).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b0100_0001);
}
#[test]
pub fn unpack_minus_2() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, -2).is_ok());
buff.set_position(0);
assert_eq!(-2, unpack(&mut buff).unwrap());
}
#[test]
pub fn pack_minus_1() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, -1).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b0100_0000);
}
#[test]
pub fn unpack_minus_1() {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, -1).is_ok());
buff.set_position(0);
assert_eq!(-1, unpack(&mut buff).unwrap());
}
#[test]
pub fn pack_0_to_63() {
for i in 0..64 {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, i).is_ok());
let buff = buff.into_inner();
assert_eq!(i32::from(buff[0]), i);
}
}
#[test]
pub fn unpack_0_to_63() {
for i in 0..64 {
let mut buff = Cursor::new([0; 1]);
assert!(pack(&mut buff, i).is_ok());
buff.set_position(0);
assert_eq!(i, unpack(&mut buff).unwrap());
}
}
#[test]
pub fn pack_64() {
let mut buff = Cursor::new([0; 2]);
assert!(pack(&mut buff, 64).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b1000_0000);
assert_eq!(buff[1], 0b0000_0001);
}
#[test]
pub fn unpack_64() {
let mut buff = Cursor::new([0; 2]);
assert!(pack(&mut buff, 64).is_ok());
buff.set_position(0);
assert_eq!(64, unpack(&mut buff).unwrap());
}
#[test]
pub fn pack_64_trait() {
let mut buff = Cursor::new([0; 2]);
assert!(64.pack(&mut buff).is_ok());
let buff = buff.into_inner();
assert_eq!(buff[0], 0b1000_0000);
assert_eq!(buff[1], 0b0000_0001);
}
#[test]
pub fn pack_64_trait_slice() {
let mut buff = [0; 2];
assert!(64.pack(&mut buff.as_mut_slice()).is_ok());
assert_eq!(buff[0], 0b1000_0000);
assert_eq!(buff[1], 0b0000_0001);
}
#[test]
pub fn unpack_64_trait() {
let mut buff = Cursor::new([0b1000_0000, 0b0000_0001]);
let result = buff.unpack().unwrap();
assert_eq!(result, 64);
}
#[test]
pub fn unpack_64_trait_slice() {
let buff = [0b1000_0000, 0b0000_0001];
let result = buff.as_slice().unpack().unwrap();
assert_eq!(result, 64);
}
#[test]
pub fn roundtrip_256_trait() {
let mut buff = Cursor::new([0; MAX_BYTES_PACKED]);
256.pack(&mut buff).unwrap();
buff.set_position(0);
let result = buff.unpack().unwrap();
assert_eq!(256, result);
}
static DATA: [i32; 14] = [
0,
1,
-1,
32,
64,
256,
-512,
12345,
-123_456,
1_234_567,
12_345_678,
123_456_789,
2_147_483_647,
(-2_147_483_647 - 1),
];
static SIZES: [u64; 14] = [1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5];
#[test]
pub fn roundtrip_pack_unpack() {
for i in 0..DATA.len() {
let mut buff = Cursor::new([0; MAX_BYTES_PACKED]);
DATA[i].pack(&mut buff).unwrap();
assert_eq!(buff.position(), SIZES[i]);
buff.set_position(0);
let result = buff.unpack().unwrap();
assert_eq!(buff.position(), SIZES[i]);
assert_eq!(DATA[i], result);
}
}
}