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
|
|
@ -2,7 +2,7 @@
|
||||||
#![allow(unreachable_patterns)]
|
#![allow(unreachable_patterns)]
|
||||||
|
|
||||||
use bitbuffer::{
|
use bitbuffer::{
|
||||||
bit_size_of, bit_size_of_sized, BigEndian, BitReadBuffer, BitReadStream, Endianness,
|
bit_size_of, bit_size_of_sized, BigEndian, BitReadStream, BitWriteBuffer, Endianness,
|
||||||
LittleEndian,
|
LittleEndian,
|
||||||
};
|
};
|
||||||
use bitbuffer_derive::{BitRead, BitReadSized};
|
use bitbuffer_derive::{BitRead, BitReadSized};
|
||||||
|
|
@ -45,7 +45,7 @@ fn test_read_struct() {
|
||||||
0b0101_0101,
|
0b0101_0101,
|
||||||
0b1010_1010,
|
0b1010_1010,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, LittleEndian);
|
let buffer = BitWriteBuffer::new(bytes, LittleEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TestStruct {
|
TestStruct {
|
||||||
|
|
@ -83,7 +83,7 @@ fn test_read_bare_enum() {
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(TestBareEnum::Asd, stream.read().unwrap());
|
assert_eq!(TestBareEnum::Asd, stream.read().unwrap());
|
||||||
assert_eq!(TestBareEnum::Foo, stream.read().unwrap());
|
assert_eq!(TestBareEnum::Foo, stream.read().unwrap());
|
||||||
|
|
@ -114,7 +114,7 @@ fn test_read_unnamed_field_enum() {
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TestUnnamedFieldEnum::Asd(0b_00_0110_10),
|
TestUnnamedFieldEnum::Asd(0b_00_0110_10),
|
||||||
|
|
@ -144,7 +144,7 @@ fn test_read_struct_sized() {
|
||||||
let bytes = vec![
|
let bytes = vec![
|
||||||
12, 'h' as u8, 'e' as u8, 'l' as u8, 'l' as u8, 'o' as u8, 0, 0, 0, 0, 0, 0,
|
12, 'h' as u8, 'e' as u8, 'l' as u8, 'l' as u8, 'o' as u8, 0, 0, 0, 0, 0, 0,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, LittleEndian);
|
let buffer = BitWriteBuffer::new(bytes, LittleEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TestStructSized {
|
TestStructSized {
|
||||||
|
|
@ -180,7 +180,7 @@ fn test_read_unnamed_field_enum_sized() {
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TestUnnamedFieldEnumSized::Asd(0b_00_0110),
|
TestUnnamedFieldEnumSized::Asd(0b_00_0110),
|
||||||
|
|
@ -213,7 +213,7 @@ fn test_read_struct2() {
|
||||||
'l' as u8,
|
'l' as u8,
|
||||||
'e' as u8,
|
'e' as u8,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TestStruct2 {
|
TestStruct2 {
|
||||||
|
|
@ -236,7 +236,7 @@ struct TestStruct3<E: Endianness> {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_struct3() {
|
fn test_read_struct3() {
|
||||||
let bytes = vec![0b0000_0101, 0, 0, 0, 0, 0, 0, 0];
|
let bytes = vec![0b0000_0101, 0, 0, 0, 0, 0, 0, 0];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
let result: TestStruct3<BigEndian> = stream.read().unwrap();
|
let result: TestStruct3<BigEndian> = stream.read().unwrap();
|
||||||
assert_eq!(5, result.size);
|
assert_eq!(5, result.size);
|
||||||
|
|
@ -265,7 +265,7 @@ fn test_read_rest_enum() {
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
0b1000_0100,
|
0b1000_0100,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(TestEnumRest::Asd, stream.read().unwrap());
|
assert_eq!(TestEnumRest::Asd, stream.read().unwrap());
|
||||||
assert_eq!(TestEnumRest::Foo, stream.read().unwrap());
|
assert_eq!(TestEnumRest::Foo, stream.read().unwrap());
|
||||||
|
|
@ -281,7 +281,7 @@ fn test_unnamed_struct() {
|
||||||
let bytes = vec![
|
let bytes = vec![
|
||||||
12, 'h' as u8, 'e' as u8, 'l' as u8, 'l' as u8, 'o' as u8, 0, 0, 0, 0, 0, 0,
|
12, 'h' as u8, 'e' as u8, 'l' as u8, 'l' as u8, 'o' as u8, 0, 0, 0, 0, 0, 0,
|
||||||
];
|
];
|
||||||
let buffer = BitReadBuffer::new(bytes, LittleEndian);
|
let buffer = BitWriteBuffer::new(bytes, LittleEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -295,7 +295,7 @@ struct EmptyStruct;
|
||||||
|
|
||||||
fn test_empty_struct() {
|
fn test_empty_struct() {
|
||||||
let bytes = vec![0, 0, 0, 0];
|
let bytes = vec![0, 0, 0, 0];
|
||||||
let buffer = BitReadBuffer::new(bytes, BigEndian);
|
let buffer = BitWriteBuffer::new(bytes, BigEndian);
|
||||||
let mut stream = BitReadStream::from(buffer);
|
let mut stream = BitReadStream::from(buffer);
|
||||||
assert_eq!(EmptyStruct, stream.read().unwrap());
|
assert_eq!(EmptyStruct, stream.read().unwrap());
|
||||||
assert_eq!(0, stream.pos());
|
assert_eq!(0, stream.pos());
|
||||||
|
|
|
||||||
|
|
@ -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 read::{BitRead, BitReadSized, LazyBitRead, LazyBitReadSized};
|
||||||
pub use readbuffer::BitReadBuffer;
|
pub use readbuffer::BitReadBuffer;
|
||||||
pub use readstream::BitReadStream;
|
pub use readstream::BitReadStream;
|
||||||
|
pub use writestream::BitWriteStream;
|
||||||
|
|
||||||
mod endianness;
|
mod endianness;
|
||||||
mod is_signed;
|
mod num_traits;
|
||||||
mod read;
|
mod read;
|
||||||
mod readbuffer;
|
mod readbuffer;
|
||||||
mod readstream;
|
mod readstream;
|
||||||
mod unchecked_primitive;
|
mod writestream;
|
||||||
|
|
||||||
/// 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)]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// some extra number traits
|
||||||
|
|
||||||
/// Allow casting floats unchecked
|
/// Allow casting floats unchecked
|
||||||
pub trait UncheckedPrimitiveFloat: Sized {
|
pub trait UncheckedPrimitiveFloat: Sized {
|
||||||
fn from_f32_unchecked(n: f32) -> Self;
|
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!(i128, into_i128_unchecked);
|
||||||
impl_unchecked_int!(usize, into_usize_unchecked);
|
impl_unchecked_int!(usize, into_usize_unchecked);
|
||||||
impl_unchecked_int!(isize, into_isize_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 num_traits::{Float, PrimInt};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::is_signed::IsSigned;
|
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
|
||||||
use crate::{ReadError, Result};
|
use crate::{ReadError, Result};
|
||||||
use std::convert::TryInto;
|
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>
|
impl<E> BitReadBuffer<E>
|
||||||
where
|
where
|
||||||
E: Endianness,
|
E: Endianness,
|
||||||
|
|
@ -109,7 +124,6 @@ where
|
||||||
unsafe fn read_usize(&self, position: usize, count: usize) -> usize {
|
unsafe fn read_usize(&self, position: usize, count: usize) -> usize {
|
||||||
let byte_index = position / 8;
|
let byte_index = position / 8;
|
||||||
let bit_offset = position & 7;
|
let bit_offset = position & 7;
|
||||||
let usize_bit_size = size_of::<usize>() * 8;
|
|
||||||
|
|
||||||
let bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index);
|
let bytes: [u8; USIZE_SIZE] = self.read_usize_bytes(byte_index);
|
||||||
|
|
||||||
|
|
@ -119,13 +133,7 @@ where
|
||||||
usize::from_be_bytes(bytes)
|
usize::from_be_bytes(bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
let shifted = if E::is_le() {
|
get_bits_from_usize::<E>(container, bit_offset, count)
|
||||||
container >> bit_offset
|
|
||||||
} else {
|
|
||||||
container >> (usize_bit_size - bit_offset - count)
|
|
||||||
};
|
|
||||||
let mask = !(std::usize::MAX << count);
|
|
||||||
shifted & mask
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a single bit from the buffer as boolean
|
/// Read a single bit from the buffer as boolean
|
||||||
|
|
@ -160,8 +168,13 @@ where
|
||||||
|
|
||||||
if position < self.bit_len() {
|
if position < self.bit_len() {
|
||||||
let byte = self.bytes[byte_index];
|
let byte = self.bytes[byte_index];
|
||||||
let shifted = byte >> bit_offset;
|
if E::is_le() {
|
||||||
Ok(shifted & 1u8 == 1)
|
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 {
|
} else {
|
||||||
Err(ReadError::NotEnoughData {
|
Err(ReadError::NotEnoughData {
|
||||||
requested: 1,
|
requested: 1,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ use std::ops::BitOrAssign;
|
||||||
use num_traits::{Float, PrimInt};
|
use num_traits::{Float, PrimInt};
|
||||||
|
|
||||||
use crate::endianness::Endianness;
|
use crate::endianness::Endianness;
|
||||||
use crate::is_signed::IsSigned;
|
use crate::num_traits::{IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||||
use crate::unchecked_primitive::{UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
|
||||||
use crate::BitReadBuffer;
|
use crate::BitReadBuffer;
|
||||||
use crate::{BitRead, BitReadSized, ReadError, Result};
|
use crate::{BitRead, BitReadSized, ReadError, Result};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
@ -40,7 +39,7 @@ impl<E> BitReadStream<E>
|
||||||
where
|
where
|
||||||
E: Endianness,
|
E: Endianness,
|
||||||
{
|
{
|
||||||
/// Create a new stream for a [`BitBuffer`]
|
/// Create a new stream from a [`BitBuffer`]
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,9 @@ fn read_u8_le() {
|
||||||
let buffer = BitReadBuffer::new(BYTES.to_vec(), LittleEndian);
|
let buffer = BitReadBuffer::new(BYTES.to_vec(), LittleEndian);
|
||||||
|
|
||||||
assert_eq!(buffer.read_int::<u8>(0, 1).unwrap(), 0b1);
|
assert_eq!(buffer.read_int::<u8>(0, 1).unwrap(), 0b1);
|
||||||
|
assert_eq!(buffer.read_bool(0).unwrap(), true);
|
||||||
assert_eq!(buffer.read_int::<u8>(1, 1).unwrap(), 0b0);
|
assert_eq!(buffer.read_int::<u8>(1, 1).unwrap(), 0b0);
|
||||||
|
assert_eq!(buffer.read_bool(1).unwrap(), false);
|
||||||
assert_eq!(buffer.read_int::<u8>(2, 2).unwrap(), 0b01);
|
assert_eq!(buffer.read_int::<u8>(2, 2).unwrap(), 0b01);
|
||||||
assert_eq!(buffer.read_int::<u8>(0, 3).unwrap(), 0b101);
|
assert_eq!(buffer.read_int::<u8>(0, 3).unwrap(), 0b101);
|
||||||
assert_eq!(buffer.read_int::<u8>(7, 5).unwrap(), 0b1010_1);
|
assert_eq!(buffer.read_int::<u8>(7, 5).unwrap(), 0b1010_1);
|
||||||
|
|
@ -43,6 +45,9 @@ fn read_u8_be() {
|
||||||
assert_eq!(buffer.read_int::<u8>(0, 3).unwrap(), 0b101);
|
assert_eq!(buffer.read_int::<u8>(0, 3).unwrap(), 0b101);
|
||||||
assert_eq!(buffer.read_int::<u8>(7, 5).unwrap(), 0b1011_0);
|
assert_eq!(buffer.read_int::<u8>(7, 5).unwrap(), 0b1011_0);
|
||||||
assert_eq!(buffer.read_int::<u8>(6, 5).unwrap(), 0b01_011);
|
assert_eq!(buffer.read_int::<u8>(6, 5).unwrap(), 0b01_011);
|
||||||
|
|
||||||
|
assert_eq!(buffer.read_bool(0).unwrap(), true);
|
||||||
|
assert_eq!(buffer.read_bool(8).unwrap(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
115
tests/write_tests.rs
Normal file
115
tests/write_tests.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
use bitbuffer::{BigEndian, BitReadBuffer, BitReadStream, BitWriteStream, LittleEndian};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_bool_le() {
|
||||||
|
let mut stream = BitWriteStream::new(LittleEndian);
|
||||||
|
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_bool(false).unwrap();
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
|
||||||
|
let data = stream.finish();
|
||||||
|
let mut read = BitReadStream::from(BitReadBuffer::new(data, LittleEndian));
|
||||||
|
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
|
||||||
|
// 0 padded
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_bool_be() {
|
||||||
|
let mut stream = BitWriteStream::new(BigEndian);
|
||||||
|
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_bool(false).unwrap();
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
|
||||||
|
let data = stream.finish();
|
||||||
|
let mut read = BitReadStream::from(BitReadBuffer::new(data, BigEndian));
|
||||||
|
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
|
||||||
|
// 0 padded
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_bool_number_le() {
|
||||||
|
let mut stream = BitWriteStream::new(LittleEndian);
|
||||||
|
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_int(3253u16, 16).unwrap();
|
||||||
|
stream.write_int(13253u64, 64).unwrap();
|
||||||
|
|
||||||
|
let data = stream.finish();
|
||||||
|
let mut read = BitReadStream::from(BitReadBuffer::new(data, LittleEndian));
|
||||||
|
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
assert_eq!(3253u16, read.read().unwrap());
|
||||||
|
assert_eq!(13253u64, read.read().unwrap());
|
||||||
|
|
||||||
|
// 0 padded
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_bool_number_be() {
|
||||||
|
let mut stream = BitWriteStream::new(BigEndian);
|
||||||
|
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_int(3253u16, 16).unwrap();
|
||||||
|
stream.write_int(13253u64, 64).unwrap();
|
||||||
|
|
||||||
|
let data = stream.finish();
|
||||||
|
let mut read = BitReadStream::from(BitReadBuffer::new(data, BigEndian));
|
||||||
|
|
||||||
|
assert_eq!(1u8, read.read_int(1).unwrap());
|
||||||
|
assert_eq!(3253u16, read.read().unwrap());
|
||||||
|
assert_eq!(13253u64, read.read().unwrap());
|
||||||
|
|
||||||
|
// 0 padded
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_float_le() {
|
||||||
|
let mut stream = BitWriteStream::new(LittleEndian);
|
||||||
|
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_float(3253.12f32).unwrap();
|
||||||
|
|
||||||
|
let data = stream.finish();
|
||||||
|
let mut read = BitReadStream::from(BitReadBuffer::new(data, LittleEndian));
|
||||||
|
|
||||||
|
assert_eq!(true, read.read_bool().unwrap());
|
||||||
|
assert_eq!(3253.12f32, read.read().unwrap());
|
||||||
|
|
||||||
|
// 0 padded
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_float_be() {
|
||||||
|
let mut stream = BitWriteStream::new(BigEndian);
|
||||||
|
|
||||||
|
stream.write_bool(true).unwrap();
|
||||||
|
stream.write_float(3253.12f32).unwrap();
|
||||||
|
|
||||||
|
let data = stream.finish();
|
||||||
|
let mut read = BitReadStream::from(BitReadBuffer::new(data, BigEndian));
|
||||||
|
|
||||||
|
assert_eq!(1u8, read.read_int(1).unwrap());
|
||||||
|
assert_eq!(3253.12f32, read.read().unwrap());
|
||||||
|
|
||||||
|
// 0 padded
|
||||||
|
assert_eq!(false, read.read_bool().unwrap());
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue