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

optimize aligned floats

This commit is contained in:
Robin Appelman 2021-07-24 18:49:32 +02:00
commit 9aba4351ac
5 changed files with 82 additions and 20 deletions

View file

@ -1,12 +1,21 @@
use crate::Endianness;
use std::array::TryFromSliceError;
use std::convert::TryFrom;
/// some extra number traits /// some extra number traits
/// 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>;
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;
fn to_bytes<E: Endianness>(self) -> Self::BYTES;
fn from_bytes<E: Endianness>(bytes: Self::BYTES) -> Self;
} }
impl UncheckedPrimitiveFloat for f32 { impl UncheckedPrimitiveFloat for f32 {
type BYTES = [u8; 4];
#[inline(always)] #[inline(always)]
fn from_f32_unchecked(n: f32) -> Self { fn from_f32_unchecked(n: f32) -> Self {
n n
@ -15,9 +24,24 @@ impl UncheckedPrimitiveFloat for f32 {
fn from_f64_unchecked(n: f64) -> Self { fn from_f64_unchecked(n: f64) -> Self {
n as f32 n as f32
} }
fn to_bytes<E: Endianness>(self) -> Self::BYTES {
if E::is_le() {
self.to_le_bytes()
} else {
self.to_be_bytes()
}
}
fn from_bytes<E: Endianness>(bytes: Self::BYTES) -> Self {
if E::is_le() {
Self::from_le_bytes(bytes)
} else {
Self::from_be_bytes(bytes)
}
}
} }
impl UncheckedPrimitiveFloat for f64 { impl UncheckedPrimitiveFloat for f64 {
type BYTES = [u8; 8];
#[inline(always)] #[inline(always)]
fn from_f32_unchecked(n: f32) -> Self { fn from_f32_unchecked(n: f32) -> Self {
n as f64 n as f64
@ -26,6 +50,20 @@ impl UncheckedPrimitiveFloat for f64 {
fn from_f64_unchecked(n: f64) -> Self { fn from_f64_unchecked(n: f64) -> Self {
n n
} }
fn to_bytes<E: Endianness>(self) -> Self::BYTES {
if E::is_le() {
self.to_le_bytes()
} else {
self.to_be_bytes()
}
}
fn from_bytes<E: Endianness>(bytes: Self::BYTES) -> Self {
if E::is_le() {
Self::from_le_bytes(bytes)
} else {
Self::from_be_bytes(bytes)
}
}
} }
/// Allow casting integers unchecked /// Allow casting integers unchecked

View file

@ -737,16 +737,24 @@ where
where where
T: Float + UncheckedPrimitiveFloat, T: Float + UncheckedPrimitiveFloat,
{ {
if size_of::<T>() == 4 { if position & 7 == 0 {
let int = if size_of::<T>() < USIZE_SIZE { let byte_pos = position / 8;
self.read_fit_usize::<u32>(position, 32, end) let bytes = self.slice[byte_pos..byte_pos + size_of::<T>()]
} else { .try_into()
self.read_no_fit_usize::<u32>(position, 32, end) .unwrap();
}; T::from_bytes::<E>(bytes)
T::from_f32_unchecked(f32::from_bits(int))
} else { } else {
let int = self.read_no_fit_usize::<u64>(position, 64, end); if size_of::<T>() == 4 {
T::from_f64_unchecked(f64::from_bits(int)) let int = if size_of::<T>() < USIZE_SIZE {
self.read_fit_usize::<u32>(position, 32, end)
} else {
self.read_no_fit_usize::<u32>(position, 32, end)
};
T::from_f32_unchecked(f32::from_bits(int))
} else {
let int = self.read_no_fit_usize::<u64>(position, 64, end);
T::from_f64_unchecked(f64::from_bits(int))
}
} }
} }

View file

@ -93,4 +93,10 @@ impl<'a, E: Endianness> WriteBuffer<'a, E> {
let merged = merged.to_le_bytes(); let merged = merged.to_le_bytes();
self.bytes[byte_pos..byte_pos + byte_count].copy_from_slice(&merged[0..byte_count]); self.bytes[byte_pos..byte_pos + byte_count].copy_from_slice(&merged[0..byte_count]);
} }
pub fn extends_from_slice(&mut self, slice: &[u8]) {
debug_assert_eq!(0, self.bit_len & 7);
self.bytes.extend_from_slice(slice);
self.bit_len += slice.len() * 8
}
} }

View file

@ -173,14 +173,19 @@ where
where where
T: Float + UncheckedPrimitiveFloat, T: Float + UncheckedPrimitiveFloat,
{ {
if size_of::<T>() == 4 { if self.buffer.bit_len() & 7 == 0 {
if size_of::<T>() < USIZE_SIZE { let bytes = value.to_bytes::<E>();
self.push_bits(value.to_f32().unwrap().to_bits() as usize, 32); self.buffer.extends_from_slice(bytes.as_ref());
} else {
self.push_non_fit_bits(value.to_f32().unwrap().to_bits().into_bytes(), 32)
};
} else { } else {
self.push_non_fit_bits(value.to_f64().unwrap().to_bits().into_bytes(), 64) if size_of::<T>() == 4 {
if size_of::<T>() < USIZE_SIZE {
self.push_bits(value.to_f32().unwrap().to_bits() as usize, 32);
} else {
self.push_non_fit_bits(value.to_f32().unwrap().to_bits().into_bytes(), 32)
};
} else {
self.push_non_fit_bits(value.to_f64().unwrap().to_bits().into_bytes(), 64)
}
} }
Ok(()) Ok(())
@ -205,10 +210,14 @@ where
/// ``` /// ```
#[inline] #[inline]
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> { pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
bytes if self.buffer.bit_len() & 7 == 0 {
.iter() self.buffer.extends_from_slice(bytes);
.copied() } else {
.for_each(|chunk| self.push_bits(chunk as usize, 8)); bytes
.iter()
.copied()
.for_each(|chunk| self.push_bits(chunk as usize, 8));
}
Ok(()) Ok(())
} }

View file

@ -3,6 +3,7 @@ use bitbuffer::{
}; };
use std::fmt::Debug; use std::fmt::Debug;
#[track_caller]
fn roundtrip< fn roundtrip<
T: BitRead<'static, BigEndian> T: BitRead<'static, BigEndian>
+ BitWrite<BigEndian> + BitWrite<BigEndian>