mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 08:34:07 +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 proc_macro2::{Span, TokenStream};
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
use syn::token::Token;
|
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, parse_str, Attribute, Data, DeriveInput, Expr, Fields, Ident,
|
parse_macro_input, parse_quote, parse_str, Attribute, Data, DeriveInput, Expr, Fields, Ident,
|
||||||
Lit, LitStr, Meta, Pat, Path,
|
Lit, LitStr, Meta, Pat, Path,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use num_traits::{Float, PrimInt};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::is_signed::IsSigned;
|
use crate::is_signed::IsSigned;
|
||||||
|
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
use crate::{ReadError, Result};
|
use crate::{ReadError, Result};
|
||||||
|
|
||||||
const USIZE_SIZE: usize = size_of::<usize>();
|
const USIZE_SIZE: usize = size_of::<usize>();
|
||||||
|
|
@ -192,7 +193,7 @@ where
|
||||||
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
||||||
pub fn read_int<T>(&self, position: usize, count: usize) -> Result<T>
|
pub fn read_int<T>(&self, position: usize, count: usize) -> Result<T>
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||||
{
|
{
|
||||||
let value = {
|
let value = {
|
||||||
let type_bit_size = size_of::<T>() * 8;
|
let type_bit_size = size_of::<T>() * 8;
|
||||||
|
|
@ -210,9 +211,9 @@ where
|
||||||
let raw = self.read_usize(position, count)?;
|
let raw = self.read_usize(position, count)?;
|
||||||
let max_signed_value = (1 << (type_bit_size - 1)) - 1;
|
let max_signed_value = (1 << (type_bit_size - 1)) - 1;
|
||||||
if T::is_signed() && raw > max_signed_value {
|
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 {
|
} else {
|
||||||
T::from(raw).unwrap()
|
T::from_unchecked(raw)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut left_to_read = count;
|
let mut left_to_read = count;
|
||||||
|
|
@ -223,7 +224,7 @@ where
|
||||||
while left_to_read > 0 {
|
while left_to_read > 0 {
|
||||||
let bits_left = self.bit_len - read_pos;
|
let bits_left = self.bit_len - read_pos;
|
||||||
let read = min(min(left_to_read, max_read), bits_left);
|
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() {
|
if E::is_le() {
|
||||||
partial |= data << bit_offset;
|
partial |= data << bit_offset;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -400,14 +401,18 @@ where
|
||||||
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||||
pub fn read_float<T>(&self, position: usize) -> Result<T>
|
pub fn read_float<T>(&self, position: usize) -> Result<T>
|
||||||
where
|
where
|
||||||
T: Float,
|
T: Float + UncheckedPrimitiveFloat,
|
||||||
{
|
{
|
||||||
if size_of::<T>() == 4 {
|
if size_of::<T>() == 4 {
|
||||||
let int = self.read_int::<u32>(position, 32)?;
|
let int = if size_of::<T>() < USIZE_SIZE {
|
||||||
Ok(T::from(f32::from_bits(int)).unwrap())
|
self.read_usize(position, 32)? as u32
|
||||||
|
} else {
|
||||||
|
self.read_int::<u32>(position, 32)?
|
||||||
|
};
|
||||||
|
Ok(T::from_f32_unchecked(f32::from_bits(int)))
|
||||||
} else {
|
} else {
|
||||||
let int = self.read_int::<u64>(position, 64)?;
|
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 is_signed;
|
||||||
mod read;
|
mod read;
|
||||||
mod stream;
|
mod stream;
|
||||||
|
mod unchecked_primitive;
|
||||||
|
|
||||||
/// Errors that can be returned when trying to read from a buffer
|
/// Errors that can be returned when trying to read from a buffer
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use num_traits::{Float, PrimInt};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::is_signed::IsSigned;
|
use crate::is_signed::IsSigned;
|
||||||
|
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
use crate::BitBuffer;
|
use crate::BitBuffer;
|
||||||
use crate::{BitRead, BitReadSized, ReadError, Result};
|
use crate::{BitRead, BitReadSized, ReadError, Result};
|
||||||
|
|
||||||
|
|
@ -143,7 +144,7 @@ where
|
||||||
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
/// [`ReadError::TooManyBits`]: enum.ReadError.html#variant.TooManyBits
|
||||||
pub fn read_int<T>(&mut self, count: usize) -> Result<T>
|
pub fn read_int<T>(&mut self, count: usize) -> Result<T>
|
||||||
where
|
where
|
||||||
T: PrimInt + BitOrAssign + IsSigned,
|
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt,
|
||||||
{
|
{
|
||||||
self.verify_bits_left(count)?;
|
self.verify_bits_left(count)?;
|
||||||
let result = self.buffer.read_int(self.pos, count);
|
let result = self.buffer.read_int(self.pos, count);
|
||||||
|
|
@ -181,7 +182,7 @@ where
|
||||||
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
/// [`ReadError::NotEnoughData`]: enum.ReadError.html#variant.NotEnoughData
|
||||||
pub fn read_float<T>(&mut self) -> Result<T>
|
pub fn read_float<T>(&mut self) -> Result<T>
|
||||||
where
|
where
|
||||||
T: Float,
|
T: Float + UncheckedPrimitiveFloat,
|
||||||
{
|
{
|
||||||
let count = size_of::<T>() * 8;
|
let count = size_of::<T>() * 8;
|
||||||
self.verify_bits_left(count)?;
|
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};
|
//#![feature(test)]
|
||||||
use maplit::hashmap;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
|
// for bench on nightly
|
||||||
|
//extern crate test;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
// for bench on nightly
|
// for bench on nightly
|
||||||
//use std::fs;
|
//use std::fs;
|
||||||
//use test::Bencher;
|
//use test::Bencher;
|
||||||
|
|
||||||
|
use maplit::hashmap;
|
||||||
|
|
||||||
|
use bitstream_reader::{BigEndian, BitBuffer, BitRead, BitStream, LittleEndian};
|
||||||
|
|
||||||
const BYTES: &'static [u8] = &[
|
const BYTES: &'static [u8] = &[
|
||||||
0b1011_0101,
|
0b1011_0101,
|
||||||
0b0110_1010,
|
0b0110_1010,
|
||||||
|
|
@ -372,7 +378,7 @@ fn test_read_struct() {
|
||||||
// 0b1110_0111,
|
// 0b1110_0111,
|
||||||
|
|
||||||
// for bench on nightly
|
// 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 size = 5;
|
||||||
// let mut pos = 0;
|
// let mut pos = 0;
|
||||||
// let len = buffer.bit_len();
|
// let len = buffer.bit_len();
|
||||||
|
|
@ -386,6 +392,7 @@ fn test_read_struct() {
|
||||||
// pos += size;
|
// pos += size;
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
//
|
//
|
||||||
//#[bench]
|
//#[bench]
|
||||||
//fn perf_padded(b: &mut Bencher) {
|
//fn perf_padded(b: &mut Bencher) {
|
||||||
|
|
@ -404,10 +411,9 @@ fn test_read_struct() {
|
||||||
//#[bench]
|
//#[bench]
|
||||||
//fn perf_non_padded(b: &mut Bencher) {
|
//fn perf_non_padded(b: &mut Bencher) {
|
||||||
// let file = fs::read("/bulk/tmp/test.dem").expect("Unable to read file");
|
// 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(|| {
|
// b.iter(|| {
|
||||||
// let buffer = BitBuffer::new(&bytes, LittleEndian);
|
// let data = read_perf(&buffer);
|
||||||
// let data = read_perf(buffer);
|
|
||||||
// assert_eq!(data, 43943);
|
// assert_eq!(data, 43943);
|
||||||
// test::black_box(data);
|
// test::black_box(data);
|
||||||
// });
|
// });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue