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:
parent
1c037528f6
commit
540d9c0e8a
3 changed files with 80 additions and 34 deletions
25
src/is_signed.rs
Normal file
25
src/is_signed.rs
Normal 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);
|
||||||
79
src/lib.rs
79
src/lib.rs
|
|
@ -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,43 +105,54 @@ 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 = {
|
||||||
return Err(ReadError::TooManyBits {
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
requested: count,
|
|
||||||
max: size_of::<T>() * 8,
|
if type_bit_size < count {
|
||||||
});
|
return Err(ReadError::TooManyBits {
|
||||||
}
|
requested: count,
|
||||||
if size_of::<usize>() > size_of::<T>() || (count / 8) < size_of::<usize>() {
|
max: size_of::<T>() * 8,
|
||||||
Ok(T::from(self.read_usize(position, count)?).unwrap())
|
});
|
||||||
} else {
|
|
||||||
let mut bits_left = count;
|
|
||||||
let mut partial = T::zero();
|
|
||||||
let max_read = size_of::<usize>() - 1 * 8;
|
|
||||||
let mut read_pos = position;
|
|
||||||
let mut bit_offset = 0;
|
|
||||||
while bits_left > 0 {
|
|
||||||
let read = min(min(bits_left, max_read), self.bit_len - read_pos);
|
|
||||||
partial |= T::from(self.read_usize(read_pos, read)?).unwrap() << bit_offset;
|
|
||||||
bit_offset += read;
|
|
||||||
read_pos += read;
|
|
||||||
bits_left -= read;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(partial)
|
if size_of::<usize>() > size_of::<T>() || (count / 8) < size_of::<usize>() {
|
||||||
|
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 {
|
||||||
|
let mut bits_left = count;
|
||||||
|
let mut partial = T::zero();
|
||||||
|
let max_read = size_of::<usize>() - 1 * 8;
|
||||||
|
let mut read_pos = position;
|
||||||
|
let mut bit_offset = 0;
|
||||||
|
while bits_left > 0 {
|
||||||
|
let read = min(min(bits_left, max_read), self.bit_len - read_pos);
|
||||||
|
partial |= T::from(self.read_usize(read_pos, read)?).unwrap() << bit_offset;
|
||||||
|
bit_offset += read;
|
||||||
|
read_pos += read;
|
||||||
|
bits_left -= read;
|
||||||
|
}
|
||||||
|
|
||||||
|
partial
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if T::is_signed() {
|
||||||
|
let sign_bit = value >> (count - 1) & 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_signed<T>(&self, position: usize, count: usize) -> Result<T>
|
|
||||||
where T: PrimInt + BitOrAssign + Signed
|
|
||||||
{
|
|
||||||
let value = self.read::<T>(position, count)?;
|
|
||||||
|
|
||||||
let sign_bit = value >> (count - 1) & T::one();
|
|
||||||
Ok(value | (T::zero() - sign_bit) ^ ((T::one() << count) - T::one()))
|
|
||||||
}
|
|
||||||
|
|
||||||
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>> {
|
||||||
let mut data = vec!();
|
let mut data = vec!();
|
||||||
data.reserve_exact(byte_count);
|
data.reserve_exact(byte_count);
|
||||||
|
|
|
||||||
10
src/tests.rs
10
src/tests.rs
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue