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

writing wip and bool be read fixes

This commit is contained in:
Robin Appelman 2020-02-13 02:20:08 +01:00
commit 8979354c60
9 changed files with 477 additions and 55 deletions

View file

@ -1,27 +0,0 @@
pub trait IsSigned {
fn is_signed() -> bool;
}
macro_rules! impl_is_signed {
($type:ty, $signed:expr) => {
impl IsSigned for $type {
#[inline(always)]
fn is_signed() -> bool {
$signed
}
}
};
}
impl_is_signed!(u8, false);
impl_is_signed!(u16, false);
impl_is_signed!(u32, false);
impl_is_signed!(u64, false);
impl_is_signed!(u128, false);
impl_is_signed!(usize, false);
impl_is_signed!(i8, true);
impl_is_signed!(i16, true);
impl_is_signed!(i32, true);
impl_is_signed!(i64, true);
impl_is_signed!(i128, true);
impl_is_signed!(isize, true);

View file

@ -64,13 +64,14 @@ pub use endianness::*;
pub use read::{BitRead, BitReadSized, LazyBitRead, LazyBitReadSized};
pub use readbuffer::BitReadBuffer;
pub use readstream::BitReadStream;
pub use writestream::BitWriteStream;
mod endianness;
mod is_signed;
mod num_traits;
mod read;
mod readbuffer;
mod readstream;
mod unchecked_primitive;
mod writestream;
/// Errors that can be returned when trying to read from a buffer
#[derive(Debug)]

View file

@ -1,3 +1,5 @@
/// some extra number traits
/// Allow casting floats unchecked
pub trait UncheckedPrimitiveFloat: Sized {
fn from_f32_unchecked(n: f32) -> Self;
@ -177,3 +179,59 @@ 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);
pub trait IsSigned {
fn is_signed() -> bool;
}
macro_rules! impl_is_signed {
($type:ty, $signed:expr) => {
impl IsSigned for $type {
#[inline(always)]
fn is_signed() -> bool {
$signed
}
}
};
}
pub trait IntoBytes: Sized {
fn into_bytes(self) -> Vec<u8>;
}
macro_rules! impl_into_bytes {
($type:ty) => {
impl IntoBytes for $type {
#[inline(always)]
fn into_bytes(self) -> Vec<u8> {
self.to_le_bytes().to_vec()
}
}
};
}
impl_is_signed!(u8, false);
impl_is_signed!(u16, false);
impl_is_signed!(u32, false);
impl_is_signed!(u64, false);
impl_is_signed!(u128, false);
impl_is_signed!(usize, false);
impl_is_signed!(i8, true);
impl_is_signed!(i16, true);
impl_is_signed!(i32, true);
impl_is_signed!(i64, true);
impl_is_signed!(i128, true);
impl_is_signed!(isize, true);
impl_into_bytes!(u8);
impl_into_bytes!(u16);
impl_into_bytes!(u32);
impl_into_bytes!(u64);
impl_into_bytes!(u128);
impl_into_bytes!(usize);
impl_into_bytes!(i8);
impl_into_bytes!(i16);
impl_into_bytes!(i32);
impl_into_bytes!(i64);
impl_into_bytes!(i128);
impl_into_bytes!(isize);

View file

@ -9,8 +9,7 @@ use std::rc::Rc;
use num_traits::{Float, PrimInt};
use crate::endianness::Endianness;
use crate::is_signed::IsSigned;
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
use crate::{ReadError, Result};
use std::convert::TryInto;
@ -74,6 +73,22 @@ where
}
}
pub(crate) fn get_bits_from_usize<E: Endianness>(
val: usize,
bit_offset: usize,
count: usize,
) -> usize {
let usize_bit_size = size_of::<usize>() * 8;
let shifted = if E::is_le() {
val >> bit_offset
} else {
val >> (usize_bit_size - bit_offset - count)
};
let mask = !(std::usize::MAX << count);
shifted & mask
}
impl<E> BitReadBuffer<E>
where
E: Endianness,
@ -109,7 +124,6 @@ where
unsafe fn read_usize(&self, position: usize, count: usize) -> usize {
let byte_index = position / 8;
let bit_offset = position & 7;
let usize_bit_size = size_of::<usize>() * 8;
let bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index);
@ -119,13 +133,7 @@ where
usize::from_be_bytes(bytes)
};
let shifted = if E::is_le() {
container >> bit_offset
} else {
container >> (usize_bit_size - bit_offset - count)
};
let mask = !(std::usize::MAX << count);
shifted & mask
get_bits_from_usize::<E>(container, bit_offset, count)
}
/// Read a single bit from the buffer as boolean
@ -160,8 +168,13 @@ where
if position < self.bit_len() {
let byte = self.bytes[byte_index];
let shifted = byte >> bit_offset;
Ok(shifted & 1u8 == 1)
if E::is_le() {
let shifted = byte >> bit_offset as u8;
Ok(shifted & 1u8 == 1)
} else {
let shifted = byte << bit_offset as u8;
Ok(shifted & 0b1000_0000u8 == 0b1000_0000u8)
}
} else {
Err(ReadError::NotEnoughData {
requested: 1,

View file

@ -4,8 +4,7 @@ use std::ops::BitOrAssign;
use num_traits::{Float, PrimInt};
use crate::endianness::Endianness;
use crate::is_signed::IsSigned;
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
use crate::BitReadBuffer;
use crate::{BitRead, BitReadSized, ReadError, Result};
use std::cmp::min;
@ -40,7 +39,7 @@ impl<E> BitReadStream<E>
where
E: Endianness,
{
/// Create a new stream for a [`BitBuffer`]
/// Create a new stream from a [`BitBuffer`]
///
/// # Examples
///

258
src/writestream.rs Normal file
View file

@ -0,0 +1,258 @@
use std::cmp::min;
use std::marker::PhantomData;
use std::mem::size_of;
use std::ops::{BitOrAssign, BitXor};
use num_traits::{Float, PrimInt};
use crate::endianness::Endianness;
use crate::num_traits::{IntoBytes, IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
use crate::readbuffer::get_bits_from_usize;
use crate::{LittleEndian, ReadError, Result};
use std::iter::{once, repeat};
const USIZE_SIZE: usize = size_of::<usize>();
const USIZE_BITS: usize = USIZE_SIZE * 8;
/// Stream that provides an a way to write non bit aligned adata
///
/// # Examples
///
/// ```
/// use bitbuffer::{BitWriteStream, LittleEndian};
/// # use bitbuffer::Result;
///
/// # fn main() -> Result<()> {
/// let mut stream = BitWriteStream::new(LittleEndian);
///
/// stream.write_bool(false)?;
/// stream.write_int(123u16, 15)?;
/// # Ok(())
/// # }
/// ```
///
/// [`BitBuffer`]: struct.BitBuffer.html
pub struct BitWriteStream<E>
where
E: Endianness,
{
bytes: Vec<u8>,
bit_len: usize,
endianness: PhantomData<E>,
}
impl<E> BitWriteStream<E>
where
E: Endianness,
{
/// Create a new write stream
///
/// # Examples
///
/// ```
/// use bitbuffer::{BitWriteStream, LittleEndian};
///
/// let mut stream = BitWriteStream::new(LittleEndian);
/// ```
pub fn new(_endianness: E) -> Self {
BitWriteStream {
bytes: Vec::new(),
bit_len: 0,
endianness: PhantomData,
}
}
}
impl<E> BitWriteStream<E>
where
E: Endianness,
{
/// The number of written bits in the buffer
pub fn bit_len(&self) -> usize {
self.bit_len
}
/// The number of written bytes in the buffer
pub fn byte_len(&self) -> usize {
self.bytes.len()
}
fn push_non_fit_bits(&mut self, bits: &[u8], count: usize) {
debug_assert!(bits.len() == count / 8);
let counts = repeat(8)
.take(bits.len() - 1)
.chain(once(count - (bits.len() - 1) * 8));
if E::is_le() {
bits.iter()
.copied()
.zip(counts)
.for_each(|(chunk, count)| self.push_bits(chunk as usize, count))
} else {
bits.iter()
.rev()
.copied()
.zip(counts)
.for_each(|(chunk, count)| self.push_bits(chunk as usize, count))
}
}
/// Push up to an usize worth of bits
fn push_bits(&mut self, bits: usize, count: usize) {
let bit_offset = self.bit_len & 7;
let byte_count = (count + 7) / 8;
if bit_offset == 0 {
if E::is_le() {
self.bytes
.extend_from_slice(&bits.to_le_bytes()[0..byte_count])
} else {
let bytes = (bits << (USIZE_BITS - bit_offset - count)).to_be_bytes();
self.bytes.extend_from_slice(&bytes[0..byte_count])
}
self.bit_len += count;
} else {
if E::is_le() {
let first_part_length = min(USIZE_SIZE - bit_offset, count);
let first_part = get_bits_from_usize::<E>(bits, 0, first_part_length) as u8;
let last_written_byte = self.bytes.pop().unwrap_or(0);
let merged_byte = last_written_byte | (first_part << bit_offset as u8);
self.bytes.push(merged_byte);
self.bit_len += first_part_length;
if first_part_length < count {
let second_part = get_bits_from_usize::<E>(
bits,
first_part_length,
count - first_part_length,
);
self.push_bits(second_part, count - first_part_length);
}
} else {
let first_part_length = min(USIZE_SIZE - bit_offset, count);
let first_part = get_bits_from_usize::<LittleEndian>(
bits,
count - first_part_length,
first_part_length,
) as u8;
let last_written_byte = self.bytes.pop().unwrap_or(0);
let merged_byte =
last_written_byte | first_part << (8 - bit_offset - first_part_length) as u8;
self.bytes.push(merged_byte);
self.bit_len += first_part_length;
if first_part_length < count {
let second_part =
get_bits_from_usize::<LittleEndian>(bits, 0, count - first_part_length);
self.push_bits(second_part, count - first_part_length);
}
}
}
}
/// Write a boolean into the buffer
///
/// # Examples
///
/// ```
/// # use bitbuffer::{BitReadBuffer, LittleEndian, Result};
/// #
/// # fn main() -> Result<()> {
/// # use bitbuffer::{BitWriteStream, LittleEndian};
///
/// let mut stream = BitWriteStream::new(LittleEndian);
/// stream.write_bool(true)?;
/// #
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn write_bool(&mut self, value: bool) -> Result<()> {
self.push_bits(value as usize, 1);
Ok(())
}
/// Write an integer into the buffer
///
/// # Examples
///
/// ```
/// # use bitbuffer::{BitReadBuffer, LittleEndian, Result};
/// #
/// # fn main() -> Result<()> {
/// # use bitbuffer::{BitWriteStream, LittleEndian};
///
/// let mut stream = BitWriteStream::new(LittleEndian);
/// stream.write_int(123u16, 15)?;
/// #
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn write_int<T>(&mut self, value: T, count: usize) -> Result<()>
where
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + IntoBytes,
{
let type_bit_size = size_of::<T>() * 8;
if type_bit_size < count {
return Err(ReadError::TooManyBits {
requested: count,
max: type_bit_size,
});
}
if type_bit_size < USIZE_BITS {
if T::is_signed() {
todo!()
} else {
self.push_bits(value.into_usize_unchecked(), count);
}
} else {
self.push_non_fit_bits(&value.into_bytes(), count)
}
Ok(())
}
/// Write a float into the buffer
///
/// # Examples
///
/// ```
/// # use bitbuffer::{BitReadBuffer, LittleEndian, Result};
/// #
/// # fn main() -> Result<()> {
/// # use bitbuffer::{BitWriteStream, LittleEndian};
///
/// let mut stream = BitWriteStream::new(LittleEndian);
/// stream.write_float(123.15f32)?;
/// #
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn write_float<T>(&mut self, value: T) -> Result<()>
where
T: Float + UncheckedPrimitiveFloat,
{
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_le_bytes(), 32)
};
} else {
self.push_non_fit_bits(&value.to_f64().unwrap().to_le_bytes(), 64)
}
Ok(())
}
/// Convert the write buffer into the written bytes
pub fn finish(self) -> Vec<u8> {
self.bytes
}
}