mirror of
https://codeberg.org/icewind/bitbuffer.git
synced 2026-06-03 16:44:06 +02:00
bunch of write stuff
This commit is contained in:
parent
4d2ea4ee7c
commit
23ed7b0e4a
12 changed files with 840 additions and 116 deletions
|
|
@ -18,11 +18,11 @@ pub trait Endianness: private::Sealed {
|
|||
}
|
||||
|
||||
/// Marks the buffer or stream as big endian
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BigEndian;
|
||||
|
||||
/// Marks the buffer or stream as little endian
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct LittleEndian;
|
||||
|
||||
macro_rules! impl_endianness {
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ pub use readbuffer::BitReadBuffer;
|
|||
pub use readstream::BitReadStream;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
pub use write::{BitWrite, BitWriteSized};
|
||||
pub use writestream::BitWriteStream;
|
||||
|
||||
mod endianness;
|
||||
|
|
@ -70,6 +71,7 @@ mod num_traits;
|
|||
mod read;
|
||||
mod readbuffer;
|
||||
mod readstream;
|
||||
mod write;
|
||||
mod writestream;
|
||||
|
||||
/// Errors that can be returned when trying to read from a buffer
|
||||
|
|
|
|||
|
|
@ -202,63 +202,13 @@ pub trait IntoBytes: Sized {
|
|||
}
|
||||
|
||||
macro_rules! impl_into_bytes {
|
||||
($type:ty, $iter:ident) => {
|
||||
// once std::array:IntoIter is stabilized we can get rid of this iterator
|
||||
// https://github.com/rust-lang/rust/issues/65798
|
||||
pub struct $iter {
|
||||
data: [u8; std::mem::size_of::<$type>()],
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl $iter {
|
||||
pub fn new(int: $type) -> Self {
|
||||
$iter {
|
||||
data: int.to_le_bytes(),
|
||||
start: 0,
|
||||
end: std::mem::size_of::<$type>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for $iter {
|
||||
type Item = u8;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.start < self.end {
|
||||
let byte = self.data[self.start];
|
||||
self.start += 1;
|
||||
Some(byte)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let size = self.end - self.start;
|
||||
(size, Some(size))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::DoubleEndedIterator for $iter {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.end > self.start {
|
||||
self.end -= 1;
|
||||
Some(self.data[self.end])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::ExactSizeIterator for $iter {}
|
||||
|
||||
($type:ty, $bytes:expr) => {
|
||||
impl IntoBytes for $type {
|
||||
type Iter = $iter;
|
||||
type Iter = std::array::IntoIter<u8, $bytes>;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_bytes(self) -> Self::Iter {
|
||||
<$iter>::new(self)
|
||||
Self::Iter::new(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -277,15 +227,24 @@ impl_is_signed!(i64, true);
|
|||
impl_is_signed!(i128, true);
|
||||
impl_is_signed!(isize, true);
|
||||
|
||||
impl_into_bytes!(u8, BytesIterU8);
|
||||
impl_into_bytes!(u16, BytesIterU16);
|
||||
impl_into_bytes!(u32, BytesIterU32);
|
||||
impl_into_bytes!(u64, BytesIterU64);
|
||||
impl_into_bytes!(u128, BytesIterU128);
|
||||
impl_into_bytes!(usize, BytesIterUsize);
|
||||
impl_into_bytes!(i8, BytesIterI8);
|
||||
impl_into_bytes!(i16, BytesIterI16);
|
||||
impl_into_bytes!(i32, BytesIterI32);
|
||||
impl_into_bytes!(i64, BytesIterI64);
|
||||
impl_into_bytes!(i128, BytesIterI128);
|
||||
impl_into_bytes!(isize, BytesIterIsize);
|
||||
impl_into_bytes!(u8, 1);
|
||||
impl_into_bytes!(u16, 2);
|
||||
impl_into_bytes!(u32, 4);
|
||||
impl_into_bytes!(u64, 8);
|
||||
impl_into_bytes!(u128, 16);
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl_into_bytes!(usize, 8);
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
impl_into_bytes!(usize, 4);
|
||||
|
||||
impl_into_bytes!(i8, 1);
|
||||
impl_into_bytes!(i16, 2);
|
||||
impl_into_bytes!(i32, 4);
|
||||
impl_into_bytes!(i64, 8);
|
||||
impl_into_bytes!(i128, 16);
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
impl_into_bytes!(isize, 8);
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
impl_into_bytes!(isize, 4);
|
||||
|
|
|
|||
122
src/write.rs
Normal file
122
src/write.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
use crate::{BitReadStream, BitWriteStream, Endianness, Result};
|
||||
use std::mem::size_of;
|
||||
|
||||
/// Trait for types that can be written to a stream without requiring the size to be configured
|
||||
pub trait BitWrite<E: Endianness> {
|
||||
/// Write the type to stream
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()>;
|
||||
}
|
||||
|
||||
macro_rules! impl_write_int {
|
||||
($type:ty) => {
|
||||
impl<E: Endianness> BitWrite<E> for $type {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_int::<$type>(*self, size_of::<$type>() * 8)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_write_int!(u8);
|
||||
impl_write_int!(u16);
|
||||
impl_write_int!(u32);
|
||||
impl_write_int!(u64);
|
||||
impl_write_int!(u128);
|
||||
impl_write_int!(i8);
|
||||
impl_write_int!(i16);
|
||||
impl_write_int!(i32);
|
||||
impl_write_int!(i64);
|
||||
impl_write_int!(i128);
|
||||
|
||||
impl<E: Endianness> BitWrite<E> for f32 {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_float::<f32>(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWrite<E> for f64 {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_float::<f64>(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWrite<E> for bool {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_bool(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWrite<E> for str {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_string(self, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWrite<E> for String {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_string(self, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWrite<E> for BitReadStream<'_, E> {
|
||||
#[inline]
|
||||
fn write(&self, stream: &mut BitWriteStream<E>) -> Result<()> {
|
||||
stream.write_bits(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for types that can be written to a stream, requiring the size to be configured
|
||||
pub trait BitWriteSized<E: Endianness> {
|
||||
/// Write the type to stream
|
||||
fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWriteSized<E> for str {
|
||||
#[inline]
|
||||
fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
|
||||
stream.write_string(self, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Endianness> BitWriteSized<E> for String {
|
||||
#[inline]
|
||||
fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
|
||||
stream.write_string(self, Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_write_sized_int {
|
||||
($type:ty) => {
|
||||
impl<E: Endianness> BitWriteSized<E> for $type {
|
||||
#[inline]
|
||||
fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
|
||||
stream.write_int::<$type>(*self, len)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_write_sized_int!(u8);
|
||||
impl_write_sized_int!(u16);
|
||||
impl_write_sized_int!(u32);
|
||||
impl_write_sized_int!(u64);
|
||||
impl_write_sized_int!(u128);
|
||||
impl_write_sized_int!(i8);
|
||||
impl_write_sized_int!(i16);
|
||||
impl_write_sized_int!(i32);
|
||||
impl_write_sized_int!(i64);
|
||||
impl_write_sized_int!(i128);
|
||||
|
||||
impl<E: Endianness> BitWriteSized<E> for BitReadStream<'_, E> {
|
||||
#[inline]
|
||||
fn write_sized(&self, stream: &mut BitWriteStream<E>, len: usize) -> Result<()> {
|
||||
let bits = self.clone().read_bits(len)?;
|
||||
stream.write_bits(&bits)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,9 @@ use std::ops::{BitOrAssign, BitXor};
|
|||
|
||||
use crate::endianness::Endianness;
|
||||
use crate::num_traits::{IntoBytes, IsSigned, UncheckedPrimitiveFloat, UncheckedPrimitiveInt};
|
||||
use crate::{BitError, Result};
|
||||
use crate::{BitError, BitReadStream, BitWrite, BitWriteSized, Result};
|
||||
use std::cmp::min;
|
||||
use std::fmt::Debug;
|
||||
|
||||
const USIZE_SIZE: usize = size_of::<usize>();
|
||||
const USIZE_BITS: usize = USIZE_SIZE * 8;
|
||||
|
|
@ -29,6 +31,7 @@ const USIZE_BITS: usize = USIZE_SIZE * 8;
|
|||
/// ```
|
||||
///
|
||||
/// [`BitBuffer`]: struct.BitBuffer.html
|
||||
#[derive(Clone)]
|
||||
pub struct BitWriteStream<E>
|
||||
where
|
||||
E: Endianness,
|
||||
|
|
@ -79,14 +82,17 @@ where
|
|||
I: ExactSizeIterator,
|
||||
I: DoubleEndedIterator<Item = u8>,
|
||||
{
|
||||
let full_bytes = min(bits.len() - 1, count / 8);
|
||||
|
||||
let counts = repeat(8)
|
||||
.take(bits.len() - 1)
|
||||
.chain(once(count - (bits.len() - 1) * 8));
|
||||
.take(full_bytes)
|
||||
.chain(once(count - full_bytes * 8));
|
||||
if E::is_le() {
|
||||
bits.zip(counts)
|
||||
.for_each(|(chunk, count)| self.push_bits(chunk as usize, count))
|
||||
} else {
|
||||
bits.rev()
|
||||
bits.take(count / 8 + 1)
|
||||
.rev()
|
||||
.zip(counts)
|
||||
.for_each(|(chunk, count)| self.push_bits(chunk as usize, count))
|
||||
}
|
||||
|
|
@ -97,7 +103,11 @@ where
|
|||
debug_assert!(count < USIZE_BITS - 8);
|
||||
|
||||
let bit_offset = self.bit_len & 7;
|
||||
let last_written_byte = self.bytes.pop().unwrap_or(0);
|
||||
let last_written_byte = if bit_offset > 0 {
|
||||
self.bytes.pop().unwrap_or(0)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let merged_byte_count = (count + bit_offset + 7) / 8;
|
||||
|
||||
if E::is_le() {
|
||||
|
|
@ -154,7 +164,7 @@ where
|
|||
#[inline]
|
||||
pub fn write_int<T>(&mut self, value: T, count: usize) -> Result<()>
|
||||
where
|
||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + IntoBytes,
|
||||
T: PrimInt + BitOrAssign + IsSigned + UncheckedPrimitiveInt + BitXor + IntoBytes + Debug,
|
||||
{
|
||||
let type_bit_size = size_of::<T>() * 8;
|
||||
|
||||
|
|
@ -166,11 +176,13 @@ where
|
|||
}
|
||||
|
||||
if type_bit_size < USIZE_BITS {
|
||||
if T::is_signed() {
|
||||
todo!()
|
||||
} else {
|
||||
self.push_bits(value.into_usize_unchecked(), count);
|
||||
}
|
||||
// if T::is_signed() && count < type_bit_size {
|
||||
// // set
|
||||
// let sign_bit = T::one() << (count - 1);
|
||||
// let value = abs(value) | sign_bit;
|
||||
// }
|
||||
|
||||
self.push_bits(value.into_usize_unchecked(), count);
|
||||
} else {
|
||||
self.push_non_fit_bits(value.into_bytes(), count)
|
||||
}
|
||||
|
|
@ -230,7 +242,33 @@ where
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
|
||||
self.push_non_fit_bits(bytes.iter().copied(), bytes.len() * 8);
|
||||
bytes
|
||||
.iter()
|
||||
.copied()
|
||||
.for_each(|chunk| self.push_bits(chunk as usize, 8));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write bits from a read stream into the buffer
|
||||
#[inline]
|
||||
pub fn write_bits(&mut self, bits: &BitReadStream<E>) -> Result<()> {
|
||||
let mut bits = bits.clone();
|
||||
let bit_offset = self.bit_len % 8;
|
||||
if bit_offset > 0 {
|
||||
let start = bits.read_int::<u8>(8 - bit_offset)?;
|
||||
self.push_bits(start as usize, 8 - bit_offset);
|
||||
}
|
||||
|
||||
while bits.bits_left() > 32 {
|
||||
let chunk = bits.read::<u32>()?;
|
||||
self.push_bits(chunk as usize, 32);
|
||||
}
|
||||
|
||||
if bits.bits_left() > 0 {
|
||||
let end_bits = bits.bits_left();
|
||||
let end = bits.read_int::<u32>(end_bits)?;
|
||||
self.push_bits(end as usize, end_bits);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -284,4 +322,16 @@ where
|
|||
pub fn finish(self) -> Vec<u8> {
|
||||
self.bytes
|
||||
}
|
||||
|
||||
/// Write the type to stream
|
||||
#[inline]
|
||||
pub fn write<T: BitWrite<E>>(&mut self, value: &T) -> Result<()> {
|
||||
value.write(self)
|
||||
}
|
||||
|
||||
/// Write the type to stream
|
||||
#[inline]
|
||||
pub fn write_sized<T: BitWriteSized<E>>(&mut self, value: &T, length: usize) -> Result<()> {
|
||||
value.write_sized(self, length)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue