mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 08:34:07 +02:00
fix reading large signed ints
This commit is contained in:
parent
b46bc881f0
commit
efed5a68cb
3 changed files with 72 additions and 14 deletions
|
|
@ -5,7 +5,7 @@ use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::ops::{BitOrAssign, BitXor, Index, Range, RangeFrom};
|
use std::ops::{BitOrAssign, BitXor, Index, Range, RangeFrom};
|
||||||
|
|
||||||
use num_traits::{Float, PrimInt};
|
use num_traits::{Float, PrimInt, WrappingSub};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
|
|
@ -360,7 +360,7 @@ where
|
||||||
#[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 + BitXor,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + WrappingSub,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
|
|
||||||
|
|
@ -395,7 +395,7 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn read_int_unchecked<T>(&self, position: usize, count: usize, end: bool) -> T
|
pub unsafe fn read_int_unchecked<T>(&self, position: usize, count: usize, end: bool) -> T
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + WrappingSub,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
|
|
||||||
|
|
@ -453,14 +453,14 @@ where
|
||||||
|
|
||||||
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 + BitXor,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + WrappingSub,
|
||||||
{
|
{
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
T::zero()
|
T::zero()
|
||||||
} else if T::is_signed() {
|
} else if T::is_signed() {
|
||||||
let sign_bit = value >> (count - 1) & T::one();
|
let sign_bit = value >> (count - 1) & T::one();
|
||||||
if sign_bit == T::one() {
|
if sign_bit == T::one() {
|
||||||
value | (T::zero() - T::one()) ^ ((T::one() << count) - T::one())
|
value | (T::zero() - T::one()) ^ (T::one() << count).wrapping_sub(&T::one())
|
||||||
} else {
|
} else {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
@ -718,6 +718,7 @@ where
|
||||||
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,
|
||||||
|
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
if position + type_bit_size + USIZE_BIT_SIZE > self.bit_len() {
|
if position + type_bit_size + USIZE_BIT_SIZE > self.bit_len() {
|
||||||
|
|
@ -745,6 +746,7 @@ where
|
||||||
pub unsafe fn read_float_unchecked<T>(&self, position: usize, end: bool) -> T
|
pub unsafe fn read_float_unchecked<T>(&self, position: usize, end: bool) -> T
|
||||||
where
|
where
|
||||||
T: Float + UncheckedPrimitiveFloat,
|
T: Float + UncheckedPrimitiveFloat,
|
||||||
|
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
|
||||||
{
|
{
|
||||||
if position & 7 == 0 {
|
if position & 7 == 0 {
|
||||||
let byte_pos = position / 8;
|
let byte_pos = position / 8;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::ops::BitOrAssign;
|
use std::ops::BitOrAssign;
|
||||||
|
|
||||||
use num_traits::{Float, PrimInt};
|
use num_traits::{Float, PrimInt, WrappingSub};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
|
|
@ -141,7 +141,7 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_int<T>(&mut self, count: usize) -> Result<T>
|
pub fn read_int<T>(&mut self, count: usize) -> Result<T>
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + WrappingSub,
|
||||||
{
|
{
|
||||||
let result = self.buffer.read_int(self.pos, count);
|
let result = self.buffer.read_int(self.pos, count);
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
|
|
@ -154,7 +154,7 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn read_int_unchecked<T>(&mut self, count: usize, end: bool) -> T
|
pub unsafe fn read_int_unchecked<T>(&mut self, count: usize, end: bool) -> T
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + WrappingSub,
|
||||||
{
|
{
|
||||||
let result = self.buffer.read_int_unchecked(self.pos, count, end);
|
let result = self.buffer.read_int_unchecked(self.pos, count, end);
|
||||||
self.pos += count;
|
self.pos += count;
|
||||||
|
|
@ -191,6 +191,7 @@ where
|
||||||
pub fn read_float<T>(&mut self) -> Result<T>
|
pub fn read_float<T>(&mut self) -> Result<T>
|
||||||
where
|
where
|
||||||
T: Float + UncheckedPrimitiveFloat,
|
T: Float + UncheckedPrimitiveFloat,
|
||||||
|
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
|
||||||
{
|
{
|
||||||
let count = size_of::<T>() * 8;
|
let count = size_of::<T>() * 8;
|
||||||
let result = self.buffer.read_float(self.pos);
|
let result = self.buffer.read_float(self.pos);
|
||||||
|
|
@ -205,6 +206,7 @@ where
|
||||||
pub unsafe fn read_float_unchecked<T>(&mut self, end: bool) -> T
|
pub unsafe fn read_float_unchecked<T>(&mut self, end: bool) -> T
|
||||||
where
|
where
|
||||||
T: Float + UncheckedPrimitiveFloat,
|
T: Float + UncheckedPrimitiveFloat,
|
||||||
|
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
|
||||||
{
|
{
|
||||||
let count = size_of::<T>() * 8;
|
let count = size_of::<T>() * 8;
|
||||||
let result = self.buffer.read_float_unchecked(self.pos, end);
|
let result = self.buffer.read_float_unchecked(self.pos, end);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
|
use bitbuffer::num_traits::{IsSigned, SplitFitUsize, UncheckedPrimitiveInt};
|
||||||
use bitbuffer::{BigEndian, BitReadBuffer, BitReadStream, BitWriteStream, LittleEndian};
|
use bitbuffer::{BigEndian, BitReadBuffer, BitReadStream, BitWriteStream, LittleEndian};
|
||||||
|
use num_traits::{PrimInt, WrappingSub};
|
||||||
|
use std::any::type_name;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::ops::BitOrAssign;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -216,8 +222,6 @@ fn test_write_to_slice() {
|
||||||
stream.write_int(13253u64, 64).unwrap();
|
stream.write_int(13253u64, 64).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&data);
|
|
||||||
|
|
||||||
let mut read = BitReadStream::from(BitReadBuffer::new(&data[..], LittleEndian));
|
let mut read = BitReadStream::from(BitReadBuffer::new(&data[..], LittleEndian));
|
||||||
|
|
||||||
assert!(read.read_bool().unwrap());
|
assert!(read.read_bool().unwrap());
|
||||||
|
|
@ -248,16 +252,66 @@ fn test_write_last_slice() {
|
||||||
fn test_write_be_long() {
|
fn test_write_be_long() {
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
let mut writer = BitWriteStream::new(&mut bytes, BigEndian);
|
let mut writer = BitWriteStream::new(&mut bytes, BigEndian);
|
||||||
let num1 = 0b11000_00111110usize;
|
let num1 = 0b11000_00111110u64;
|
||||||
let num2 = 0b1111111_11100100_00100100_11011101_00000011_11100000_01100111_11011011usize;
|
let num2 = 0b1111111_11100100_00100100_11011101_00000011_11100000_01100111_11011011u64;
|
||||||
writer.write_int(num1, 13).unwrap();
|
writer.write_int(num1, 13).unwrap();
|
||||||
writer.write_int(num2, 63).unwrap();
|
writer.write_int(num2, 63).unwrap();
|
||||||
|
|
||||||
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
||||||
let mut reader = BitReadStream::new(buffer);
|
let mut reader = BitReadStream::new(buffer);
|
||||||
let num1actual = reader.read_int::<usize>(13).unwrap();
|
let num1actual = reader.read_int::<u64>(13).unwrap();
|
||||||
let num2actual = reader.read_int::<usize>(63).unwrap();
|
let num2actual = reader.read_int::<u64>(63).unwrap();
|
||||||
|
|
||||||
assert_eq!(num1actual, num1);
|
assert_eq!(num1actual, num1);
|
||||||
assert_eq!(num2actual, num2);
|
assert_eq!(num2actual, num2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_all_lengths() {
|
||||||
|
let pattern = 0b10101010u8;
|
||||||
|
test_write_all_lengths_ty::<u8>(pattern);
|
||||||
|
test_write_all_lengths_ty::<u16>(u16::from_le_bytes([pattern; 2]));
|
||||||
|
test_write_all_lengths_ty::<u32>(u32::from_le_bytes([pattern; 4]));
|
||||||
|
test_write_all_lengths_ty::<u64>(u64::from_le_bytes([pattern; 8]));
|
||||||
|
test_write_all_lengths_ty::<u128>(u128::from_le_bytes([pattern; 16]));
|
||||||
|
test_write_all_lengths_ty::<usize>(usize::from_le_bytes([pattern; size_of::<usize>()]));
|
||||||
|
|
||||||
|
test_write_all_lengths_ty::<i8>(i8::from_le_bytes([pattern; 1]));
|
||||||
|
test_write_all_lengths_ty::<i16>(i16::from_le_bytes([pattern; 2]));
|
||||||
|
test_write_all_lengths_ty::<i32>(i32::from_le_bytes([pattern; 4]));
|
||||||
|
test_write_all_lengths_ty::<i64>(i64::from_le_bytes([pattern; 8]));
|
||||||
|
test_write_all_lengths_ty::<i128>(i128::from_le_bytes([pattern; 16]));
|
||||||
|
test_write_all_lengths_ty::<isize>(isize::from_le_bytes([pattern; size_of::<isize>()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_write_all_lengths_ty<
|
||||||
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + Debug + SplitFitUsize + WrappingSub,
|
||||||
|
>(
|
||||||
|
pattern: T,
|
||||||
|
) {
|
||||||
|
let max_bits = size_of::<T>() * 8;
|
||||||
|
let mut bytes = Vec::new();
|
||||||
|
let mut writer = BitWriteStream::new(&mut bytes, BigEndian);
|
||||||
|
|
||||||
|
let mut expected = Vec::<T>::new();
|
||||||
|
|
||||||
|
for bits in 1..max_bits {
|
||||||
|
let value = pattern >> (max_bits - bits);
|
||||||
|
expected.push(value);
|
||||||
|
writer.write_int(value, bits).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = BitReadBuffer::new(&bytes, BigEndian);
|
||||||
|
let mut reader = BitReadStream::new(buffer);
|
||||||
|
|
||||||
|
for (bits, expected_value) in (1..max_bits).zip(expected.into_iter()) {
|
||||||
|
let actual = reader.read_int::<T>(bits).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
expected_value,
|
||||||
|
actual,
|
||||||
|
"write + read for {} bits {}",
|
||||||
|
bits,
|
||||||
|
type_name::<T>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue