1
0
Fork 0
mirror of https://codeberg.org/icewind/bitbuffer.git synced 2026-06-03 16:44:06 +02:00

combine signed reading

This commit is contained in:
Robin Appelman 2019-02-17 01:37:00 +01:00
commit 540d9c0e8a
3 changed files with 80 additions and 34 deletions

25
src/is_signed.rs Normal file
View file

@ -0,0 +1,25 @@
pub trait IsSigned {
fn is_signed() -> bool;
}
macro_rules! impl_is_signed {
($type:ty, $signed:expr) => {
impl IsSigned for $type {
#[inline]
fn is_signed() -> bool {
$signed
}
}
}
}
impl_is_signed!(u8, false);
impl_is_signed!(u16, false);
impl_is_signed!(u32, false);
impl_is_signed!(u64, false);
impl_is_signed!(usize, false);
impl_is_signed!(i8, true);
impl_is_signed!(i16, true);
impl_is_signed!(i32, true);
impl_is_signed!(i64, true);
impl_is_signed!(isize, true);

View file

@ -2,13 +2,15 @@
extern crate test; extern crate test;
use num_traits::{PrimInt, Signed}; use is_signed::IsSigned;
use num_traits::{PrimInt};
use std::cmp::min; use std::cmp::min;
use std::mem::size_of; use std::mem::size_of;
use std::ops::BitOrAssign; use std::ops::BitOrAssign;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
mod is_signed;
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
pub enum ReadError { pub enum ReadError {
@ -66,7 +68,7 @@ impl<'a> BitBuffer<'a> {
self.byte_len self.byte_len
} }
pub fn read_usize(&self, position: usize, count: usize) -> Result<usize> { fn read_usize(&self, position: usize, count: usize) -> Result<usize> {
if position + count > self.bit_len { if position + count > self.bit_len {
return Err(ReadError::NotEnoughData { return Err(ReadError::NotEnoughData {
requested: count, requested: count,
@ -103,16 +105,26 @@ impl<'a> BitBuffer<'a> {
} }
pub fn read<T>(&self, position: usize, count: usize) -> Result<T> pub fn read<T>(&self, position: usize, count: usize) -> Result<T>
where T: PrimInt + BitOrAssign where T: PrimInt + BitOrAssign + IsSigned
{ {
if size_of::<T>() * 8 < count { let value = {
let type_bit_size = size_of::<T>() * 8;
if type_bit_size < count {
return Err(ReadError::TooManyBits { return Err(ReadError::TooManyBits {
requested: count, requested: count,
max: size_of::<T>() * 8, max: size_of::<T>() * 8,
}); });
} }
if size_of::<usize>() > size_of::<T>() || (count / 8) < size_of::<usize>() { if size_of::<usize>() > size_of::<T>() || (count / 8) < size_of::<usize>() {
Ok(T::from(self.read_usize(position, count)?).unwrap()) let raw = self.read_usize(position, count)?;
let max_signed_value = (1 << (type_bit_size - 1)) - 1;
if T::is_signed() && raw > max_signed_value {
return Ok(T::zero() - T::from(raw & max_signed_value).unwrap())
} else {
T::from(raw).unwrap()
}
} else { } else {
let mut bits_left = count; let mut bits_left = count;
let mut partial = T::zero(); let mut partial = T::zero();
@ -127,17 +139,18 @@ impl<'a> BitBuffer<'a> {
bits_left -= read; bits_left -= read;
} }
Ok(partial) partial
}
} }
};
pub fn read_signed<T>(&self, position: usize, count: usize) -> Result<T> if T::is_signed() {
where T: PrimInt + BitOrAssign + Signed
{
let value = self.read::<T>(position, count)?;
let sign_bit = value >> (count - 1) & T::one(); let sign_bit = value >> (count - 1) & T::one();
Ok(value | (T::zero() - sign_bit) ^ ((T::one() << count) - T::one())) let absolute_value = value & !(T::max_value() << (count - 1));
let sign = T::one() - sign_bit - sign_bit;
Ok(absolute_value * sign)
} else {
Ok(value)
}
} }
pub fn read_bytes(&self, position: usize, byte_count: usize) -> Result<Vec<u8>> { pub fn read_bytes(&self, position: usize, byte_count: usize) -> Result<Vec<u8>> {

View file

@ -2,7 +2,6 @@ use std::fs;
use super::*; use super::*;
use test::Bencher; use test::Bencher;
const BYTES: &'static[u8] = &[ const BYTES: &'static[u8] = &[
0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001, 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111, 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111,
@ -17,6 +16,7 @@ fn read_u8() {
assert_eq!(buffer.read::<u8>(0, 1).unwrap(), 0b1); assert_eq!(buffer.read::<u8>(0, 1).unwrap(), 0b1);
assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0); assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0);
assert_eq!(buffer.read::<u8>(2, 2).unwrap(), 0b01); assert_eq!(buffer.read::<u8>(2, 2).unwrap(), 0b01);
assert_eq!(buffer.read::<u8>(0, 3).unwrap(), 0b101);
assert_eq!(buffer.read::<u8>(7, 5).unwrap(), 0b1010_1); assert_eq!(buffer.read::<u8>(7, 5).unwrap(), 0b1010_1);
assert_eq!(buffer.read::<u8>(6, 5).unwrap(), 0b010_10); assert_eq!(buffer.read::<u8>(6, 5).unwrap(), 0b010_10);
} }
@ -43,6 +43,14 @@ fn read_u64() {
assert_eq!(buffer.read::<u64>(6, 60).unwrap(), 0b00_1110_01111001_1001_1001_1001_1001_1001_1001_1001_1010_1100_0110_1010_10); assert_eq!(buffer.read::<u64>(6, 60).unwrap(), 0b00_1110_01111001_1001_1001_1001_1001_1001_1001_1001_1010_1100_0110_1010_10);
} }
#[test]
fn read_i8() {
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
assert_eq!(buffer.read::<i8>(0, 3).unwrap(), -0b1);
assert_eq!(buffer.read::<i8>(0, 8).unwrap(), -0b011_0101);
}
fn read_perf(buffer: BitBuffer) -> u16 { fn read_perf(buffer: BitBuffer) -> u16 {
let size = 5; let size = 5;
let mut pos = 0; let mut pos = 0;