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:
parent
8d38830adc
commit
741f987833
2 changed files with 66 additions and 145 deletions
165
src/lib.rs
165
src/lib.rs
|
|
@ -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![];
|
||||||
|
bytes.extend_from_slice(data);
|
||||||
|
BitBuffer::from_vec(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
BitBuffer {
|
||||||
bytes,
|
bytes: bytes.to_vec(),
|
||||||
order,
|
bit_len: byte_len * 8,
|
||||||
bit_len: bytes.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 byte_len(&self) -> usize {
|
||||||
|
self.byte_len
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_u8(&self, position: usize, count: usize) -> Result<u8> {
|
pub fn read_u8(&self, position: usize, count: usize) -> Result<u8> {
|
||||||
match self.order {
|
self.bytes.
|
||||||
ByteOrder::LittleEndian => bitreader_unsigned_le!(self, u8, position, count),
|
bitreader_unsigned_le!(self, u8, position, count)
|
||||||
ByteOrder::BigEndian => bitreader_unsigned_be!(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> {
|
||||||
|
|
|
||||||
44
src/tests.rs
44
src/tests.rs
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue