1
0
Fork 0
mirror of https://codeberg.org/icewind/bitbuffer.git synced 2026-06-04 00:54:07 +02:00

remove padded optimization for the sake of api cleanness

This commit is contained in:
Robin Appelman 2019-02-27 16:21:04 +01:00
commit eee2cf5c43
3 changed files with 36 additions and 102 deletions

View file

@ -48,37 +48,21 @@ impl IsPadded for Padded {
/// ]; /// ];
/// let buffer = BitBuffer::new(bytes.to_vec(), LittleEndian); /// let buffer = BitBuffer::new(bytes.to_vec(), LittleEndian);
/// ``` /// ```
/// pub struct BitBuffer<E>
/// You can also provide a slice padded with at least `size_of::<usize>() - 1` bytes,
/// when the input slice is padded, the BitBuffer can use some optimizations which result in a ~1.5 time performance increase
///
/// ```
/// 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(bytes.to_vec(), 8, LittleEndian);
/// ```
pub struct BitBuffer<E, S>
where where
E: Endianness, E: Endianness,
S: IsPadded,
{ {
bytes: Vec<u8>, bytes: Vec<u8>,
bit_len: usize, bit_len: usize,
byte_len: usize, byte_len: usize,
endianness: PhantomData<E>, endianness: PhantomData<E>,
is_padded: PhantomData<S>,
} }
impl<E> BitBuffer<E, NonPadded> impl<E> BitBuffer<E>
where where
E: Endianness, E: Endianness,
{ {
/// Create a new BitBuffer from a byte slice /// Create a new BitBuffer from a byte vector
/// ///
/// # Examples /// # Examples
/// ///
@ -98,57 +82,13 @@ where
byte_len, byte_len,
bit_len: byte_len * 8, bit_len: byte_len * 8,
endianness: PhantomData, endianness: PhantomData,
is_padded: PhantomData,
} }
} }
} }
impl<E> BitBuffer<E, Padded> impl<E> BitBuffer<E>
where where
E: Endianness, 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(bytes.to_vec(), 8, LittleEndian);
/// ```
pub fn from_padded(bytes: Vec<u8>, byte_len: usize, _endianness: E) -> Self {
if bytes.len() < byte_len + USIZE_SIZE - 1 {
panic!(
"not enough padding bytes, {} required, {} provided",
USIZE_SIZE - 1,
byte_len - bytes.len()
)
}
BitBuffer {
bytes,
byte_len,
bit_len: byte_len * 8,
endianness: PhantomData,
is_padded: PhantomData,
}
}
}
impl<E, S> BitBuffer<E, S>
where
E: Endianness,
S: IsPadded,
{ {
/// The available number of bits in the buffer /// The available number of bits in the buffer
pub fn bit_len(&self) -> usize { pub fn bit_len(&self) -> usize {
@ -167,11 +107,7 @@ where
bits_left: self.bit_len - position, bits_left: self.bit_len - position,
}); });
} }
let byte_index = if S::is_padded() { let byte_index = min(position / 8, self.byte_len - USIZE_SIZE);
position / 8
} else {
min(position / 8, self.byte_len - USIZE_SIZE)
};
let bit_offset = position - byte_index * 8; let bit_offset = position - byte_index * 8;
let raw_container: &usize = unsafe { let raw_container: &usize = unsafe {
// this is safe here because it's already verified that there is enough data in the slice // this is safe here because it's already verified that there is enough data in the slice

View file

@ -1,16 +1,16 @@
use crate::{BitStream, Endianness, IsPadded, Result}; use crate::{BitStream, Endianness, Result};
/// Trait for types that can be read from a stream without requiring the size to be configured /// Trait for types that can be read from a stream without requiring the size to be configured
pub trait Read<E: Endianness, P: IsPadded>: Sized { pub trait Read<E: Endianness>: Sized {
/// Read the type from stream /// Read the type from stream
fn read(stream: &mut BitStream<E, P>) -> Result<Self>; fn read(stream: &mut BitStream<E>) -> Result<Self>;
} }
macro_rules! impl_read_int { macro_rules! impl_read_int {
($type:ty, $len:expr) => { ($type:ty, $len:expr) => {
impl<E: Endianness, P: IsPadded> Read<E, P> for $type { impl<E: Endianness> Read<E> for $type {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>) -> Result<$type> { fn read(stream: &mut BitStream<E>) -> Result<$type> {
stream.read_int::<$type>($len) stream.read_int::<$type>($len)
} }
} }
@ -28,45 +28,45 @@ impl_read_int!(i32, 32);
impl_read_int!(i64, 64); impl_read_int!(i64, 64);
impl_read_int!(i128, 128); impl_read_int!(i128, 128);
impl<E: Endianness, P: IsPadded> Read<E, P> for f32 { impl<E: Endianness> Read<E> for f32 {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>) -> Result<f32> { fn read(stream: &mut BitStream<E>) -> Result<f32> {
stream.read_float::<f32>() stream.read_float::<f32>()
} }
} }
impl<E: Endianness, P: IsPadded> Read<E, P> for f64 { impl<E: Endianness> Read<E> for f64 {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>) -> Result<f64> { fn read(stream: &mut BitStream<E>) -> Result<f64> {
stream.read_float::<f64>() stream.read_float::<f64>()
} }
} }
impl<E: Endianness, P: IsPadded> Read<E, P> for bool { impl<E: Endianness> Read<E> for bool {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>) -> Result<bool> { fn read(stream: &mut BitStream<E>) -> Result<bool> {
stream.read_bool() stream.read_bool()
} }
} }
impl<E: Endianness, P: IsPadded> Read<E, P> for String { impl<E: Endianness> Read<E> for String {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>) -> Result<String> { fn read(stream: &mut BitStream<E>) -> Result<String> {
stream.read_string(None) stream.read_string(None)
} }
} }
/// Trait for types that can be read from a stream wit requiring the size to be configured /// Trait for types that can be read from a stream wit requiring the size to be configured
pub trait ReadSized<E: Endianness, P: IsPadded>: Sized { pub trait ReadSized<E: Endianness>: Sized {
/// Read the type from stream /// Read the type from stream
fn read(stream: &mut BitStream<E, P>, size: usize) -> Result<Self>; fn read(stream: &mut BitStream<E>, size: usize) -> Result<Self>;
} }
macro_rules! impl_read_int_sized { macro_rules! impl_read_int_sized {
($type:ty) => { ($type:ty) => {
impl<E: Endianness, P: IsPadded> ReadSized<E, P> for $type { impl<E: Endianness> ReadSized<E> for $type {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>, size: usize) -> Result<$type> { fn read(stream: &mut BitStream<E>, size: usize) -> Result<$type> {
stream.read_int::<$type>(size) stream.read_int::<$type>(size)
} }
} }
@ -84,16 +84,16 @@ impl_read_int_sized!(i32);
impl_read_int_sized!(i64); impl_read_int_sized!(i64);
impl_read_int_sized!(i128); impl_read_int_sized!(i128);
impl<E: Endianness, P: IsPadded> ReadSized<E, P> for String { impl<E: Endianness> ReadSized<E> for String {
#[inline(always)] #[inline(always)]
fn read(stream: &mut BitStream<E, P>, size: usize) -> Result<String> { fn read(stream: &mut BitStream<E>, size: usize) -> Result<String> {
stream.read_string(Some(size)) stream.read_string(Some(size))
} }
} }
/// Read a boolean, if true, read the value, else return None /// Read a boolean, if true, read the value, else return None
impl<E: Endianness, P: IsPadded, T: Read<E, P>> Read<E, P> for Option<T> { impl<E: Endianness, T: Read<E>> Read<E> for Option<T> {
fn read(stream: &mut BitStream<E, P>) -> Result<Self> { fn read(stream: &mut BitStream<E>) -> Result<Self> {
if stream.read()? { if stream.read()? {
Ok(Some(stream.read()?)) Ok(Some(stream.read()?))
} else { } else {
@ -102,8 +102,8 @@ impl<E: Endianness, P: IsPadded, T: Read<E, P>> Read<E, P> for Option<T> {
} }
} }
impl<E: Endianness, P: IsPadded, T: Read<E, P>> ReadSized<E, P> for Vec<T> { impl<E: Endianness, T: Read<E>> ReadSized<E> for Vec<T> {
fn read(stream: &mut BitStream<E, P>, size: usize) -> Result<Self> { fn read(stream: &mut BitStream<E>, size: usize) -> Result<Self> {
let mut vec = Vec::with_capacity(size); let mut vec = Vec::with_capacity(size);
for _ in 0..size { for _ in 0..size {
vec.push(stream.read()?) vec.push(stream.read()?)
@ -113,9 +113,9 @@ impl<E: Endianness, P: IsPadded, T: Read<E, P>> ReadSized<E, P> for Vec<T> {
} }
// Once we have something like https://github.com/rust-lang/rfcs/issues/1053 we can do this optimization // Once we have something like https://github.com/rust-lang/rfcs/issues/1053 we can do this optimization
//impl<E: Endianness, P: IsPadded> ReadSized<E, P> for Vec<u8> { //impl<E: Endianness> ReadSized<E> for Vec<u8> {
// #[inline(always)] // #[inline(always)]
// fn read(stream: &mut BitStream<E, P>, size: usize) -> Result<Self> { // fn read(stream: &mut BitStream<E>, size: usize) -> Result<Self> {
// stream.read_bytes(size) // stream.read_bytes(size)
// } // }
//} //}

View file

@ -24,21 +24,19 @@ use crate::{Read, ReadError, ReadSized, Result};
/// let buffer = BitBuffer::new(bytes.to_vec(), LittleEndian); /// let buffer = BitBuffer::new(bytes.to_vec(), LittleEndian);
/// let mut stream = BitStream::new(buffer, None, None); /// let mut stream = BitStream::new(buffer, None, None);
/// ``` /// ```
pub struct BitStream<E, S> pub struct BitStream<E>
where where
E: Endianness, E: Endianness,
S: IsPadded,
{ {
buffer: Rc<BitBuffer<E, S>>, buffer: Rc<BitBuffer<E>>,
start_pos: usize, start_pos: usize,
pos: usize, pos: usize,
bit_len: usize, bit_len: usize,
} }
impl<E, S> BitStream<E, S> impl<E> BitStream<E>
where where
E: Endianness, E: Endianness,
S: IsPadded,
{ {
/// Create a new stream for a buffer /// Create a new stream for a buffer
/// ///
@ -59,7 +57,7 @@ where
/// let buffer = BitBuffer::new(bytes.to_vec(), LittleEndian); /// let buffer = BitBuffer::new(bytes.to_vec(), LittleEndian);
/// let mut stream = BitStream::new(buffer, None, None); /// let mut stream = BitStream::new(buffer, None, None);
/// ``` /// ```
pub fn new(buffer: BitBuffer<E, S>, start_pos: Option<usize>, bit_len: Option<usize>) -> Self { pub fn new(buffer: BitBuffer<E>, start_pos: Option<usize>, bit_len: Option<usize>) -> Self {
let buffer_len = buffer.bit_len(); let buffer_len = buffer.bit_len();
let start = start_pos.unwrap_or_default(); let start = start_pos.unwrap_or_default();
if start > buffer_len { if start > buffer_len {
@ -413,12 +411,12 @@ where
} }
/// Read a value based on the provided type /// Read a value based on the provided type
pub fn read<T: Read<E, S>>(&mut self) -> Result<T> { pub fn read<T: Read<E>>(&mut self) -> Result<T> {
T::read(self) T::read(self)
} }
/// Read a value based on the provided type and size /// Read a value based on the provided type and size
pub fn read_sized<T: ReadSized<E, S>>(&mut self, size: usize) -> Result<T> { pub fn read_sized<T: ReadSized<E>>(&mut self, size: usize) -> Result<T> {
T::read(self, size) T::read(self, size)
} }
} }