mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
larger non fit chunks
This commit is contained in:
parent
9d04fbe7cf
commit
bf27c72389
3 changed files with 157 additions and 85 deletions
|
|
@ -10,7 +10,13 @@ use std::ops::{BitOrAssign, BitXor};
|
||||||
/// Allow casting floats unchecked
|
/// Allow casting floats unchecked
|
||||||
pub trait UncheckedPrimitiveFloat: Sized {
|
pub trait UncheckedPrimitiveFloat: Sized {
|
||||||
type BYTES: AsRef<[u8]> + for<'a> TryFrom<&'a [u8], Error = TryFromSliceError>;
|
type BYTES: AsRef<[u8]> + for<'a> TryFrom<&'a [u8], Error = TryFromSliceError>;
|
||||||
type INT: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + Debug + IntoBytes;
|
type INT: PrimInt
|
||||||
|
+ BitOrAssign
|
||||||
|
+ IsSigned
|
||||||
|
+ UncheckedPrimitiveInt
|
||||||
|
+ BitXor
|
||||||
|
+ Debug
|
||||||
|
+ SplitFitUsize;
|
||||||
|
|
||||||
fn from_f32_unchecked(n: f32) -> Self;
|
fn from_f32_unchecked(n: f32) -> Self;
|
||||||
fn from_f64_unchecked(n: f64) -> Self;
|
fn from_f64_unchecked(n: f64) -> Self;
|
||||||
|
|
@ -255,56 +261,6 @@ macro_rules! impl_is_signed {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait IntoBytes: Sized {
|
|
||||||
type BytesIter: DoubleEndedIterator<Item = u8> + ExactSizeIterator;
|
|
||||||
type U16Iter: DoubleEndedIterator<Item = u16> + ExactSizeIterator;
|
|
||||||
|
|
||||||
fn into_bytes(self) -> Self::BytesIter;
|
|
||||||
|
|
||||||
fn into_u16(self) -> Self::U16Iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_into_bytes {
|
|
||||||
($type:ty, $bytes:expr, 1 ) => {
|
|
||||||
impl IntoBytes for $type {
|
|
||||||
type BytesIter = std::array::IntoIter<u8, $bytes>;
|
|
||||||
type U16Iter = std::array::IntoIter<u16, 1>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn into_bytes(self) -> Self::BytesIter {
|
|
||||||
Self::BytesIter::new(self.to_le_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn into_u16(self) -> Self::U16Iter {
|
|
||||||
Self::U16Iter::new([self as u16])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($type:ty, $bytes:expr, $shorts:expr ) => {
|
|
||||||
impl IntoBytes for $type {
|
|
||||||
type BytesIter = std::array::IntoIter<u8, $bytes>;
|
|
||||||
type U16Iter = std::array::IntoIter<u16, { $shorts }>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn into_bytes(self) -> Self::BytesIter {
|
|
||||||
Self::BytesIter::new(self.to_le_bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn into_u16(self) -> Self::U16Iter {
|
|
||||||
let bytes = self.to_le_bytes();
|
|
||||||
let mut shorts = [0; $shorts];
|
|
||||||
let mut chunks = bytes.chunks(2).zip(shorts.iter_mut());
|
|
||||||
while let Some((&[a, b], short)) = chunks.next() {
|
|
||||||
*short = (b as u16) << 8 | a as u16;
|
|
||||||
}
|
|
||||||
Self::U16Iter::new(shorts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_is_signed!(u8, false);
|
impl_is_signed!(u8, false);
|
||||||
impl_is_signed!(u16, false);
|
impl_is_signed!(u16, false);
|
||||||
impl_is_signed!(u32, false);
|
impl_is_signed!(u32, false);
|
||||||
|
|
@ -318,24 +274,142 @@ impl_is_signed!(i64, true);
|
||||||
impl_is_signed!(i128, true);
|
impl_is_signed!(i128, true);
|
||||||
impl_is_signed!(isize, true);
|
impl_is_signed!(isize, true);
|
||||||
|
|
||||||
impl_into_bytes!(u8, 1, 1);
|
pub trait SplitFitUsize {
|
||||||
impl_into_bytes!(u16, 2, 1);
|
type Iter: Iterator<Item = (usize, u8)> + ExactSizeIterator + DoubleEndedIterator;
|
||||||
impl_into_bytes!(u32, 4, 2);
|
|
||||||
impl_into_bytes!(u64, 8, 4);
|
|
||||||
impl_into_bytes!(u128, 16, 8);
|
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::array;
|
||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
macro_rules! impl_split_fit {
|
||||||
|
($type:ty) => {
|
||||||
|
impl SplitFitUsize for $type {
|
||||||
|
type Iter = array::IntoIter<(usize, u8), 1>;
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter {
|
||||||
|
assert!(size_of::<Self>() < size_of::<usize>());
|
||||||
|
Self::Iter::new([(self as usize, size_of::<Self>() as u8 * 8)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_split_fit_signed {
|
||||||
|
($signed_type:ty, $unsigned_type:ty) => {
|
||||||
|
impl SplitFitUsize for $signed_type {
|
||||||
|
type Iter = <$unsigned_type as SplitFitUsize>::Iter;
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter {
|
||||||
|
let unsigned = <$unsigned_type>::from_ne_bytes(self.to_ne_bytes());
|
||||||
|
unsigned.split_fit_usize::<E>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_split_fit!(u8);
|
||||||
|
impl_split_fit!(u16);
|
||||||
|
impl_split_fit!(i8);
|
||||||
|
impl_split_fit!(i16);
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
impl_into_bytes!(usize, 8, 4);
|
impl_split_fit!(u32);
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
impl_into_bytes!(usize, 4, 2);
|
|
||||||
|
|
||||||
impl_into_bytes!(i8, 1, 1);
|
|
||||||
impl_into_bytes!(i16, 2, 1);
|
|
||||||
impl_into_bytes!(i32, 4, 2);
|
|
||||||
impl_into_bytes!(i64, 8, 4);
|
|
||||||
impl_into_bytes!(i128, 16, 8);
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
impl_into_bytes!(isize, 8, 4);
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
impl_into_bytes!(isize, 4, 2);
|
impl SplitFitUsize for u32 {
|
||||||
|
type Iter = array::IntoIter<(usize, u8), 2>;
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter {
|
||||||
|
Self::Iter::new(if E::is_le() {
|
||||||
|
[
|
||||||
|
((self & (Self::MAX >> 8)) as usize, 24),
|
||||||
|
((self >> 24) as usize, 8),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
((self >> 24) as usize, 8),
|
||||||
|
((self & (Self::MAX >> 8)) as usize, 24),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_split_fit_signed!(i32, u32);
|
||||||
|
|
||||||
|
impl SplitFitUsize for u64 {
|
||||||
|
type Iter = array::IntoIter<(usize, u8), 3>;
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter {
|
||||||
|
Self::Iter::new(if E::is_le() {
|
||||||
|
[
|
||||||
|
((self & (Self::MAX >> 40)) as usize, 24),
|
||||||
|
((self >> 24 & (Self::MAX >> 16)) as usize, 24),
|
||||||
|
((self >> 48) as usize, 16),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
((self >> 48) as usize, 16),
|
||||||
|
((self >> 24 & (Self::MAX >> 16)) as usize, 24),
|
||||||
|
((self & (Self::MAX >> 40)) as usize, 24),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_split_fit_signed!(i64, u64);
|
||||||
|
|
||||||
|
impl SplitFitUsize for u128 {
|
||||||
|
type Iter = array::IntoIter<(usize, u8), 6>;
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter {
|
||||||
|
Self::Iter::new(if E::is_le() {
|
||||||
|
[
|
||||||
|
((self & (Self::MAX >> 104)) as usize, 24),
|
||||||
|
((self >> 24 & (Self::MAX >> 80)) as usize, 24),
|
||||||
|
((self >> 48 & (Self::MAX >> 56)) as usize, 24),
|
||||||
|
((self >> 72 & (Self::MAX >> 32)) as usize, 24),
|
||||||
|
((self >> 96 & (Self::MAX >> 8)) as usize, 24),
|
||||||
|
((self >> 120) as usize, 8),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
((self >> 120) as usize, 8),
|
||||||
|
((self >> 96 & (Self::MAX >> 8)) as usize, 24),
|
||||||
|
((self >> 72 & (Self::MAX >> 32)) as usize, 24),
|
||||||
|
((self >> 48 & (Self::MAX >> 56)) as usize, 24),
|
||||||
|
((self >> 24 & (Self::MAX >> 80)) as usize, 24),
|
||||||
|
((self & (Self::MAX >> 104)) as usize, 24),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_split_fit_signed!(i128, u128);
|
||||||
|
|
||||||
|
impl SplitFitUsize for usize {
|
||||||
|
type Iter = array::IntoIter<(usize, u8), 2>;
|
||||||
|
|
||||||
|
fn split_fit_usize<E: Endianness>(self) -> Self::Iter {
|
||||||
|
const USIZE_BITS: usize = size_of::<usize>() * 8;
|
||||||
|
Self::Iter::new(if E::is_le() {
|
||||||
|
[
|
||||||
|
(
|
||||||
|
(self & (Self::MAX >> (USIZE_BITS - 8))) as usize,
|
||||||
|
USIZE_BITS as u8 - 8,
|
||||||
|
),
|
||||||
|
((self >> (USIZE_BITS - 8)) as usize, 8),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
[
|
||||||
|
((self >> (USIZE_BITS - 8)) as usize, 8),
|
||||||
|
(
|
||||||
|
(self & (Self::MAX >> (USIZE_BITS - 8))) as usize,
|
||||||
|
USIZE_BITS as u8 - 8,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_split_fit_signed!(isize, usize);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::Endianness;
|
use crate::Endianness;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::iter::{once, repeat};
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
|
@ -29,22 +28,15 @@ impl<'a, E: Endianness> WriteBuffer<'a, E> {
|
||||||
pub fn push_non_fit_bits<I>(&mut self, bits: I, count: usize)
|
pub fn push_non_fit_bits<I>(&mut self, bits: I, count: usize)
|
||||||
where
|
where
|
||||||
I: ExactSizeIterator,
|
I: ExactSizeIterator,
|
||||||
I: DoubleEndedIterator<Item = u16>,
|
I: DoubleEndedIterator<Item = (usize, u8)>,
|
||||||
{
|
{
|
||||||
let chunk_bits = u16::BITS as usize;
|
let mut remaining = count;
|
||||||
let full_bytes = min(bits.len() - 1, count / chunk_bits);
|
for (chunk, chunk_size) in bits {
|
||||||
|
if remaining > 0 {
|
||||||
let counts = repeat(chunk_bits)
|
let bits = min(remaining, chunk_size as usize);
|
||||||
.take(full_bytes)
|
self.push_bits(chunk, bits);
|
||||||
.chain(once(count - full_bytes * chunk_bits));
|
remaining -= bits
|
||||||
if E::is_le() {
|
}
|
||||||
bits.zip(counts)
|
|
||||||
.for_each(|(chunk, count)| self.push_bits(chunk as usize, count))
|
|
||||||
} else {
|
|
||||||
bits.take(count / 8 + 1)
|
|
||||||
.rev()
|
|
||||||
.zip(counts)
|
|
||||||
.for_each(|(chunk, count)| self.push_bits(chunk as usize, count))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::mem::size_of;
|
||||||
use std::ops::{BitOrAssign, BitXor};
|
use std::ops::{BitOrAssign, BitXor};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::num_traits::{IntoBytes, IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
use crate::num_traits::{IsSigned, SplitFitUsize, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
use crate::writebuffer::WriteBuffer;
|
use crate::writebuffer::WriteBuffer;
|
||||||
use crate::{BitError, BitReadStream, BitWrite, BitWriteSized, Result};
|
use crate::{BitError, BitReadStream, BitWrite, BitWriteSized, Result};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
@ -76,7 +76,7 @@ where
|
||||||
fn push_non_fit_bits<I>(&mut self, bits: I, count: usize)
|
fn push_non_fit_bits<I>(&mut self, bits: I, count: usize)
|
||||||
where
|
where
|
||||||
I: ExactSizeIterator,
|
I: ExactSizeIterator,
|
||||||
I: DoubleEndedIterator<Item = u16>,
|
I: DoubleEndedIterator<Item = (usize, u8)>,
|
||||||
{
|
{
|
||||||
self.buffer.push_non_fit_bits(bits, count)
|
self.buffer.push_non_fit_bits(bits, count)
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +131,13 @@ where
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn write_int<T>(&mut self, value: T, count: usize) -> Result<()>
|
pub fn write_int<T>(&mut self, value: T, count: usize) -> Result<()>
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + IntoBytes + Debug,
|
T: PrimInt
|
||||||
|
+ BitOrAssign
|
||||||
|
+ IsSigned
|
||||||
|
+ UncheckedPrimitiveInt
|
||||||
|
+ BitXor
|
||||||
|
+ Debug
|
||||||
|
+ SplitFitUsize,
|
||||||
{
|
{
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
|
|
||||||
|
|
@ -145,7 +151,7 @@ where
|
||||||
if type_bit_size < USIZE_BITS || count <= (USIZE_BITS - (self.bit_len() % 8)) {
|
if type_bit_size < USIZE_BITS || count <= (USIZE_BITS - (self.bit_len() % 8)) {
|
||||||
self.push_bits(value.into_usize_unchecked(), count);
|
self.push_bits(value.into_usize_unchecked(), count);
|
||||||
} else {
|
} else {
|
||||||
self.push_non_fit_bits(value.into_u16(), count)
|
self.push_non_fit_bits(value.split_fit_usize::<E>(), count)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue