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:
parent
0e239cc011
commit
8979354c60
9 changed files with 477 additions and 55 deletions
|
|
@ -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);
|
||||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
258
src/writestream.rs
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue