mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
generics
This commit is contained in:
parent
74124393be
commit
1c037528f6
3 changed files with 63 additions and 70 deletions
|
|
@ -3,3 +3,6 @@ name = "bitbuffer"
|
|||
version = "0.1.0"
|
||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
num-traits = "0.2"
|
||||
99
src/lib.rs
99
src/lib.rs
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
extern crate test;
|
||||
|
||||
use num_traits::{PrimInt, Signed};
|
||||
use std::cmp::min;
|
||||
use std::mem::size_of;
|
||||
use std::ops::BitOrAssign;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
|
@ -47,16 +49,6 @@ macro_rules! array_ref {
|
|||
|
||||
const USIZE_SIZE: usize = std::mem::size_of::<usize>();
|
||||
|
||||
macro_rules! make_signed {
|
||||
($unsigned:expr, $type:ty, $count:expr) => {
|
||||
{
|
||||
let sign_bits = $unsigned >> ($count - 1) & 1;
|
||||
let high_bits = 0 - sign_bits as $type;
|
||||
high_bits << $count | $unsigned as $type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BitBuffer<'a> {
|
||||
pub fn from_padded_slice(bytes: &'a [u8], byte_len: usize) -> BitBuffer<'a> {
|
||||
BitBuffer {
|
||||
|
|
@ -74,7 +66,13 @@ impl<'a> BitBuffer<'a> {
|
|||
self.byte_len
|
||||
}
|
||||
|
||||
pub fn read_usize(&self, position: usize, count: usize) -> usize {
|
||||
pub fn read_usize(&self, position: usize, count: usize) -> Result<usize> {
|
||||
if position + count > self.bit_len {
|
||||
return Err(ReadError::NotEnoughData {
|
||||
requested: count,
|
||||
bits_left: self.bit_len - position,
|
||||
});
|
||||
}
|
||||
let byte_index = position / 8;
|
||||
let bit_offset = position & 7;
|
||||
let bytes: &[u8; USIZE_SIZE] = array_ref!(self.bytes, byte_index, USIZE_SIZE);
|
||||
|
|
@ -84,72 +82,65 @@ impl<'a> BitBuffer<'a> {
|
|||
let container = usize::from_le(container_le);
|
||||
let shifted = container >> bit_offset;
|
||||
let mask = !(usize::max_value() << count);
|
||||
shifted & mask
|
||||
Ok(shifted & mask)
|
||||
}
|
||||
|
||||
pub fn read_bool(&self, position: usize) -> bool {
|
||||
pub fn read_bool(&self, position: usize) -> Result<bool> {
|
||||
let byte_index = position / 8;
|
||||
let bit_offset = position & 7;
|
||||
|
||||
if position >= self.bit_len {
|
||||
return Err(ReadError::NotEnoughData {
|
||||
requested: 1,
|
||||
bits_left: self.bit_len - position,
|
||||
});
|
||||
}
|
||||
|
||||
let byte = self.bytes[byte_index];
|
||||
let shifted = byte >> bit_offset;
|
||||
let mask = 1u8 << bit_offset;
|
||||
shifted & mask == 1
|
||||
Ok(shifted & mask == 1)
|
||||
}
|
||||
|
||||
pub fn read_u8(&self, position: usize, count: usize) -> u8 {
|
||||
self.read_usize(position, count) as u8
|
||||
pub fn read<T>(&self, position: usize, count: usize) -> Result<T>
|
||||
where T: PrimInt + BitOrAssign
|
||||
{
|
||||
if size_of::<T>() * 8 < count {
|
||||
return Err(ReadError::TooManyBits {
|
||||
requested: count,
|
||||
max: size_of::<T>() * 8,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn read_u16(&self, position: usize, count: usize) -> u16 {
|
||||
self.read_usize(position, count) as u16
|
||||
}
|
||||
|
||||
pub fn read_u32(&self, position: usize, count: usize) -> u32 {
|
||||
if size_of::<usize>() > size_of::<u32>() || (count / 8) < size_of::<usize>() {
|
||||
self.read_usize(position, count) as u32
|
||||
} else {
|
||||
let value: u32 = self.read_u16(position, count) as u32;
|
||||
value | (self.read_u16(position + 16, count - 16) as u32) << 16
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_u64(&self, position: usize, count: usize) -> u64 {
|
||||
if size_of::<usize>() > size_of::<u64>() || (count / 8) < size_of::<usize>() {
|
||||
self.read_usize(position, count) as u64
|
||||
if size_of::<usize>() > size_of::<T>() || (count / 8) < size_of::<usize>() {
|
||||
Ok(T::from(self.read_usize(position, count)?).unwrap())
|
||||
} else {
|
||||
let mut bits_left = count;
|
||||
let mut value = 0;
|
||||
let max_read = (size_of::<usize>() - 1) * 8;
|
||||
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(bits_left, max_read);
|
||||
value |= (self.read_usize(read_pos, read) as u64) << bit_offset;
|
||||
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;
|
||||
}
|
||||
|
||||
value
|
||||
Ok(partial)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_i8(&self, position: usize, count: usize) -> i8 {
|
||||
let unsigned = self.read_u8(position, count);
|
||||
make_signed!(unsigned, i8, count)
|
||||
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_i16(&self, position: usize, count: usize) -> i16 {
|
||||
let unsigned = self.read_u16(position, count);
|
||||
make_signed!(unsigned, i16, count)
|
||||
}
|
||||
|
||||
pub fn read_i32(&self, position: usize, count: usize) -> i32 {
|
||||
let unsigned = self.read_u32(position, count);
|
||||
make_signed!(unsigned, i32, count)
|
||||
}
|
||||
|
||||
pub fn read_bytes(&self, position: usize, byte_count: usize) -> Vec<u8> {
|
||||
pub fn read_bytes(&self, position: usize, byte_count: usize) -> Result<Vec<u8>> {
|
||||
let mut data = vec!();
|
||||
data.reserve_exact(byte_count);
|
||||
let mut byte_left = byte_count;
|
||||
|
|
@ -157,12 +148,12 @@ impl<'a> BitBuffer<'a> {
|
|||
let mut read_pos = position;
|
||||
while byte_left > 0 {
|
||||
let read = min(byte_left, max_read);
|
||||
let bytes: [u8; USIZE_SIZE] = self.read_usize(read_pos, read * 8).to_le_bytes();
|
||||
let bytes: [u8; USIZE_SIZE] = self.read_usize(read_pos, read * 8)?.to_le_bytes();
|
||||
let usable_bytes = &bytes[0..max_read];
|
||||
data.extend_from_slice(usable_bytes);
|
||||
byte_left -= read;
|
||||
read_pos += read;
|
||||
}
|
||||
data
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
33
src/tests.rs
33
src/tests.rs
|
|
@ -6,42 +6,41 @@ use test::Bencher;
|
|||
const BYTES: &'static[u8] = &[
|
||||
0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
|
||||
0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111,
|
||||
0, 0, 0, 0, 0, 0, 0, 0 ,0
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn read_u8() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 8);
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read_u8(0, 1), 0b1);
|
||||
assert_eq!(buffer.read_u8(1, 1), 0b0);
|
||||
assert_eq!(buffer.read_u8(2, 2), 0b01);
|
||||
assert_eq!(buffer.read_u8(7, 5), 0b1010_1);
|
||||
assert_eq!(buffer.read_u8(6, 5), 0b010_10);
|
||||
assert_eq!(buffer.read::<u8>(0, 1).unwrap(), 0b1);
|
||||
assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0);
|
||||
assert_eq!(buffer.read::<u8>(2, 2).unwrap(), 0b01);
|
||||
assert_eq!(buffer.read::<u8>(7, 5).unwrap(), 0b1010_1);
|
||||
assert_eq!(buffer.read::<u8>(6, 5).unwrap(), 0b010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u16() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 8);
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read_u16(6, 12), 0b00_0110_1010_10);
|
||||
assert_eq!(buffer.read::<u16>(6, 12).unwrap(), 0b00_0110_1010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u32() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 8);
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read_u32(6, 24), 0b01_1001_1010_1100_0110_1010_10);
|
||||
assert_eq!(buffer.read::<u32>(6, 24).unwrap(), 0b01_1001_1010_1100_0110_1010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u64() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 8);
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
|
||||
println!("{:#036b}", buffer.read_u64(6, 32));
|
||||
assert_eq!(buffer.read_u64(6, 34), 0b1001_1001_1001_1001_1010_1100_0110_1010_10);
|
||||
assert_eq!(buffer.read_u64(6, 60), 0b00_1110_01111001_1001_1001_1001_1001_1001_1001_1001_1010_1100_0110_1010_10);
|
||||
assert_eq!(buffer.read::<u64>(6, 34).unwrap(), 0b1001_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);
|
||||
}
|
||||
|
||||
fn read_perf(buffer: BitBuffer) -> u16 {
|
||||
|
|
@ -53,7 +52,7 @@ fn read_perf(buffer: BitBuffer) -> u16 {
|
|||
if pos + size > len {
|
||||
return result;
|
||||
}
|
||||
let data = buffer.read_u16(pos, size);
|
||||
let data = buffer.read::<u16>(pos, size).unwrap();
|
||||
result = result.wrapping_add(data);
|
||||
pos += size;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue