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

fix reading large signed ints

This commit is contained in:
Robin Appelman 2024-04-04 22:55:06 +02:00
commit efed5a68cb
3 changed files with 72 additions and 14 deletions

View file

@ -5,7 +5,7 @@ use std::marker::PhantomData;
use std::mem::size_of;
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::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
@ -360,7 +360,7 @@ where
#[inline]
pub fn read_int<T>(&self, position: usize, count: usize) -> Result<T>
where
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor,
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + WrappingSub,
{
let type_bit_size = size_of::<T>() * 8;
@ -395,7 +395,7 @@ where
#[inline]
pub unsafe fn read_int_unchecked<T>(&self, position: usize, count: usize, end: bool) -> T
where
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor,
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + WrappingSub,
{
let type_bit_size = size_of::<T>() * 8;
@ -453,14 +453,14 @@ where
fn make_signed<T>(&self, value: T, count: usize) -> T
where
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor,
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + WrappingSub,
{
if count == 0 {
T::zero()
} else if T::is_signed() {
let sign_bit = value >> (count - 1) & 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 {
value
}
@ -718,6 +718,7 @@ where
pub fn read_float<T>(&self, position: usize) -> Result<T>
where
T: Float + UncheckedPrimitiveFloat,
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
{
let type_bit_size = size_of::<T>() * 8;
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
where
T: Float + UncheckedPrimitiveFloat,
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
{
if position & 7 == 0 {
let byte_pos = position / 8;

View file

@ -1,7 +1,7 @@
use std::mem::size_of;
use std::ops::BitOrAssign;
use num_traits::{Float, PrimInt};
use num_traits::{Float, PrimInt, WrappingSub};
use crate::endianness::Endianness;
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
@ -141,7 +141,7 @@ where
#[inline]
pub fn read_int<T>(&mut self, count: usize) -> Result<T>
where
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + WrappingSub,
{
let result = self.buffer.read_int(self.pos, count);
if result.is_ok() {
@ -154,7 +154,7 @@ where
#[inline]
pub unsafe fn read_int_unchecked<T>(&mut self, count: usize, end: bool) -> T
where
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + WrappingSub,
{
let result = self.buffer.read_int_unchecked(self.pos, count, end);
self.pos += count;
@ -191,6 +191,7 @@ where
pub fn read_float<T>(&mut self) -> Result<T>
where
T: Float + UncheckedPrimitiveFloat,
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
{
let count = size_of::<T>() * 8;
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
where
T: Float + UncheckedPrimitiveFloat,
<T as UncheckedPrimitiveFloat>::INT: WrappingSub,
{
let count = size_of::<T>() * 8;
let result = self.buffer.read_float_unchecked(self.pos, end);

View file

@ -1,4 +1,10 @@
use bitbuffer::num_traits::{IsSigned, SplitFitUsize, UncheckedPrimitiveInt};
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::sync::Arc;
@ -216,8 +222,6 @@ fn test_write_to_slice() {
stream.write_int(13253u64, 64).unwrap();
}
dbg!(&data);
let mut read = BitReadStream::from(BitReadBuffer::new(&data[..], LittleEndian));
assert!(read.read_bool().unwrap());
@ -248,16 +252,66 @@ fn test_write_last_slice() {
fn test_write_be_long() {
let mut bytes = vec![];
let mut writer = BitWriteStream::new(&mut bytes, BigEndian);
let num1 = 0b11000_00111110usize;
let num2 = 0b1111111_11100100_00100100_11011101_00000011_11100000_01100111_11011011usize;
let num1 = 0b11000_00111110u64;
let num2 = 0b1111111_11100100_00100100_11011101_00000011_11100000_01100111_11011011u64;
writer.write_int(num1, 13).unwrap();
writer.write_int(num2, 63).unwrap();
let buffer = BitReadBuffer::new(&bytes, BigEndian);
let mut reader = BitReadStream::new(buffer);
let num1actual = reader.read_int::<usize>(13).unwrap();
let num2actual = reader.read_int::<usize>(63).unwrap();
let num1actual = reader.read_int::<u64>(13).unwrap();
let num2actual = reader.read_int::<u64>(63).unwrap();
assert_eq!(num1actual, num1);
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>()
);
}
}