mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
use unchecked type conversions
this is safe because we already checked their bit size
This commit is contained in:
parent
8fdcd6b4c4
commit
bd012039d0
6 changed files with 209 additions and 18 deletions
|
|
@ -138,7 +138,6 @@ extern crate proc_macro;
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::token::Token;
|
||||
use syn::{
|
||||
parse_macro_input, parse_quote, parse_str, Attribute, Data, DeriveInput, Expr, Fields, Ident,
|
||||
Lit, LitStr, Meta, Pat, Path,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use num_traits::{Float, PrimInt};
|
|||
|
||||
use crate::endianness::Endianness;
|
||||
use crate::is_signed::IsSigned;
|
||||
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||
use crate::{ReadError, Result};
|
||||
|
||||
const USIZE_SIZE: usize = size_of::<usize>();
|
||||
|
|
@ -192,7 +193,7 @@ where
|
|||
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
||||
pub fn read_int<T>(&self, position: usize, count: usize) -> Result<T>
|
||||
where
|
||||
T: PrimInt + BitOrAssign + IsSigned,
|
||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||
{
|
||||
let value = {
|
||||
let type_bit_size = size_of::<T>() * 8;
|
||||
|
|
@ -210,9 +211,9 @@ where
|
|||
let raw = self.read_usize(position, count)?;
|
||||
let max_signed_value = (1 << (type_bit_size - 1)) - 1;
|
||||
if T::is_signed() && raw > max_signed_value {
|
||||
return Ok(T::zero() - T::from(raw & max_signed_value).unwrap());
|
||||
return Ok(T::zero() - T::from_unchecked(raw & max_signed_value));
|
||||
} else {
|
||||
T::from(raw).unwrap()
|
||||
T::from_unchecked(raw)
|
||||
}
|
||||
} else {
|
||||
let mut left_to_read = count;
|
||||
|
|
@ -223,7 +224,7 @@ where
|
|||
while left_to_read > 0 {
|
||||
let bits_left = self.bit_len - read_pos;
|
||||
let read = min(min(left_to_read, max_read), bits_left);
|
||||
let data = T::from(self.read_usize(read_pos, read)?).unwrap();
|
||||
let data = T::from_unchecked(self.read_usize(read_pos, read)?);
|
||||
if E::is_le() {
|
||||
partial |= data << bit_offset;
|
||||
} else {
|
||||
|
|
@ -400,14 +401,18 @@ where
|
|||
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||
pub fn read_float<T>(&self, position: usize) -> Result<T>
|
||||
where
|
||||
T: Float,
|
||||
T: Float + UncheckedPrimitiveFloat,
|
||||
{
|
||||
if size_of::<T>() == 4 {
|
||||
let int = self.read_int::<u32>(position, 32)?;
|
||||
Ok(T::from(f32::from_bits(int)).unwrap())
|
||||
let int = if size_of::<T>() < USIZE_SIZE {
|
||||
self.read_usize(position, 32)? as u32
|
||||
} else {
|
||||
self.read_int::<u32>(position, 32)?
|
||||
};
|
||||
Ok(T::from_f32_unchecked(f32::from_bits(int)))
|
||||
} else {
|
||||
let int = self.read_int::<u64>(position, 64)?;
|
||||
Ok(T::from(f64::from_bits(int)).unwrap())
|
||||
Ok(T::from_f64_unchecked(f64::from_bits(int)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ mod endianness;
|
|||
mod is_signed;
|
||||
mod read;
|
||||
mod stream;
|
||||
mod unchecked_primitive;
|
||||
|
||||
/// Errors that can be returned when trying to read from a buffer
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use num_traits::{Float, PrimInt};
|
|||
|
||||
use crate::endianness::Endianness;
|
||||
use crate::is_signed::IsSigned;
|
||||
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||
use crate::BitBuffer;
|
||||
use crate::{BitRead, BitReadSized, ReadError, Result};
|
||||
|
||||
|
|
@ -143,7 +144,7 @@ where
|
|||
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
||||
pub fn read_int<T>(&mut self, count: usize) -> Result<T>
|
||||
where
|
||||
T: PrimInt + BitOrAssign + IsSigned,
|
||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||
{
|
||||
self.verify_bits_left(count)?;
|
||||
let result = self.buffer.read_int(self.pos, count);
|
||||
|
|
@ -181,7 +182,7 @@ where
|
|||
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||
pub fn read_float<T>(&mut self) -> Result<T>
|
||||
where
|
||||
T: Float,
|
||||
T: Float + UncheckedPrimitiveFloat,
|
||||
{
|
||||
let count = size_of::<T>() * 8;
|
||||
self.verify_bits_left(count)?;
|
||||
|
|
|
|||
179
src/unchecked_primitive.rs
Normal file
179
src/unchecked_primitive.rs
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/// Allow casting floats unchecked
|
||||
pub trait UncheckedPrimitiveFloat: Sized {
|
||||
fn from_f32_unchecked(n: f32) -> Self;
|
||||
fn from_f64_unchecked(n: f64) -> Self;
|
||||
}
|
||||
|
||||
impl UncheckedPrimitiveFloat for f32 {
|
||||
#[inline(always)]
|
||||
fn from_f32_unchecked(n: f32) -> Self {
|
||||
n
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_f64_unchecked(n: f64) -> Self {
|
||||
n as f32
|
||||
}
|
||||
}
|
||||
|
||||
impl UncheckedPrimitiveFloat for f64 {
|
||||
#[inline(always)]
|
||||
fn from_f32_unchecked(n: f32) -> Self {
|
||||
n as f64
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_f64_unchecked(n: f64) -> Self {
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow casting integers unchecked
|
||||
pub trait UncheckedPrimitiveInt: Sized {
|
||||
fn from_u8_unchecked(n: u8) -> Self;
|
||||
fn from_i8_unchecked(n: i8) -> Self;
|
||||
fn from_u16_unchecked(n: u16) -> Self;
|
||||
fn from_i16_unchecked(n: i16) -> Self;
|
||||
fn from_u32_unchecked(n: u32) -> Self;
|
||||
fn from_i32_unchecked(n: i32) -> Self;
|
||||
fn from_u64_unchecked(n: u64) -> Self;
|
||||
fn from_i64_unchecked(n: i64) -> Self;
|
||||
fn from_u128_unchecked(n: u128) -> Self;
|
||||
fn from_i128_unchecked(n: i128) -> Self;
|
||||
fn from_usize_unchecked(n: usize) -> Self;
|
||||
fn from_isize_unchecked(n: isize) -> Self;
|
||||
|
||||
fn into_u8_unchecked(self) -> u8;
|
||||
fn into_i8_unchecked(self) -> i8;
|
||||
fn into_u16_unchecked(self) -> u16;
|
||||
fn into_i16_unchecked(self) -> i16;
|
||||
fn into_u32_unchecked(self) -> u32;
|
||||
fn into_i32_unchecked(self) -> i32;
|
||||
fn into_u64_unchecked(self) -> u64;
|
||||
fn into_i64_unchecked(self) -> i64;
|
||||
fn into_u128_unchecked(self) -> u128;
|
||||
fn into_i128_unchecked(self) -> i128;
|
||||
fn into_usize_unchecked(self) -> usize;
|
||||
fn into_isize_unchecked(self) -> isize;
|
||||
|
||||
fn from_unchecked<N: UncheckedPrimitiveInt>(n: N) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_unchecked_int {
|
||||
($type:ty, $conv:ident) => {
|
||||
impl UncheckedPrimitiveInt for $type {
|
||||
#[inline(always)]
|
||||
fn from_u8_unchecked(n: u8) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_i8_unchecked(n: i8) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u16_unchecked(n: u16) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_i16_unchecked(n: i16) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u32_unchecked(n: u32) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_i32_unchecked(n: i32) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u64_unchecked(n: u64) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_i64_unchecked(n: i64) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_u128_unchecked(n: u128) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_i128_unchecked(n: i128) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_usize_unchecked(n: usize) -> Self {
|
||||
n as $type
|
||||
}
|
||||
#[inline(always)]
|
||||
fn from_isize_unchecked(n: isize) -> Self {
|
||||
n as $type
|
||||
}
|
||||
|
||||
fn into_u8_unchecked(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_i8_unchecked(self) -> i8 {
|
||||
self as i8
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_u16_unchecked(self) -> u16 {
|
||||
self as u16
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_i16_unchecked(self) -> i16 {
|
||||
self as i16
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_u32_unchecked(self) -> u32 {
|
||||
self as u32
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_i32_unchecked(self) -> i32 {
|
||||
self as i32
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_u64_unchecked(self) -> u64 {
|
||||
self as u64
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_i64_unchecked(self) -> i64 {
|
||||
self as i64
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_u128_unchecked(self) -> u128 {
|
||||
self as u128
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_i128_unchecked(self) -> i128 {
|
||||
self as i128
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_usize_unchecked(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
#[inline(always)]
|
||||
fn into_isize_unchecked(self) -> isize {
|
||||
self as isize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_unchecked<N: UncheckedPrimitiveInt>(n: N) -> Self {
|
||||
n.$conv()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_unchecked_int!(u8, into_u8_unchecked);
|
||||
impl_unchecked_int!(i8, into_i8_unchecked);
|
||||
impl_unchecked_int!(u16, into_u16_unchecked);
|
||||
impl_unchecked_int!(i16, into_i16_unchecked);
|
||||
impl_unchecked_int!(u32, into_u32_unchecked);
|
||||
impl_unchecked_int!(i32, into_i32_unchecked);
|
||||
impl_unchecked_int!(u64, into_u64_unchecked);
|
||||
impl_unchecked_int!(i64, into_i64_unchecked);
|
||||
impl_unchecked_int!(u128, into_u128_unchecked);
|
||||
impl_unchecked_int!(i128, into_i128_unchecked);
|
||||
impl_unchecked_int!(usize, into_usize_unchecked);
|
||||
impl_unchecked_int!(isize, into_isize_unchecked);
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
use bitstream_reader::{BigEndian, BitBuffer, BitRead, BitStream, LittleEndian};
|
||||
use maplit::hashmap;
|
||||
use std::collections::HashMap;
|
||||
//#![feature(test)]
|
||||
|
||||
// for bench on nightly
|
||||
//extern crate test;
|
||||
|
||||
use std::collections::HashMap;
|
||||
// for bench on nightly
|
||||
//use std::fs;
|
||||
//use test::Bencher;
|
||||
|
||||
use maplit::hashmap;
|
||||
|
||||
use bitstream_reader::{BigEndian, BitBuffer, BitRead, BitStream, LittleEndian};
|
||||
|
||||
const BYTES: &'static [u8] = &[
|
||||
0b1011_0101,
|
||||
0b0110_1010,
|
||||
|
|
@ -372,7 +378,7 @@ fn test_read_struct() {
|
|||
// 0b1110_0111,
|
||||
|
||||
// for bench on nightly
|
||||
//fn read_perf<P: IsPadded>(buffer: BitBuffer<LittleEndian, P>) -> u16 {
|
||||
//fn read_perf(buffer: &BitBuffer<LittleEndian>) -> u16 {
|
||||
// let size = 5;
|
||||
// let mut pos = 0;
|
||||
// let len = buffer.bit_len();
|
||||
|
|
@ -386,6 +392,7 @@ fn test_read_struct() {
|
|||
// pos += size;
|
||||
// }
|
||||
//}
|
||||
|
||||
//
|
||||
//#[bench]
|
||||
//fn perf_padded(b: &mut Bencher) {
|
||||
|
|
@ -404,10 +411,9 @@ fn test_read_struct() {
|
|||
//#[bench]
|
||||
//fn perf_non_padded(b: &mut Bencher) {
|
||||
// let file = fs::read("/bulk/tmp/test.dem").expect("Unable to read file");
|
||||
// let bytes = file.as_slice();
|
||||
// let buffer = BitBuffer::new(file, LittleEndian);
|
||||
// b.iter(|| {
|
||||
// let buffer = BitBuffer::new(&bytes, LittleEndian);
|
||||
// let data = read_perf(buffer);
|
||||
// let data = read_perf(&buffer);
|
||||
// assert_eq!(data, 43943);
|
||||
// test::black_box(data);
|
||||
// });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue