1
0
Fork 0
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:
Robin Appelman 2019-03-03 01:42:56 +01:00
commit bd012039d0
6 changed files with 209 additions and 18 deletions

View file

@ -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,

View file

@ -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)))
}
}
}

View file

@ -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)]

View file

@ -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
View 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);

View file

@ -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);
// });