mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 08:34:07 +02:00
add big endian
This commit is contained in:
parent
b804b4a59e
commit
c33a9a711b
3 changed files with 166 additions and 42 deletions
32
src/endianness.rs
Normal file
32
src/endianness.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/// Trait for specifying endianness of bit buffer
|
||||
pub trait Endianness {
|
||||
fn is_le() -> bool;
|
||||
fn is_be() -> bool;
|
||||
}
|
||||
|
||||
/// Trait for specifying that the bit buffer is big endian
|
||||
pub struct BigEndian {
|
||||
}
|
||||
|
||||
/// Trait for specifying that the bit buffer is little endian
|
||||
pub struct LittleEndian {
|
||||
}
|
||||
|
||||
macro_rules! impl_endianness {
|
||||
($type:ty, $le:expr) => {
|
||||
impl Endianness for $type {
|
||||
#[inline]
|
||||
fn is_le() -> bool {
|
||||
$le
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_be() -> bool {
|
||||
!$le
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_endianness!(BigEndian, false);
|
||||
impl_endianness!(LittleEndian, true);
|
||||
62
src/lib.rs
62
src/lib.rs
|
|
@ -5,15 +5,22 @@
|
|||
|
||||
extern crate test;
|
||||
|
||||
use endianness::Endianness;
|
||||
use is_signed::IsSigned;
|
||||
use num_traits::{Float, PrimInt};
|
||||
use std::cmp::min;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::size_of;
|
||||
use std::ops::BitOrAssign;
|
||||
|
||||
pub use endianness::{LittleEndian, BigEndian};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod is_signed;
|
||||
mod endianness;
|
||||
|
||||
const USIZE_SIZE: usize = size_of::<usize>();
|
||||
|
||||
/// Errors that can be returned when trying to read from a buffer
|
||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||
|
|
@ -38,15 +45,17 @@ pub enum ReadError {
|
|||
pub type Result<T> = std::result::Result<T, ReadError>;
|
||||
|
||||
/// Buffer that allows reading integers of arbitrary bit length and non byte-aligned integers
|
||||
pub struct BitBuffer<'a> {
|
||||
///
|
||||
/// The endianness used when reading from the buffer is specified as type parameter
|
||||
pub struct BitBuffer<'a, E>
|
||||
where E: Endianness {
|
||||
bytes: &'a [u8],
|
||||
bit_len: usize,
|
||||
byte_len: usize,
|
||||
endianness: PhantomData<E>,
|
||||
}
|
||||
|
||||
const USIZE_SIZE: usize = size_of::<usize>();
|
||||
|
||||
impl<'a> BitBuffer<'a> {
|
||||
impl<'a, E> BitBuffer<'a, E> where E: Endianness {
|
||||
/// Create a new BitBuffer from a byte slice with included padding
|
||||
///
|
||||
/// The padding is required because the optimized method for reading bits can overshoot the last requested bit
|
||||
|
|
@ -59,18 +68,18 @@ impl<'a> BitBuffer<'a> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bitbuffer::BitBuffer;
|
||||
/// use bitbuffer::{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);
|
||||
/// let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(bytes, 8);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
pub fn from_padded_slice(bytes: &'a [u8], byte_len: usize) -> BitBuffer<'a> {
|
||||
pub fn from_padded_slice(bytes: &'a [u8], byte_len: usize) -> Self {
|
||||
if byte_len > bytes.len() - (USIZE_SIZE - 1) {
|
||||
panic!("Not enough padding on slice, at least {} bytes of padding are required", USIZE_SIZE - 1);
|
||||
}
|
||||
|
|
@ -78,6 +87,7 @@ impl<'a> BitBuffer<'a> {
|
|||
bytes,
|
||||
byte_len,
|
||||
bit_len: byte_len * 8,
|
||||
endianness: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,8 +118,16 @@ impl<'a> BitBuffer<'a> {
|
|||
let bytes: [u8; USIZE_SIZE] = unsafe {
|
||||
*(slice.as_ptr() as *const [u8; USIZE_SIZE])
|
||||
};
|
||||
let container = usize::from_le_bytes(bytes);
|
||||
let shifted = container >> bit_offset;
|
||||
let container = if E::is_le() {
|
||||
usize::from_le_bytes(bytes)
|
||||
} else {
|
||||
usize::from_be_bytes(bytes)
|
||||
};
|
||||
let shifted = if E::is_le() {
|
||||
container >> bit_offset
|
||||
} else {
|
||||
container >> USIZE_SIZE * 8 - bit_offset - count
|
||||
};
|
||||
let mask = !(usize::max_value() << count);
|
||||
Ok(shifted & mask)
|
||||
}
|
||||
|
|
@ -123,14 +141,14 @@ impl<'a> BitBuffer<'a> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bitbuffer::BitBuffer;
|
||||
/// use bitbuffer::{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);
|
||||
/// let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(bytes, 8);
|
||||
/// let result = buffer.read_bool(6);
|
||||
/// ```
|
||||
pub fn read_bool(&self, position: usize) -> Result<bool> {
|
||||
|
|
@ -160,14 +178,14 @@ impl<'a> BitBuffer<'a> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bitbuffer::BitBuffer;
|
||||
/// use bitbuffer::{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);
|
||||
/// let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(bytes, 8);
|
||||
/// let result = buffer.read::<u16>(10, 9);
|
||||
/// ```
|
||||
pub fn read<T>(&self, position: usize, count: usize) -> Result<T>
|
||||
|
|
@ -201,7 +219,13 @@ impl<'a> BitBuffer<'a> {
|
|||
while left_to_read > 0 {
|
||||
let bits_left = self.bit_len - read_pos;
|
||||
let read = min(min(left_to_read, max_read), bits_left);
|
||||
partial |= T::from(self.read_usize(read_pos, read)?).unwrap() << bit_offset;
|
||||
let data = T::from(self.read_usize(read_pos, read)?).unwrap();
|
||||
if E::is_le() {
|
||||
partial |= data << bit_offset;
|
||||
} else {
|
||||
partial = partial << read;
|
||||
partial |= data;
|
||||
}
|
||||
bit_offset += read;
|
||||
read_pos += read;
|
||||
left_to_read -= read;
|
||||
|
|
@ -230,14 +254,14 @@ impl<'a> BitBuffer<'a> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bitbuffer::BitBuffer;
|
||||
/// use bitbuffer::{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);
|
||||
/// let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(bytes, 8);
|
||||
/// let bytes = buffer.read_bytes(5, 3);
|
||||
/// ```
|
||||
pub fn read_bytes(&self, position: usize, byte_count: usize) -> Result<Vec<u8>> {
|
||||
|
|
@ -267,14 +291,14 @@ impl<'a> BitBuffer<'a> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use bitbuffer::BitBuffer;
|
||||
/// use bitbuffer::{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);
|
||||
/// let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(bytes, 8);
|
||||
/// let result = buffer.read_float::<f32>(10);
|
||||
/// ```
|
||||
pub fn read_float<T>(&self, position: usize) -> Result<T>
|
||||
|
|
@ -284,7 +308,7 @@ impl<'a> BitBuffer<'a> {
|
|||
let int = self.read::<u32>(position, 32)?;
|
||||
Ok(T::from(f32::from_bits(int)).unwrap())
|
||||
} else {
|
||||
let int = self.read::<u64>(position, 64)?;
|
||||
let int = self.read::<u64>(position, 64)?;
|
||||
Ok(T::from(f64::from_bits(int)).unwrap())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
114
src/tests.rs
114
src/tests.rs
|
|
@ -10,8 +10,8 @@ const BYTES: &'static [u8] = &[
|
|||
];
|
||||
|
||||
#[test]
|
||||
fn read_u8() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_u8_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<u8>(0, 1).unwrap(), 0b1);
|
||||
assert_eq!(buffer.read::<u8>(1, 1).unwrap(), 0b0);
|
||||
|
|
@ -22,22 +22,48 @@ fn read_u8() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn read_u16() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_u8_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
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(), 0b11);
|
||||
assert_eq!(buffer.read::<u8>(0, 3).unwrap(), 0b101);
|
||||
assert_eq!(buffer.read::<u8>(7, 5).unwrap(), 0b1011_0);
|
||||
assert_eq!(buffer.read::<u8>(6, 5).unwrap(), 0b01_011);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u16_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<u16>(6, 12).unwrap(), 0b00_0110_1010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u32() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_u16_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<u16>(6, 12).unwrap(), 0b01_0110_1010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u32_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
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, 12);
|
||||
fn read_u32_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<u32>(6, 24).unwrap(), 0b01_0110_1010_1010_1100_1001_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_u64_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
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(), 0b01_1110_0111_1001_1001_1001_1001_1001_1001_1001_1001_1010_1100_0110_1010_10);
|
||||
|
|
@ -45,32 +71,65 @@ fn read_u64() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn read_i8() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_u64_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i8>(0, 3).unwrap(), -0b1);
|
||||
assert_eq!(buffer.read::<u64>(6, 34).unwrap(), 0b01_0110_1010_1010_1100_1001_1001_1001_1001);
|
||||
assert_eq!(buffer.read::<u64>(6, 60).unwrap(), 0b01_0110_1010_1010_1100_1001_1001_1001_1001_1001_1001_1001_1001_1110_0111_10);
|
||||
assert_eq!(buffer.read::<u64>(6, 64).unwrap(), 0b01_0110_1010_1010_1100_1001_1001_1001_1001_1001_1001_1001_1001_1110_0111_1001_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i8_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i8>(0, 3).unwrap(), -0b01);
|
||||
assert_eq!(buffer.read::<i8>(0, 8).unwrap(), -0b011_0101);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i16() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_i8_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i8>(1, 2).unwrap(), 0b1);
|
||||
assert_eq!(buffer.read::<i8>(0, 3).unwrap(), -0b01);
|
||||
assert_eq!(buffer.read::<i8>(0, 8).unwrap(), -0b011_0101);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i16_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i16>(6, 12).unwrap(), 0b0_0110_1010_10);
|
||||
assert_eq!(buffer.read::<i16>(6, 13).unwrap(), -0b00_0110_1010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i32() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_i16_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i16>(6, 12).unwrap(), 0b1_0110_1010_10);
|
||||
assert_eq!(buffer.read::<i16>(7, 12).unwrap(), -0b0110_1010_101);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i32_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i32>(6, 24).unwrap(), 0b1_1001_1010_1100_0110_1010_10);
|
||||
assert_eq!(buffer.read::<i32>(6, 26).unwrap(), -0b001_1001_1010_1100_0110_1010_10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i64() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_i32_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i32>(7, 24).unwrap(), -0b0110_1010_1010_1100_1001_100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_i64_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i64>(6, 34).unwrap(), -0b001_1001_1001_1001_1010_1100_0110_1010_10);
|
||||
assert_eq!(buffer.read::<i64>(6, 59).unwrap(), -0b1110_01111001_1001_1001_1001_1001_1001_1001_1001_1010_1100_0110_1010_10);
|
||||
|
|
@ -78,20 +137,29 @@ fn read_i64() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn read_f32() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_i64_be() {
|
||||
let buffer:BitBuffer<BigEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read::<i64>(7, 34).unwrap(), -0b0110_1010_1010_1100_1001_1001_1001_1001_1);
|
||||
assert_eq!(buffer.read::<i64>(7, 60).unwrap(), -0b0110_1010_1010_1100_1001_1001_1001_1001_1001_1001_1001_1001_1110_0111_100);
|
||||
assert_eq!(buffer.read::<i64>(7, 64).unwrap(), -0b0110_1010_1010_1100_1001_1001_1001_1001_1001_1001_1001_1001_1110_0111_1001_100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_f32_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read_float::<f64>(6).unwrap(), 135447455835963910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_f64() {
|
||||
let buffer = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
fn read_f64_le() {
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(BYTES, 12);
|
||||
|
||||
assert_eq!(buffer.read_float::<f64>(6).unwrap(), 135447455835963910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0);
|
||||
}
|
||||
|
||||
fn read_perf(buffer: BitBuffer) -> u16 {
|
||||
fn read_perf<E: Endianness>(buffer: BitBuffer<E>) -> u16 {
|
||||
let size = 5;
|
||||
let mut pos = 0;
|
||||
let len = buffer.bit_len();
|
||||
|
|
@ -113,7 +181,7 @@ fn perf(b: &mut Bencher) {
|
|||
file.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
let bytes = file.as_slice();
|
||||
b.iter(|| {
|
||||
let buffer = BitBuffer::from_padded_slice(&bytes, length);
|
||||
let buffer:BitBuffer<LittleEndian> = BitBuffer::from_padded_slice(&bytes, length);
|
||||
let data = read_perf(buffer);
|
||||
assert_eq!(data, 43943);
|
||||
test::black_box(data);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue