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

read usize at once

This commit is contained in:
Robin Appelman 2019-02-13 13:13:36 +01:00
commit 741f987833
2 changed files with 66 additions and 145 deletions

View file

@ -2,17 +2,11 @@
extern crate test; extern crate test;
use std::cmp::min; use std::mem::size_of;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ByteOrder {
LittleEndian,
BigEndian,
}
#[derive(Debug, PartialEq, Copy, Clone)] #[derive(Debug, PartialEq, Copy, Clone)]
pub enum ReadError { pub enum ReadError {
TooManyBits { TooManyBits {
@ -27,74 +21,35 @@ pub enum ReadError {
pub type Result<T> = std::result::Result<T, ReadError>; pub type Result<T> = std::result::Result<T, ReadError>;
pub struct BitBuffer<'a> { pub struct BitBuffer {
bytes: &'a [u8], bytes: Vec<u8>,
order: ByteOrder,
bit_len: usize, bit_len: usize,
byte_len: usize,
} }
macro_rules! bitreader_unsigned_be { macro_rules! array_ref {
($buffer:expr, $type:ty, $position:expr, $count:expr) => { ($arr:expr, $offset:expr, $len:expr) => {{
{ {
let size = std::mem::size_of::<$type>() * 8; #[inline]
if $count > size { unsafe fn as_array<T>(slice: &[T]) -> &[T; $len] {
return Err(ReadError::TooManyBits { requested: $count, max: size }); &*(slice.as_ptr() as *const [_; $len])
} }
let offset = $offset;
let bits_left = $buffer.bit_len() - $position; let slice = & $arr[offset..offset + $len];
#[allow(unused_unsafe)]
if $count > bits_left { unsafe {
return Err(ReadError::NotEnoughData { requested: $count, bits_left}); as_array(slice)
}
let mut value: $type = 0;
//let mut i = 0;
//let mut offset = $position;
// while i < $count {
// let remaining = $count - i;
// let bit_offset = (offset & 7) as u8;
// let byte_index = (offset / 8) as usize;
// let byte = $buffer.bytes[byte_index];
//
// // how much can we read from the current byte
// let read = min(remaining, 8 - bit_offset);
//
// println!("{}, {}", remaining, bit_offset);
// println!("read {} bits from {:#010b}", read, byte);
//
// let mask = !(0xFFu8.wrapping_shl(read as u32));
// let shift = 8 - read - bit_offset;
// let shifted = byte.wrapping_shr(shift as u32);
// let read_bits = shifted & mask;
// println!("{:#010b} >> {} = {:#010b}", byte, shift, shifted);
// println!("{:#010b} & {:#010b} = {:#010b}", mask, shifted, read_bits);
//
// println!("{:#010b} << {} | {:#010b}", value, read, read_bits);
// value = value << read | read_bits as $type;
//
// offset += read as usize;
// i += read;
// }
for i in $position..($position + $count as usize ) {
let byte_index = (i / 8) as usize;
let byte = $buffer.bytes[byte_index];
let shift = 7 - (i % 8);
let bit = (byte >> shift) as $type & 1;
value = (value << 1) | bit;
}
Ok(value)
} }
} }
}}
} }
const USIZE_SIZE: usize = std::mem::size_of::<usize>();
macro_rules! bitreader_unsigned_le { macro_rules! bitreader_unsigned_le {
($buffer:expr, $type:ty, $position:expr, $count:expr) => { ($buffer:expr, $type:ty, $position:expr, $count:expr) => {
{ {
let size = std::mem::size_of::<$type>() * 8; let size: usize = size_of::<$type>() * 8;
if $count > size { if $count > size {
return Err(ReadError::TooManyBits { requested: $count, max: size }); return Err(ReadError::TooManyBits { requested: $count, max: size });
} }
@ -105,33 +60,18 @@ macro_rules! bitreader_unsigned_le {
return Err(ReadError::NotEnoughData { requested: $count, bits_left}); return Err(ReadError::NotEnoughData { requested: $count, bits_left});
} }
let mut value: $type = 0; let byte_index = $position / 8;
let bit_offset = $position & 7;
let bytes:&[u8; USIZE_SIZE] = array_ref!($buffer.bytes(), byte_index, USIZE_SIZE);
let container_le = unsafe {
std::mem::transmute::<[u8; USIZE_SIZE], usize>(*bytes)
};
let container = usize::from_le(container_le);
let shifted = container >> bit_offset;
let mask = if $count == (USIZE_SIZE * 8) {usize::max_value()} else {!(usize::max_value() << $count)};
let value = shifted & mask;
let mut i = 0; Ok(value as $type)
let mut offset = $position;
while i < $count {
let remaining = $count - i;
let bit_offset = offset & 7;
let byte_index = offset / 8;
let byte = $buffer.bytes[byte_index];
// how much can we read from the current byte
let read = min(remaining, 8 - bit_offset);
//let mask = if read == 8 {0xFFu8} else {!(0xFFu8 << read)};
let mask = if read == 8 {0xFFu8} else {!(0xFFu8 << read)};
let shift = bit_offset;
let shifted = byte >> shift;
let read_bits = shifted & mask;
value |= read_bits as $type << i;
offset += read as usize;
i += read;
}
Ok(value)
} }
} }
} }
@ -147,40 +87,51 @@ macro_rules! make_signed {
} }
} }
impl<'a> BitBuffer<'a> { impl BitBuffer {
/// Construct a new BitBuffer from a byte slice. pub fn from_slice(data: &[u8]) -> BitBuffer {
pub fn new(bytes: &'a [u8], order: ByteOrder) -> BitBuffer<'a> { let mut bytes = vec![];
BitBuffer { bytes.extend_from_slice(data);
bytes, BitBuffer::from_vec(bytes)
order,
bit_len: bytes.len() * 8,
} }
pub fn from_vec(mut bytes: Vec<u8>) -> BitBuffer {
let byte_len = bytes.len();
// add leading 0 bytes for overflow during reading
bytes.resize(byte_len + size_of::<usize>(), 0);
BitBuffer::from_padded_vec(&bytes, byte_len)
}
pub fn from_padded_vec(bytes: &Vec<u8>, byte_len: usize) -> BitBuffer {
BitBuffer {
bytes: bytes.to_vec(),
bit_len: byte_len * 8,
byte_len,
}
}
fn bytes(&self) -> &[u8] {
self.bytes.as_slice()
} }
pub fn bit_len(&self) -> usize { pub fn bit_len(&self) -> usize {
self.bit_len self.bit_len
} }
pub fn read_u8(&self, position: usize, count: usize) -> Result<u8> { pub fn byte_len(&self) -> usize {
match self.order { self.byte_len
ByteOrder::LittleEndian => bitreader_unsigned_le!(self, u8, position, count),
ByteOrder::BigEndian => bitreader_unsigned_be!(self, u8, position, count)
} }
pub fn read_u8(&self, position: usize, count: usize) -> Result<u8> {
self.bytes.
bitreader_unsigned_le!(self, u8, position, count)
} }
pub fn read_u16(&self, position: usize, count: usize) -> Result<u16> { pub fn read_u16(&self, position: usize, count: usize) -> Result<u16> {
bitreader_unsigned_le!(self, u16, position, count) bitreader_unsigned_le!(self, u16, position, count)
// match self.order {
// ByteOrder::LittleEndian => bitreader_unsigned_le!(self, u16, position, count),
// ByteOrder::BigEndian => bitreader_unsigned_be!(self, u16, position, count)
// }
} }
pub fn read_u32(&self, position: usize, count: usize) -> Result<u32> { pub fn read_u32(&self, position: usize, count: usize) -> Result<u32> {
match self.order { bitreader_unsigned_le!(self, u32, position, count)
ByteOrder::LittleEndian => bitreader_unsigned_le!(self, u32, position, count),
ByteOrder::BigEndian => bitreader_unsigned_be!(self, u32, position, count)
}
} }
pub fn read_i8(&self, position: usize, count: usize) -> Result<i8> { pub fn read_i8(&self, position: usize, count: usize) -> Result<i8> {

View file

@ -2,30 +2,14 @@ use std::fs;
use super::*; use super::*;
use test::Bencher; use test::Bencher;
#[test]
fn read_be() {
let bytes = &[
0b1011_0101, 0b0110_1010, 0b1010_1100, 0b1001_1001,
0b1001_1001, 0b1001_1001, 0b1001_1001, 0b1110_0111,
];
let buffer = BitBuffer::new(bytes, ByteOrder::BigEndian);
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(7, 4).unwrap(), 0b1011);
assert_eq!(buffer.read_u8(6, 5).unwrap(), 0b01011);
}
#[test] #[test]
fn read_le() { fn read_le() {
let bytes = &[ let bytes: &[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,
]; ];
let buffer = BitBuffer::new(bytes, ByteOrder::LittleEndian); let buffer = BitBuffer::from_slice(&bytes);
assert_eq!(buffer.read_u8(0, 1).unwrap(), 0b1); assert_eq!(buffer.read_u8(0, 1).unwrap(), 0b1);
assert_eq!(buffer.read_u8(1, 1).unwrap(), 0b0); assert_eq!(buffer.read_u8(1, 1).unwrap(), 0b0);
@ -35,27 +19,11 @@ fn read_le() {
assert_eq!(buffer.read_u16(6, 12).unwrap(), 0b000110101010); assert_eq!(buffer.read_u16(6, 12).unwrap(), 0b000110101010);
} }
#[test]
fn signed_values() {
let from = -2048;
let to = 2048;
for x in from..to {
let bytes = &[
(x >> 8) as u8,
x as u8,
];
let buffer = BitBuffer::new(bytes, ByteOrder::BigEndian);
assert_eq!(buffer.read_u8(0, 4).unwrap(), if x < 0 { 0b1111 } else { 0 });
assert_eq!(buffer.read_i16(4, 12).unwrap(), x);
}
}
fn read_perf(buffer: BitBuffer) -> u16 { fn read_perf(buffer: BitBuffer) -> u16 {
let size = 5; let size = 5;
let mut pos = 0; let mut pos = 0;
let len = buffer.bit_len(); let len = buffer.bit_len();
let mut result: u16 = 0; let mut result: u16 = 0;
//while pos < len {
loop { loop {
if pos + size > len { if pos + size > len {
return result; return result;
@ -64,15 +32,17 @@ fn read_perf(buffer: BitBuffer) -> u16 {
result = result.wrapping_add(data); result = result.wrapping_add(data);
pos += size; pos += size;
} }
return result;
} }
#[bench] #[bench]
fn perf(b: &mut Bencher) { fn perf(b: &mut Bencher) {
let data = fs::read("/bulk/tmp/test.dem").expect("Unable to read file"); let mut file = fs::read("/bulk/tmp/test.dem").expect("Unable to read file");
let length = file.len();
file.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
b.iter(|| { b.iter(|| {
let buffer = BitBuffer::new(data.as_slice(), ByteOrder::LittleEndian); let buffer = BitBuffer::from_padded_vec(&file, length);
let data = read_perf(buffer); let data = read_perf(buffer);
assert_eq!(data, 43943);
test::black_box(data); test::black_box(data);
}); });
} }