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

add padded version of reader

This commit is contained in:
Robin Appelman 2019-02-22 22:19:04 +01:00
commit a26695861e
3 changed files with 135 additions and 44 deletions

View file

@ -5,10 +5,10 @@ pub trait Endianness {
}
/// Trait for specifying that the bit buffer is big endian
pub struct BigEndian {}
pub struct BigEndian;
/// Trait for specifying that the bit buffer is little endian
pub struct LittleEndian {}
pub struct LittleEndian;
macro_rules! impl_endianness {
($type:ty, $le:expr) => {

View file

@ -5,8 +5,8 @@
extern crate test;
use endianness::Endianness;
pub use endianness::{BigEndian, LittleEndian};
use endianness::Endianness;
use is_signed::IsSigned;
use num_traits::{Float, PrimInt};
use std::cmp::min;
@ -40,25 +40,52 @@ pub enum ReadError {
},
}
/// Mark source slice as not including padding
pub struct NonPadded;
/// Mark source slice as including padding
pub struct Padded;
/// Determine whether or not the source slice is padded
pub trait MaybePaddedSlice {
/// Whether or not the slice is padded
fn is_padded() -> bool;
}
impl MaybePaddedSlice for NonPadded {
#[inline]
fn is_padded() -> bool {
false
}
}
impl MaybePaddedSlice for Padded {
#[inline]
fn is_padded() -> bool {
true
}
}
/// Either the read bits in the requested format or a [`ReadError`](enum.ReadError.html)
pub type Result<T> = std::result::Result<T, ReadError>;
/// Buffer that allows reading integers of arbitrary bit length and non byte-aligned integers
///
/// The endianness used when reading from the buffer is specified as type parameter
pub struct BitBuffer<'a, E>
where
E: Endianness,
pub struct BitBuffer<'a, E, S>
where
E: Endianness,
S: MaybePaddedSlice
{
bytes: &'a [u8],
bit_len: usize,
byte_len: usize,
endianness: PhantomData<E>,
is_padded: PhantomData<S>,
}
impl<'a, E> BitBuffer<'a, E>
where
E: Endianness,
impl<'a, E> BitBuffer<'a, E, NonPadded>
where
E: Endianness
{
/// Create a new BitBuffer from a byte slice
///
@ -71,18 +98,63 @@ where
/// 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
/// 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111
/// ];
/// let buffer: BitBuffer<LittleEndian> = BitBuffer::new(bytes);
/// let buffer = BitBuffer::new(bytes, LittleEndian);
/// ```
pub fn new(bytes: &'a [u8]) -> Self {
pub fn new(bytes: &'a [u8], _endianness: E) -> Self {
let byte_len = bytes.len();
BitBuffer {
bytes,
byte_len,
bit_len: byte_len * 8,
endianness: PhantomData,
is_padded: PhantomData,
}
}
}
impl<'a, E> BitBuffer<'a, E, Padded>
where
E: Endianness
{
/// Create a new BitBuffer from a byte slice with included padding
///
/// by including at least `size_of::<usize>() - 1` bytes of padding reading can be further optimized
///
/// # Panics
///
/// Panics if not enough bytes of padding are included
///
/// # Examples
///
/// ```
/// use bitstream_reader::{BitBuffer, LittleEndian};
///
/// let bytes: &[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
/// ];
/// let buffer = BitBuffer::from_padded_slice(bytes, 8, LittleEndian);
/// ```
pub fn from_padded_slice(bytes: &'a [u8], byte_len: usize, _endianness: E) -> Self {
if bytes.len() < byte_len + USIZE_SIZE - 1 {
panic!("not enough padding bytes, {} required", USIZE_SIZE - 1)
}
BitBuffer {
bytes,
byte_len,
bit_len: byte_len * 8,
endianness: PhantomData,
is_padded: PhantomData,
}
}
}
impl<'a, E, S> BitBuffer<'a, E, S>
where
E: Endianness,
S: MaybePaddedSlice
{
/// The available number of bits in the buffer
pub fn bit_len(&self) -> usize {
self.bit_len
@ -100,7 +172,12 @@ where
bits_left: self.bit_len - position,
});
}
let byte_index = min(position / 8, self.byte_len - USIZE_SIZE);
let byte_index = if S::is_padded() {
position / 8
} else {
min(position / 8, self.byte_len - USIZE_SIZE)
};
//let byte_index = position / 8;
let bit_offset = position - byte_index * 8;
let slice = &self.bytes[byte_index..byte_index + USIZE_SIZE];
let bytes: [u8; USIZE_SIZE] = unsafe { *(slice.as_ptr() as *const [u8; USIZE_SIZE]) };
@ -133,7 +210,7 @@ where
/// 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
/// 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111
/// ];
/// let buffer: BitBuffer<LittleEndian> = BitBuffer::new(bytes);
/// let buffer = BitBuffer::new(bytes, LittleEndian);
/// let result = buffer.read_bool(5).unwrap();
/// assert_eq!(result, true);
/// ```
@ -169,13 +246,13 @@ where
/// 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
/// 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111
/// ];
/// let buffer: BitBuffer<LittleEndian> = BitBuffer::new(bytes);
/// let buffer = BitBuffer::new(bytes, LittleEndian);
/// let result = buffer.read::<u16>(10, 9).unwrap();
/// assert_eq!(result, 0b100_0110_10);
/// ```
pub fn read<T>(&self, position: usize, count: usize) -> Result<T>
where
T: PrimInt + BitOrAssign + IsSigned,
where
T: PrimInt + BitOrAssign + IsSigned,
{
let value = {
let type_bit_size = size_of::<T>() * 8;
@ -247,7 +324,7 @@ where
/// 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
/// 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111
/// ];
/// let buffer: BitBuffer<LittleEndian> = BitBuffer::new(bytes);
/// let buffer = BitBuffer::new(bytes, LittleEndian);
/// let bytes = buffer.read_bytes(5, 3).unwrap();
/// assert_eq!(bytes, &[0b0_1010_101, 0b0_1100_011, 0b1_1001_101]);
/// ```
@ -284,12 +361,12 @@ where
/// 0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
/// 0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111
/// ];
/// let buffer: BitBuffer<LittleEndian> = BitBuffer::new(bytes);
/// let buffer = BitBuffer::new(bytes, LittleEndian);
/// let result = buffer.read_float::<f32>(10).unwrap();
/// ```
pub fn read_float<T>(&self, position: usize) -> Result<T>
where
T: Float,
where
T: Float,
{
if size_of::<T>() == 4 {
let int = self.read::<u32>(position, 32)?;

View file

@ -1,5 +1,5 @@
use super::*;
use std::fs;
use super::*;
use test::Bencher;
const BYTES: &'static [u8] = &[
@ -19,7 +19,7 @@ const BYTES: &'static [u8] = &[
#[test]
fn read_u8_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(buffer.read::<u8>(0, 1).unwrap(), 0b1);
assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0);
@ -31,7 +31,7 @@ fn read_u8_le() {
#[test]
fn read_u8_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(buffer.read::<u8>(0, 1).unwrap(), 0b1);
assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0);
@ -43,21 +43,21 @@ fn read_u8_be() {
#[test]
fn read_u16_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(buffer.read::<u16>(6, 12).unwrap(), 0b00_0110_1010_10);
}
#[test]
fn read_u16_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(buffer.read::<u16>(6, 12).unwrap(), 0b01_0110_1010_10);
}
#[test]
fn read_u32_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(
buffer.read::<u32>(6, 24).unwrap(),
@ -67,7 +67,7 @@ fn read_u32_le() {
#[test]
fn read_u32_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(
buffer.read::<u32>(6, 24).unwrap(),
@ -77,7 +77,7 @@ fn read_u32_be() {
#[test]
fn read_u64_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(
buffer.read::<u64>(6, 34).unwrap(),
@ -99,7 +99,7 @@ fn read_u64_le() {
#[test]
fn read_u64_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(
buffer.read::<u64>(6, 34).unwrap(),
@ -117,7 +117,7 @@ fn read_u64_be() {
#[test]
fn read_i8_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(buffer.read::<i8>(0, 3).unwrap(), -0b01);
assert_eq!(buffer.read::<i8>(0, 8).unwrap(), -0b011_0101);
@ -125,7 +125,7 @@ fn read_i8_le() {
#[test]
fn read_i8_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(buffer.read::<i8>(1, 2).unwrap(), 0b1);
assert_eq!(buffer.read::<i8>(0, 3).unwrap(), -0b01);
@ -134,7 +134,7 @@ fn read_i8_be() {
#[test]
fn read_i16_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(buffer.read::<i16>(6, 12).unwrap(), 0b0_0110_1010_10);
assert_eq!(buffer.read::<i16>(6, 13).unwrap(), -0b00_0110_1010_10);
@ -142,7 +142,7 @@ fn read_i16_le() {
#[test]
fn read_i16_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(buffer.read::<i16>(6, 12).unwrap(), 0b1_0110_1010_10);
assert_eq!(buffer.read::<i16>(7, 12).unwrap(), -0b0110_1010_101);
@ -150,7 +150,7 @@ fn read_i16_be() {
#[test]
fn read_i32_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(
buffer.read::<i32>(6, 24).unwrap(),
@ -164,7 +164,7 @@ fn read_i32_le() {
#[test]
fn read_i32_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(
buffer.read::<i32>(7, 24).unwrap(),
@ -174,7 +174,7 @@ fn read_i32_be() {
#[test]
fn read_i64_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(
buffer.read::<i64>(6, 34).unwrap(),
@ -192,7 +192,7 @@ fn read_i64_le() {
#[test]
fn read_i64_be() {
let buffer: BitBuffer<BigEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, BigEndian);
assert_eq!(
buffer.read::<i64>(7, 34).unwrap(),
@ -210,19 +210,19 @@ fn read_i64_be() {
#[test]
fn read_f32_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(buffer.read_float::<f64>(6).unwrap(), 135447455835963910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0);
}
#[test]
fn read_f64_le() {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(BYTES);
let buffer = BitBuffer::new(BYTES, LittleEndian);
assert_eq!(buffer.read_float::<f64>(6).unwrap(), 135447455835963910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0);
}
fn read_perf<E: Endianness>(buffer: BitBuffer<E>) -> u16 {
fn read_perf<E: Endianness, S: MaybePaddedSlice>(buffer: BitBuffer<E, S>) -> u16 {
let size = 5;
let mut pos = 0;
let len = buffer.bit_len();
@ -238,11 +238,25 @@ fn read_perf<E: Endianness>(buffer: BitBuffer<E>) -> u16 {
}
#[bench]
fn perf(b: &mut Bencher) {
let file = fs::read("/bulk/tmp/test.dem").expect("Unable to read file");
fn perf_padded(b: &mut Bencher) {
let mut file = fs::read("/bulk/tmp/test.dem").expect("Unable to read file");
let len = file.len();
file.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
let bytes = file.as_slice();
b.iter(|| {
let buffer: BitBuffer<LittleEndian> = BitBuffer::new(&bytes);
let buffer = BitBuffer::from_padded_slice(&bytes, len, LittleEndian);
let data = read_perf(buffer);
assert_eq!(data, 43943);
test::black_box(data);
});
}
#[bench]
fn perf_non_padded(b: &mut Bencher) {
let file = fs::read("/bulk/tmp/test.dem").expect("Unable to read file");
let bytes = file.as_slice();
b.iter(|| {
let buffer = BitBuffer::new(&bytes, LittleEndian);
let data = read_perf(buffer);
assert_eq!(data, 43943);
test::black_box(data);