mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 08:34:07 +02:00
read for NoZero ints
This commit is contained in:
parent
b723dee23b
commit
71823f22ec
6 changed files with 100 additions and 67 deletions
|
|
@ -13,7 +13,6 @@ travis-ci = { repository = "icewind1991/bitstream_reader" }
|
||||||
[dependencies]
|
[dependencies]
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
bitstream_reader_derive = { version = "0.4", path = "bitstream_reader_derive" }
|
bitstream_reader_derive = { version = "0.4", path = "bitstream_reader_derive" }
|
||||||
bitstream_reader_skip = { version = "0.4", path = "bitstream_reader_skip", optional = true }
|
|
||||||
packed_simd = { version = "0.3", features = ["into_bits"], optional = true }
|
packed_simd = { version = "0.3", features = ["into_bits"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
@ -22,6 +21,5 @@ maplit = "1.0.1"
|
||||||
[features]
|
[features]
|
||||||
unchecked_utf8 = []
|
unchecked_utf8 = []
|
||||||
simd = ["packed_simd"]
|
simd = ["packed_simd"]
|
||||||
skip = ["bitstream_reader_skip"]
|
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
@ -114,7 +114,13 @@ fn verify_buffer(buffer: &BitBuffer<BigEndian>, inputs: &Vec<&str>) {
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn perf_string_be(b: &mut Bencher) {
|
fn perf_string_be(b: &mut Bencher) {
|
||||||
let inputs = vec!["foo", "bar", "something a little bit longer for extra testing", "a", ""];
|
let inputs = vec![
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
"something a little bit longer for extra testing",
|
||||||
|
"a",
|
||||||
|
"",
|
||||||
|
];
|
||||||
let data = build_string_data(10 * 1024 * 1024, &inputs);
|
let data = build_string_data(10 * 1024 * 1024, &inputs);
|
||||||
let buffer = BitBuffer::new(data, BigEndian);
|
let buffer = BitBuffer::new(data, BigEndian);
|
||||||
|
|
||||||
|
|
@ -137,7 +143,13 @@ fn perf_string_be(b: &mut Bencher) {
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn perf_string_le(b: &mut Bencher) {
|
fn perf_string_le(b: &mut Bencher) {
|
||||||
let inputs = vec!["foo", "bar", "something a little bit longer for extra testing", "a", ""];
|
let inputs = vec![
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
"something a little bit longer for extra testing",
|
||||||
|
"a",
|
||||||
|
"",
|
||||||
|
];
|
||||||
let data = build_string_data(10 * 1024 * 1024, &inputs);
|
let data = build_string_data(10 * 1024 * 1024, &inputs);
|
||||||
let buffer = BitBuffer::new(data, LittleEndian);
|
let buffer = BitBuffer::new(data, LittleEndian);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ use std::rc::Rc;
|
||||||
|
|
||||||
use num_traits::{Float, PrimInt};
|
use num_traits::{Float, PrimInt};
|
||||||
|
|
||||||
use crate::{ReadError, Result};
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::is_signed::IsSigned;
|
use crate::is_signed::IsSigned;
|
||||||
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
|
use crate::{ReadError, Result};
|
||||||
|
|
||||||
const USIZE_SIZE: usize = size_of::<usize>();
|
const USIZE_SIZE: usize = size_of::<usize>();
|
||||||
|
|
||||||
|
|
@ -35,8 +35,8 @@ const USIZE_SIZE: usize = size_of::<usize>();
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct BitBuffer<E>
|
pub struct BitBuffer<E>
|
||||||
where
|
where
|
||||||
E: Endianness,
|
E: Endianness,
|
||||||
{
|
{
|
||||||
bytes: Rc<Vec<u8>>,
|
bytes: Rc<Vec<u8>>,
|
||||||
bit_len: usize,
|
bit_len: usize,
|
||||||
|
|
@ -46,8 +46,8 @@ pub struct BitBuffer<E>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> BitBuffer<E>
|
impl<E> BitBuffer<E>
|
||||||
where
|
where
|
||||||
E: Endianness,
|
E: Endianness,
|
||||||
{
|
{
|
||||||
/// Create a new BitBuffer from a byte vector
|
/// Create a new BitBuffer from a byte vector
|
||||||
///
|
///
|
||||||
|
|
@ -75,8 +75,8 @@ impl<E> BitBuffer<E>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> BitBuffer<E>
|
impl<E> BitBuffer<E>
|
||||||
where
|
where
|
||||||
E: Endianness,
|
E: Endianness,
|
||||||
{
|
{
|
||||||
/// 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 {
|
||||||
|
|
@ -147,9 +147,7 @@ impl<E> BitBuffer<E>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let byte = unsafe {
|
let byte = unsafe { self.bytes.get_unchecked(byte_index) };
|
||||||
self.bytes.get_unchecked(byte_index)
|
|
||||||
};
|
|
||||||
let shifted = byte >> bit_offset;
|
let shifted = byte >> bit_offset;
|
||||||
Ok(shifted & 1u8 == 1)
|
Ok(shifted & 1u8 == 1)
|
||||||
}
|
}
|
||||||
|
|
@ -183,8 +181,8 @@ impl<E> BitBuffer<E>
|
||||||
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_int<T>(&self, position: usize, count: usize) -> Result<T>
|
pub fn read_int<T>(&self, position: usize, count: usize) -> Result<T>
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
let usize_bit_size = size_of::<usize>() * 8;
|
let usize_bit_size = size_of::<usize>() * 8;
|
||||||
|
|
@ -228,8 +226,8 @@ impl<E> BitBuffer<E>
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_fit_usize<T>(&self, position: usize, count: usize) -> T
|
fn read_fit_usize<T>(&self, position: usize, count: usize) -> T
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
let raw = self.read_usize(position, count);
|
let raw = self.read_usize(position, count);
|
||||||
|
|
@ -242,8 +240,8 @@ impl<E> BitBuffer<E>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_no_fit_usize<T>(&self, position: usize, count: usize) -> T
|
fn read_no_fit_usize<T>(&self, position: usize, count: usize) -> T
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||||
{
|
{
|
||||||
let mut left_to_read = count;
|
let mut left_to_read = count;
|
||||||
let mut acc = T::zero();
|
let mut acc = T::zero();
|
||||||
|
|
@ -269,8 +267,8 @@ impl<E> BitBuffer<E>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_signed<T>(&self, value: T, count: usize) -> T
|
fn make_signed<T>(&self, value: T, count: usize) -> T
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||||
{
|
{
|
||||||
if T::is_signed() {
|
if T::is_signed() {
|
||||||
let sign_bit = value >> (count - 1) & T::one();
|
let sign_bit = value >> (count - 1) & T::one();
|
||||||
|
|
@ -400,9 +398,7 @@ impl<E> BitBuffer<E>
|
||||||
Some(byte_len) => {
|
Some(byte_len) => {
|
||||||
let bytes = self.read_bytes(position, byte_len)?;
|
let bytes = self.read_bytes(position, byte_len)?;
|
||||||
let raw_string = if cfg!(feature = "unchecked_utf8") {
|
let raw_string = if cfg!(feature = "unchecked_utf8") {
|
||||||
unsafe {
|
unsafe { String::from_utf8_unchecked(bytes) }
|
||||||
String::from_utf8_unchecked(bytes)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
String::from_utf8(bytes)?
|
String::from_utf8(bytes)?
|
||||||
};
|
};
|
||||||
|
|
@ -411,9 +407,7 @@ impl<E> BitBuffer<E>
|
||||||
None => {
|
None => {
|
||||||
let bytes = self.read_string_bytes(position);
|
let bytes = self.read_string_bytes(position);
|
||||||
if cfg!(feature = "unchecked_utf8") {
|
if cfg!(feature = "unchecked_utf8") {
|
||||||
unsafe {
|
unsafe { Ok(String::from_utf8_unchecked(bytes)) }
|
||||||
Ok(String::from_utf8_unchecked(bytes))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
String::from_utf8(bytes).map_err(ReadError::from)
|
String::from_utf8(bytes).map_err(ReadError::from)
|
||||||
}
|
}
|
||||||
|
|
@ -454,8 +448,8 @@ impl<E> BitBuffer<E>
|
||||||
|
|
||||||
#[cfg(feature = "simd")]
|
#[cfg(feature = "simd")]
|
||||||
fn read_string_bytes(&self, position: usize) -> Vec<u8> {
|
fn read_string_bytes(&self, position: usize) -> Vec<u8> {
|
||||||
use packed_simd::u8x16;
|
|
||||||
use packed_simd::u128x1;
|
use packed_simd::u128x1;
|
||||||
|
use packed_simd::u8x16;
|
||||||
use packed_simd::IntoBits;
|
use packed_simd::IntoBits;
|
||||||
|
|
||||||
let bit_index = position & 7;
|
let bit_index = position & 7;
|
||||||
|
|
@ -465,7 +459,8 @@ impl<E> BitBuffer<E>
|
||||||
let bit_index_simd = u128x1::new(bit_index as u128);
|
let bit_index_simd = u128x1::new(bit_index as u128);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let raw_value: u128x1 = u8x16::from_slice_unaligned(&self.bytes[byte_index..byte_index + 16]).into_bits();
|
let raw_value: u128x1 =
|
||||||
|
u8x16::from_slice_unaligned(&self.bytes[byte_index..byte_index + 16]).into_bits();
|
||||||
let shifted = raw_value.rotate_right(bit_index_simd);
|
let shifted = raw_value.rotate_right(bit_index_simd);
|
||||||
let input_bytes: u8x16 = shifted.into_bits();
|
let input_bytes: u8x16 = shifted.into_bits();
|
||||||
let has_zero = ZEROS.eq(input_bytes).any();
|
let has_zero = ZEROS.eq(input_bytes).any();
|
||||||
|
|
@ -513,8 +508,8 @@ impl<E> BitBuffer<E>
|
||||||
///
|
///
|
||||||
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||||
pub fn read_float<T>(&self, position: usize) -> Result<T>
|
pub fn read_float<T>(&self, position: usize) -> Result<T>
|
||||||
where
|
where
|
||||||
T: Float + UncheckedPrimitiveFloat,
|
T: Float + UncheckedPrimitiveFloat,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
if position + type_bit_size > self.bit_len {
|
if position + type_bit_size > self.bit_len {
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,9 @@ pub use std::string::FromUtf8Error;
|
||||||
pub use bitstream_reader_derive::{BitRead, BitReadSized, BitSize, BitSizeSized};
|
pub use bitstream_reader_derive::{BitRead, BitReadSized, BitSize, BitSizeSized};
|
||||||
pub use buffer::BitBuffer;
|
pub use buffer::BitBuffer;
|
||||||
pub use endianness::*;
|
pub use endianness::*;
|
||||||
pub use read::{BitRead, BitReadSized, BitSize, BitSizeSized, LazyBitRead, LazyBitReadSized, BitSkip};
|
pub use read::{
|
||||||
|
BitRead, BitReadSized, BitSize, BitSizeSized, BitSkip, LazyBitRead, LazyBitReadSized,
|
||||||
|
};
|
||||||
pub use stream::BitStream;
|
pub use stream::BitStream;
|
||||||
|
|
||||||
mod buffer;
|
mod buffer;
|
||||||
|
|
|
||||||
59
src/read.rs
59
src/read.rs
|
|
@ -1,8 +1,10 @@
|
||||||
|
use crate::endianness::{BigEndian, LittleEndian};
|
||||||
use crate::{BitStream, Endianness, Result};
|
use crate::{BitStream, Endianness, Result};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
/// 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
|
||||||
///
|
///
|
||||||
|
|
@ -112,33 +114,64 @@ impl<T: BitRead<E> + BitSize, E: Endianness> BitSkip<E> for T {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_read_int {
|
macro_rules! impl_read_int {
|
||||||
($type:ty, $len:expr) => {
|
($type:ty) => {
|
||||||
impl<E: Endianness> BitRead<E> for $type {
|
impl<E: Endianness> BitRead<E> for $type {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read(stream: &mut BitStream<E>) -> Result<$type> {
|
fn read(stream: &mut BitStream<E>) -> Result<$type> {
|
||||||
stream.read_int::<$type>($len)
|
stream.read_int::<$type>(size_of::<$type>() * 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BitSize for $type {
|
impl BitSize for $type {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bit_size() -> usize {
|
fn bit_size() -> usize {
|
||||||
$len
|
size_of::<$type>() * 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_read_int!(u8, 8);
|
macro_rules! impl_read_int_nonzero {
|
||||||
impl_read_int!(u16, 16);
|
($type:ty) => {
|
||||||
impl_read_int!(u32, 32);
|
impl BitRead<LittleEndian> for Option<$type> {
|
||||||
impl_read_int!(u64, 64);
|
#[inline]
|
||||||
impl_read_int!(u128, 128);
|
fn read(stream: &mut BitStream<LittleEndian>) -> Result<Self> {
|
||||||
impl_read_int!(i8, 8);
|
Ok(<$type>::new(stream.read()?))
|
||||||
impl_read_int!(i16, 16);
|
}
|
||||||
impl_read_int!(i32, 32);
|
}
|
||||||
impl_read_int!(i64, 64);
|
|
||||||
impl_read_int!(i128, 128);
|
impl BitRead<BigEndian> for Option<$type> {
|
||||||
|
#[inline]
|
||||||
|
fn read(stream: &mut BitStream<BigEndian>) -> Result<Self> {
|
||||||
|
Ok(<$type>::new(stream.read()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BitSize for $type {
|
||||||
|
#[inline]
|
||||||
|
fn bit_size() -> usize {
|
||||||
|
size_of::<$type>() * 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_read_int!(u8);
|
||||||
|
impl_read_int!(u16);
|
||||||
|
impl_read_int!(u32);
|
||||||
|
impl_read_int!(u64);
|
||||||
|
impl_read_int!(u128);
|
||||||
|
impl_read_int!(i8);
|
||||||
|
impl_read_int!(i16);
|
||||||
|
impl_read_int!(i32);
|
||||||
|
impl_read_int!(i64);
|
||||||
|
impl_read_int!(i128);
|
||||||
|
|
||||||
|
impl_read_int_nonzero!(std::num::NonZeroU8);
|
||||||
|
impl_read_int_nonzero!(std::num::NonZeroU16);
|
||||||
|
impl_read_int_nonzero!(std::num::NonZeroU32);
|
||||||
|
impl_read_int_nonzero!(std::num::NonZeroU64);
|
||||||
|
impl_read_int_nonzero!(std::num::NonZeroU128);
|
||||||
|
|
||||||
impl<E: Endianness> BitRead<E> for f32 {
|
impl<E: Endianness> BitRead<E> for f32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
use maplit::hashmap;
|
use maplit::hashmap;
|
||||||
|
|
||||||
|
|
@ -255,28 +256,11 @@ fn test_read_str_be() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_str_le() {
|
fn test_read_str_le() {
|
||||||
let bytes = vec![
|
let bytes = vec![
|
||||||
'h' as u8,
|
'h' as u8, 'e' as u8, 'l' as u8, 'l' as u8, 'o' as u8, ' ' as u8, 'w' as u8, 'o' as u8,
|
||||||
'e' as u8,
|
'r' as u8, 'l' as u8, 'd' as u8, 0, 'f' as u8, 'o' as u8, 'o' as u8, 0, 0, 0, 0, 0,
|
||||||
'l' as u8,
|
|
||||||
'l' as u8,
|
|
||||||
'o' as u8,
|
|
||||||
' ' as u8,
|
|
||||||
'w' as u8,
|
|
||||||
'o' as u8,
|
|
||||||
'r' as u8,
|
|
||||||
'l' as u8,
|
|
||||||
'd' as u8,
|
|
||||||
0,
|
|
||||||
'f' as u8,
|
|
||||||
'o' as u8,
|
|
||||||
'o' as u8,
|
|
||||||
0, 0, 0, 0, 0,
|
|
||||||
];
|
];
|
||||||
let buffer = BitBuffer::new(bytes, LittleEndian);
|
let buffer = BitBuffer::new(bytes, LittleEndian);
|
||||||
assert_eq!(
|
assert_eq!(buffer.read_string(0, Some(3)).unwrap(), "hel".to_owned());
|
||||||
buffer.read_string(0, Some(3)).unwrap(),
|
|
||||||
"hel".to_owned()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
buffer.read_string(0, Some(11)).unwrap(),
|
buffer.read_string(0, Some(11)).unwrap(),
|
||||||
"hello world".to_owned()
|
"hello world".to_owned()
|
||||||
|
|
@ -390,3 +374,12 @@ fn test_read_struct() {
|
||||||
stream.read().unwrap()
|
stream.read().unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_nonzero() {
|
||||||
|
let bytes = vec![12, 0, 0, 0];
|
||||||
|
let buffer = BitBuffer::new(bytes, LittleEndian);
|
||||||
|
let mut stream = BitStream::from(buffer);
|
||||||
|
assert_eq!(NonZeroU16::new(12), stream.read().unwrap());
|
||||||
|
assert_eq!(None, stream.read::<Option<NonZeroU16>>().unwrap());
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue