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"
|
version = "0.1.0"
|
||||||
authors = ["Robin Appelman <robin@icewind.nl>"]
|
authors = ["Robin Appelman <robin@icewind.nl>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
num-traits = "0.2"
|
||||||
99
src/lib.rs
99
src/lib.rs
|
|
@ -2,8 +2,10 @@
|
||||||
|
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
|
use num_traits::{PrimInt, Signed};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
use std::ops::BitOrAssign;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
@ -47,16 +49,6 @@ macro_rules! array_ref {
|
||||||
|
|
||||||
const USIZE_SIZE: usize = std::mem::size_of::<usize>();
|
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> {
|
impl<'a> BitBuffer<'a> {
|
||||||
pub fn from_padded_slice(bytes: &'a [u8], byte_len: usize) -> BitBuffer<'a> {
|
pub fn from_padded_slice(bytes: &'a [u8], byte_len: usize) -> BitBuffer<'a> {
|
||||||
BitBuffer {
|
BitBuffer {
|
||||||
|
|
@ -74,7 +66,13 @@ impl<'a> BitBuffer<'a> {
|
||||||
self.byte_len
|
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 byte_index = position / 8;
|
||||||
let bit_offset = position & 7;
|
let bit_offset = position & 7;
|
||||||
let bytes: &[u8; USIZE_SIZE] = array_ref!(self.bytes, byte_index, USIZE_SIZE);
|
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 container = usize::from_le(container_le);
|
||||||
let shifted = container >> bit_offset;
|
let shifted = container >> bit_offset;
|
||||||
let mask = !(usize::max_value() << count);
|
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 byte_index = position / 8;
|
||||||
let bit_offset = position & 7;
|
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 byte = self.bytes[byte_index];
|
||||||
let shifted = byte >> bit_offset;
|
let shifted = byte >> bit_offset;
|
||||||
let mask = 1u8 << bit_offset;
|
let mask = 1u8 << bit_offset;
|
||||||
shifted & mask == 1
|
Ok(shifted & mask == 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_u8(&self, position: usize, count: usize) -> u8 {
|
pub fn read<T>(&self, position: usize, count: usize) -> Result<T>
|
||||||
self.read_usize(position, count) as u8
|
where T: PrimInt + BitOrAssign
|
||||||
|
{
|
||||||
|
if size_of::<T>() * 8 < count {
|
||||||
|
return Err(ReadError::TooManyBits {
|
||||||
|
requested: count,
|
||||||
|
max: size_of::<T>() * 8,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
if size_of::<usize>() > size_of::<T>() || (count / 8) < size_of::<usize>() {
|
||||||
pub fn read_u16(&self, position: usize, count: usize) -> u16 {
|
Ok(T::from(self.read_usize(position, count)?).unwrap())
|
||||||
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
|
|
||||||
} else {
|
} else {
|
||||||
let mut bits_left = count;
|
let mut bits_left = count;
|
||||||
let mut value = 0;
|
let mut partial = T::zero();
|
||||||
let max_read = (size_of::<usize>() - 1) * 8;
|
let max_read = size_of::<usize>() - 1 * 8;
|
||||||
let mut read_pos = position;
|
let mut read_pos = position;
|
||||||
let mut bit_offset = 0;
|
let mut bit_offset = 0;
|
||||||
while bits_left > 0 {
|
while bits_left > 0 {
|
||||||
let read = min(bits_left, max_read);
|
let read = min(min(bits_left, max_read), self.bit_len - read_pos);
|
||||||
value |= (self.read_usize(read_pos, read) as u64) << bit_offset;
|
partial |= T::from(self.read_usize(read_pos, read)?).unwrap() << bit_offset;
|
||||||
bit_offset += read;
|
bit_offset += read;
|
||||||
read_pos += read;
|
read_pos += read;
|
||||||
bits_left -= read;
|
bits_left -= read;
|
||||||
}
|
}
|
||||||
|
|
||||||
value
|
Ok(partial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_i8(&self, position: usize, count: usize) -> i8 {
|
pub fn read_signed<T>(&self, position: usize, count: usize) -> Result<T>
|
||||||
let unsigned = self.read_u8(position, count);
|
where T: PrimInt + BitOrAssign + Signed
|
||||||
make_signed!(unsigned, i8, count)
|
{
|
||||||
|
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 {
|
pub fn read_bytes(&self, position: usize, byte_count: usize) -> Result<Vec<u8>> {
|
||||||
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> {
|
|
||||||
let mut data = vec!();
|
let mut data = vec!();
|
||||||
data.reserve_exact(byte_count);
|
data.reserve_exact(byte_count);
|
||||||
let mut byte_left = byte_count;
|
let mut byte_left = byte_count;
|
||||||
|
|
@ -157,12 +148,12 @@ impl<'a> BitBuffer<'a> {
|
||||||
let mut read_pos = position;
|
let mut read_pos = position;
|
||||||
while byte_left > 0 {
|
while byte_left > 0 {
|
||||||
let read = min(byte_left, max_read);
|
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];
|
let usable_bytes = &bytes[0..max_read];
|
||||||
data.extend_from_slice(usable_bytes);
|
data.extend_from_slice(usable_bytes);
|
||||||
byte_left -= read;
|
byte_left -= read;
|
||||||
read_pos += 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] = &[
|
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,
|
||||||
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]
|
#[test]
|
||||||
fn read_u8() {
|
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>(0, 1).unwrap(), 0b1);
|
||||||
assert_eq!(buffer.read_u8(1, 1), 0b0);
|
assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0);
|
||||||
assert_eq!(buffer.read_u8(2, 2), 0b01);
|
assert_eq!(buffer.read::<u8>(2, 2).unwrap(), 0b01);
|
||||||
assert_eq!(buffer.read_u8(7, 5), 0b1010_1);
|
assert_eq!(buffer.read::<u8>(7, 5).unwrap(), 0b1010_1);
|
||||||
assert_eq!(buffer.read_u8(6, 5), 0b010_10);
|
assert_eq!(buffer.read::<u8>(6, 5).unwrap(), 0b010_10);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_u16() {
|
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]
|
#[test]
|
||||||
fn read_u32() {
|
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]
|
#[test]
|
||||||
fn read_u64() {
|
fn read_u64() {
|
||||||
let buffer = BitBuffer::from_padded_slice(BYTES, 8);
|
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||||
|
|
||||||
|
assert_eq!(buffer.read::<u64>(6, 34).unwrap(), 0b1001_1001_1001_1001_1010_1100_0110_1010_10);
|
||||||
println!("{:#036b}", buffer.read_u64(6, 32));
|
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, 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_perf(buffer: BitBuffer) -> u16 {
|
fn read_perf(buffer: BitBuffer) -> u16 {
|
||||||
|
|
@ -53,7 +52,7 @@ fn read_perf(buffer: BitBuffer) -> u16 {
|
||||||
if pos + size > len {
|
if pos + size > len {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
let data = buffer.read_u16(pos, size);
|
let data = buffer.read::<u16>(pos, size).unwrap();
|
||||||
result = result.wrapping_add(data);
|
result = result.wrapping_add(data);
|
||||||
pos += size;
|
pos += size;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue