diff --git a/benches/deku.rs b/benches/deku.rs index f52791a8..963407db 100644 --- a/benches/deku.rs +++ b/benches/deku.rs @@ -1,4 +1,7 @@ +use std::io::Read; + use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use deku::container::Container; use deku::prelude::*; #[derive(Debug, PartialEq, DekuRead, DekuWrite)] @@ -10,8 +13,13 @@ struct DekuBits { } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] -struct DekuByte { - data: u8, +struct DekuBytes { + // #[deku(bytes = "1")] <=== This should be emitted! + data_00: u8, + // #[deku(bytes = "2")] <=== This should be emitted! + data_01: u16, + // #[deku(bytes = "4")] <=== This should be emitted! + data_02: u32, } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] @@ -38,24 +46,27 @@ struct DekuVec { data: Vec, } -fn deku_read_bits(input: &[u8]) { - let (_rest, _v) = DekuBits::from_bytes((input, 0)).unwrap(); +fn deku_read_bits(reader: impl Read) { + let mut container = Container::new(reader); + let _v = DekuBits::from_reader(&mut container, ()).unwrap(); } fn deku_write_bits(input: &DekuBits) { let _v = input.to_bytes().unwrap(); } -fn deku_read_byte(input: &[u8]) { - let (_rest, _v) = DekuByte::from_bytes((input, 0)).unwrap(); +fn deku_read_byte(reader: impl Read) { + let mut container = Container::new(reader); + let _v = DekuBytes::from_reader(&mut container, ()).unwrap(); } -fn deku_write_byte(input: &DekuByte) { +fn deku_write_byte(input: &DekuBytes) { let _v = input.to_bytes().unwrap(); } fn deku_read_enum(input: &[u8]) { - let (_rest, _v) = DekuEnum::from_bytes((input, 0)).unwrap(); + let mut container = Container::new(input); + let _v = DekuEnum::from_reader(&mut container, ()).unwrap(); } fn deku_write_enum(input: &DekuEnum) { @@ -63,7 +74,8 @@ fn deku_write_enum(input: &DekuEnum) { } fn deku_read_vec(input: &[u8]) { - let (_rest, _v) = DekuVec::from_bytes((input, 0)).unwrap(); + let mut container = Container::new(input); + let _v = DekuVec::from_reader(&mut container, ()).unwrap(); } fn deku_write_vec(input: &DekuVec) { @@ -71,7 +83,8 @@ fn deku_write_vec(input: &DekuVec) { } fn deku_read_vec_perf(input: &[u8]) { - let (_rest, _v) = DekuVecPerf::from_bytes((input, 0)).unwrap(); + let mut container = Container::new(std::io::Cursor::new(input)); + let _v = DekuVecPerf::from_reader(&mut container, ()).unwrap(); } fn deku_write_vec_perf(input: &DekuVecPerf) { @@ -79,14 +92,21 @@ fn deku_write_vec_perf(input: &DekuVecPerf) { } fn criterion_benchmark(c: &mut Criterion) { + let mut reader = std::io::repeat(0b101); c.bench_function("deku_read_byte", |b| { - b.iter(|| deku_read_byte(black_box([0x01].as_ref()))) + b.iter(|| deku_read_byte(black_box(&mut reader))) }); c.bench_function("deku_write_byte", |b| { - b.iter(|| deku_write_byte(black_box(&DekuByte { data: 0x01 }))) + b.iter(|| { + deku_write_byte(black_box(&DekuBytes { + data_00: 0x00, + data_01: 0x02, + data_02: 0x03, + })) + }) }); c.bench_function("deku_read_bits", |b| { - b.iter(|| deku_read_bits(black_box([0xf1].as_ref()))) + b.iter(|| deku_read_bits(black_box(&mut reader))) }); c.bench_function("deku_write_bits", |b| { b.iter(|| { diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs index 067634cb..87cdcc5a 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -57,51 +57,51 @@ fn emit_struct(input: &DekuData) -> Result { let initialize_struct = super::gen_struct_init(is_named_struct, internal_fields); - //// Implement `DekuContainerRead` for types that don't need a context - //if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) { - // let from_bytes_body = wrap_default_ctx( - // quote! { - // use core::convert::TryFrom; - // use ::#crate_::bitvec::BitView; - // let __deku_input_bits = __deku_input.0.view_bits::<::#crate_::bitvec::Msb0>(); - // let mut __deku_rest = &__deku_input_bits[__deku_input.1..]; - // let mut __deku_total_read = 0; - - // #magic_read - - // #(#field_reads)* - // let __deku_value = #initialize_struct; - - // Ok((__deku_total_read, __deku_value)) - // }, - // &input.ctx, - // &input.ctx_default, - // ); - - // tokens.extend(emit_try_from(&imp, &lifetime, &ident, wher)); - - // tokens.extend(emit_from_bytes( - // &imp, - // &lifetime, - // &ident, - // wher, - // from_bytes_body, - // )); - //} + // Implement `DekuContainerRead` for types that don't need a context + if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) { + // let from_bytes_body = wrap_default_ctx( + // quote! { + // use core::convert::TryFrom; + // use ::#crate_::bitvec::BitView; + // let __deku_input_bits = __deku_input.0.view_bits::<::#crate_::bitvec::Msb0>(); + // let mut __deku_rest = &__deku_input_bits[__deku_input.1..]; + // let mut __deku_total_read = 0; + + // #magic_read + + // #(#field_reads)* + // let __deku_value = #initialize_struct; + + // Ok((__deku_total_read, __deku_value)) + // }, + // &input.ctx, + // &input.ctx_default, + // ); + + //tokens.extend(emit_try_from(&imp, &lifetime, &ident, wher)); + + // tokens.extend(emit_from_bytes( + // &imp, + // &lifetime, + // &ident, + // wher, + // from_bytes_body, + // )); + } let (ctx_types, ctx_arg) = gen_ctx_types_and_arg(input.ctx.as_ref())?; let read_body = quote! { use core::convert::TryFrom; //let mut __deku_rest = __deku_input_bits; - let mut __deku_total_read = 0; + //let mut __deku_total_read = 0; #magic_read #(#field_reads)* let __deku_value = #initialize_struct; - Ok((__deku_total_read, __deku_value)) + Ok(__deku_value) }; tokens.extend(quote! { @@ -223,18 +223,8 @@ fn emit_enum(input: &DekuData) -> Result { deku_ids.push(deku_id); } - // if we're consuming an id, set the rest to new_rest before reading the variant - let new_rest = if consume_id { - quote! { - //__deku_total_read += __deku_amt_read; - } - } else { - quote! {} - }; - quote! { { - #new_rest #(#field_reads)* Self :: #initialize_enum } @@ -290,7 +280,7 @@ fn emit_enum(input: &DekuData) -> Result { } } else if id_type.is_some() { quote! { - let (__deku_amt_read, __deku_variant_id) = <#id_type>::from_reader(container, (#id_args))?; + let __deku_variant_id = <#id_type>::from_reader(container, (#id_args))?; } } else { // either `id` or `type` needs to be specified @@ -309,46 +299,46 @@ fn emit_enum(input: &DekuData) -> Result { // Implement `DekuContainerRead` for types that don't need a context if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) { - let from_bytes_body = wrap_default_ctx( - quote! { - use core::convert::TryFrom; - use ::#crate_::bitvec::BitView; - let __deku_input_bits = __deku_input.0.view_bits::<::#crate_::bitvec::Msb0>(); - let mut __deku_rest = &__deku_input_bits[__deku_input.1..]; - let mut __deku_total_read = 0; - - #magic_read - - #variant_read - - Ok((__deku_total_read, __deku_value)) - }, - &input.ctx, - &input.ctx_default, - ); - - tokens.extend(emit_try_from(&imp, &lifetime, &ident, wher)); - - tokens.extend(emit_from_bytes( - &imp, - &lifetime, - &ident, - wher, - from_bytes_body, - )); + //let from_bytes_body = wrap_default_ctx( + // quote! { + // use core::convert::TryFrom; + // use ::#crate_::bitvec::BitView; + // let __deku_input_bits = __deku_input.0.view_bits::<::#crate_::bitvec::Msb0>(); + // let mut __deku_rest = &__deku_input_bits[__deku_input.1..]; + // let mut __deku_total_read = 0; + + // #magic_read + + // #variant_read + + // Ok((__deku_total_read, __deku_value)) + // }, + // &input.ctx, + // &input.ctx_default, + //); + + //tokens.extend(emit_try_from(&imp, &lifetime, &ident, wher)); + + //tokens.extend(emit_from_bytes( + // &imp, + // &lifetime, + // &ident, + // wher, + // from_bytes_body, + //)); } let (ctx_types, ctx_arg) = gen_ctx_types_and_arg(input.ctx.as_ref())?; let read_body = quote! { use core::convert::TryFrom; - let mut __deku_rest = __deku_input_bits; - let mut __deku_total_read = 0; + //let mut __deku_rest = __deku_input_bits; + //let mut __deku_total_read = 0; #magic_read #variant_read - Ok((__deku_total_read, __deku_value)) + Ok(__deku_value) }; tokens.extend(quote! { @@ -608,29 +598,53 @@ fn emit_field_read( quote! { { use core::borrow::Borrow; - #type_as_deku_read::from_reader(&mut container, (::#crate_::ctx::Limit::new_count(usize::try_from(*((#field_count).borrow()))?), (#read_args))) + #type_as_deku_read::from_reader + ( + container, + (::#crate_::ctx::Limit::new_count(usize::try_from(*((#field_count).borrow()))?), (#read_args)) + ) } } } else if let Some(field_bits) = &f.bits_read { quote! { { use core::borrow::Borrow; - #type_as_deku_read::from_reader(container, (::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args))) + #type_as_deku_read::from_reader + ( + container, + (::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args)) + ) } } } else if let Some(field_bytes) = &f.bytes_read { quote! { { use core::borrow::Borrow; - #type_as_deku_read::from_reader(container, (::#crate_::ctx::Limit::new_byte_size(::#crate_::ctx::ByteSize(usize::try_from(*((#field_bytes).borrow()))?)), (#read_args))) + #type_as_deku_read::from_reader + ( + container, + (::#crate_::ctx::Limit::new_byte_size(::#crate_::ctx::ByteSize(usize::try_from(*((#field_bytes).borrow()))?)), (#read_args)) + ) } } } else if let Some(field_until) = &f.until { // We wrap the input into another closure here to enforce that it is actually a callable // Otherwise, an incorrectly passed-in integer could unexpectedly convert into a `Count` limit - quote! {#type_as_deku_read::from_reader(container, (::#crate_::ctx::Limit::new_until(#field_until), (#read_args)))} + quote! { + #type_as_deku_read::from_reader + ( + container, + (::#crate_::ctx::Limit::new_until(#field_until), (#read_args)) + ) + } } else { - quote! {#type_as_deku_read::from_reader(container, (#read_args))} + quote! { + #type_as_deku_read::from_reader + ( + container, + (#read_args) + ) + } } }; @@ -647,7 +661,7 @@ fn emit_field_read( let field_read_normal = quote! { let __deku_value = #field_read_func?; - let __deku_value: #field_type = #field_map(__deku_value)?; + //let __deku_value: #field_type = #field_map(__deku_value)?; __deku_value }; diff --git a/examples/enums.rs b/examples/enums.rs index 522b9801..a817185d 100644 --- a/examples/enums.rs +++ b/examples/enums.rs @@ -29,7 +29,8 @@ enum DekuTest { fn main() { let test_data = hex!("03020102").to_vec(); - let deku_test = DekuTest::try_from(test_data.as_ref()).unwrap(); + let mut container = deku::container::Container::new(std::io::Cursor::new(test_data.clone())); + let deku_test = DekuTest::from_reader(&mut container, ()).unwrap(); assert_eq!( DekuTest::Var4 { diff --git a/examples/example.rs b/examples/example.rs index f39afd1e..8b7b0e13 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -53,17 +53,9 @@ fn main() { ] .as_ref(); - let mut container = Container::new(std::io::Cursor::new(test_data.clone())); - let a = u8::from_reader(&mut container, (Endian::Little, ByteSize(1))); - let b = u8::from_reader(&mut container, (Endian::Little, BitSize(7))); - let c = u8::from_reader(&mut container, (Endian::Little, BitSize(1))); - let d = u16::from_reader(&mut container, (Endian::Big, BitSize(16))); - println!("{a:02x?}"); - println!("{b:02x?}"); - println!("{c:02x?}"); - println!("{d:02x?}"); + let mut container = Container::new(std::io::Cursor::new(test_data)); + let test_deku = DekuTest::from_reader(&mut container, ()).unwrap(); - let test_deku = DekuTest::try_from(test_data).unwrap(); println!("{test_deku:02x?}"); assert_eq!( DekuTest { diff --git a/examples/ipv4.rs b/examples/ipv4.rs index ff3cf589..cfe371d2 100644 --- a/examples/ipv4.rs +++ b/examples/ipv4.rs @@ -51,7 +51,8 @@ pub struct Ipv4Header { fn main() { let test_data = hex!("4500004b0f490000801163a591fea0ed91fd02cb").to_vec(); - let ip_header = Ipv4Header::try_from(test_data.as_ref()).unwrap(); + let mut container = deku::container::Container::new(std::io::Cursor::new(test_data.clone())); + let ip_header = Ipv4Header::from_reader(&mut container, ()).unwrap(); assert_eq!( Ipv4Header { diff --git a/src/container.rs b/src/container.rs index eee2c918..03f931e0 100644 --- a/src/container.rs +++ b/src/container.rs @@ -1,16 +1,25 @@ use bitvec::prelude::*; use std::io::Read; +pub enum ContainerRet { + Bytes, + Bits(BitVec), +} + pub struct Container { inner: R, + // TODO; bitslice.len() == 8 leftover: BitVec, + pub(crate) bits_read: usize, } impl Container { + #[inline] pub fn new(inner: R) -> Self { Self { inner, leftover: BitVec::new(), // with_capacity 8? + bits_read: 0, } } @@ -39,8 +48,22 @@ impl Container { self.leftover = add; } + self.bits_read += ret.len(); Ok(ret) } + + // Attempt to read into bytes instead of bits + // + // 1. We must have no leftover bits, so that we are "aligned" + #[inline] + pub fn read_bytes(&mut self, amt: usize, buf: &mut [u8]) -> ContainerRet { + if self.leftover.is_empty() { + self.inner.read_exact(buf); + ContainerRet::Bytes + } else { + ContainerRet::Bits(self.read_bits(amt * 8).unwrap()) + } + } } #[cfg(test)] @@ -55,12 +78,8 @@ mod tests { let mut container = Container::new(buf); let bits = container.read_bits(4).unwrap(); - println!("{}", bits); let bits = container.read_bits(4).unwrap(); - println!("{}", bits); let bits = container.read_bits(4).unwrap(); - println!("{}", bits); let bits = container.read_bits(4).unwrap(); - println!("{}", bits); } } diff --git a/src/impls/bool.rs b/src/impls/bool.rs index 033c9a35..c8ebc736 100644 --- a/src/impls/bool.rs +++ b/src/impls/bool.rs @@ -5,7 +5,7 @@ use alloc::format; use bitvec::prelude::*; -use crate::{DekuError, DekuRead, DekuWrite, container}; +use crate::{container, DekuError, DekuRead, DekuWrite}; impl<'a, Ctx> DekuRead<'a, Ctx> for bool where diff --git a/src/impls/cow.rs b/src/impls/cow.rs index d1da2704..fe9edf86 100644 --- a/src/impls/cow.rs +++ b/src/impls/cow.rs @@ -1,4 +1,7 @@ -use std::{borrow::{Borrow, Cow}, io::Read}; +use std::{ + borrow::{Borrow, Cow}, + io::Read, +}; use bitvec::prelude::*; diff --git a/src/impls/ipaddr.rs b/src/impls/ipaddr.rs index e6162545..7a617487 100644 --- a/src/impls/ipaddr.rs +++ b/src/impls/ipaddr.rs @@ -1,4 +1,7 @@ -use std::{net::{IpAddr, Ipv4Addr, Ipv6Addr}, io::Read}; +use std::{ + io::Read, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, +}; use bitvec::prelude::*; diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index 48cb21a6..e6d55efb 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -5,12 +5,14 @@ use core::convert::TryInto; use bitvec::prelude::*; use std::io::Read; +use crate::container::ContainerRet; use crate::ctx::*; use crate::prelude::NeedSize; use crate::{DekuError, DekuRead, DekuWrite}; // specialize u8 for ByteSize impl DekuRead<'_, (Endian, ByteSize)> for u8 { + #[inline] fn read( input: &BitSlice, (_, _): (Endian, ByteSize), @@ -25,19 +27,31 @@ impl DekuRead<'_, (Endian, ByteSize)> for u8 { Ok((MAX_TYPE_BITS, value)) } + // specialize not needed? + #[inline] fn from_reader( container: &mut crate::container::Container, (endian, size): (Endian, ByteSize), ) -> Result { - let mut bits = container.read_bits(8).unwrap(); - let a = ::read(&mut bits, (endian, size))?; - Ok(a.1) + let mut buf = [0; core::mem::size_of::()]; + let ret = container.read_bytes(size.0, &mut buf); + let a = match ret { + ContainerRet::Bits(mut bits) => { + let a = ::read(&bits, (endian, size))?; + a.1 + } + ContainerRet::Bytes => { + ::from_be_bytes(buf) + } + }; + Ok(a) } } macro_rules! ImplDekuReadBits { ($typ:ty, $inner:ty) => { impl DekuRead<'_, (Endian, BitSize)> for $typ { + #[inline] fn read( input: &BitSlice, (endian, size): (Endian, BitSize), @@ -122,12 +136,13 @@ macro_rules! ImplDekuReadBits { Ok((bit_size, value)) } + #[inline] fn from_reader( container: &mut crate::container::Container, (endian, size): (Endian, BitSize), ) -> Result<$typ, DekuError> { let mut bits = container.read_bits(size.0).unwrap(); - let a = <$typ>::read(&mut bits, (endian, size))?; + let a = <$typ>::read(&bits, (endian, size))?; Ok(a.1) } } @@ -137,6 +152,7 @@ macro_rules! ImplDekuReadBits { macro_rules! ImplDekuReadBytes { ($typ:ty, $inner:ty) => { impl DekuRead<'_, (Endian, ByteSize)> for $typ { + #[inline] fn read( input: &BitSlice, (endian, size): (Endian, ByteSize), @@ -202,13 +218,23 @@ macro_rules! ImplDekuReadBytes { Ok((bit_size, value)) } + #[inline] fn from_reader( container: &mut crate::container::Container, (endian, size): (Endian, ByteSize), ) -> Result<$typ, DekuError> { - let mut bits = container.read_bits(size.0 * 8).unwrap(); - let a = <$typ>::read(&mut bits, (endian, size))?; - Ok(a.1) + let mut buf = [0; core::mem::size_of::<$typ>()]; + let ret = container.read_bytes(size.0, &mut buf); + let a = match ret { + ContainerRet::Bits(mut bits) => { + let a = <$typ>::read(&bits, (endian, size))?; + a.1 + } + ContainerRet::Bytes => { + <$typ>::from_be_bytes(buf.try_into().unwrap()) + } + }; + Ok(a) } } }; @@ -217,6 +243,7 @@ macro_rules! ImplDekuReadBytes { macro_rules! ImplDekuReadSignExtend { ($typ:ty, $inner:ty) => { impl DekuRead<'_, (Endian, ByteSize)> for $typ { + #[inline] fn read( input: &BitSlice, (endian, size): (Endian, ByteSize), @@ -231,17 +258,20 @@ macro_rules! ImplDekuReadSignExtend { Ok((amt_read, value)) } + #[inline] fn from_reader( container: &mut crate::container::Container, (endian, size): (Endian, ByteSize), ) -> Result<$typ, DekuError> { + // TODO: specialize for reading byte aligned let mut bits = container.read_bits(size.0 * 8).unwrap(); - let a = <$typ>::read(&mut bits, (endian, size))?; + let a = <$typ>::read(&bits, (endian, size))?; Ok(a.1) } } impl DekuRead<'_, (Endian, BitSize)> for $typ { + #[inline] fn read( input: &BitSlice, (endian, size): (Endian, BitSize), @@ -255,12 +285,14 @@ macro_rules! ImplDekuReadSignExtend { let value = (value as $typ) << shift >> shift; Ok((amt_read, value)) } + + #[inline] fn from_reader( container: &mut crate::container::Container, (endian, size): (Endian, BitSize), ) -> Result<$typ, DekuError> { - let mut bits = container.read_bits(size.0).unwrap(); - let a = <$typ>::read(&mut bits, (endian, size))?; + let mut bits = container.read_bits(size.0 * 8).unwrap(); + let a = <$typ>::read(&bits, (endian, size))?; Ok(a.1) } } @@ -271,6 +303,7 @@ macro_rules! ForwardDekuRead { ($typ:ty) => { // Only have `endian`, set `bit_size` to `Size::of::()` impl DekuRead<'_, Endian> for $typ { + #[inline] fn read( input: &BitSlice, endian: Endian, @@ -285,20 +318,26 @@ macro_rules! ForwardDekuRead { } } + #[inline] fn from_reader( container: &mut crate::container::Container, endian: Endian, ) -> Result<$typ, DekuError> { let bit_size = BitSize::of::<$typ>(); - let mut bits = container.read_bits(bit_size.0).unwrap(); - let a = <$typ>::read(&mut bits, endian)?; - Ok(a.1) + // Since we don't have a #[bits] or [bytes], check if we can use bytes for perf + let a = if (bit_size.0 % 8) == 0 { + <$typ>::from_reader(container, (endian, ByteSize(bit_size.0 / 8)))? + } else { + <$typ>::from_reader(container, (endian, bit_size))? + }; + Ok(a) } } // Only have `bit_size`, set `endian` to `Endian::default`. impl DekuRead<'_, ByteSize> for $typ { + #[inline] fn read( input: &BitSlice, byte_size: ByteSize, @@ -308,20 +347,21 @@ macro_rules! ForwardDekuRead { <$typ>::read(input, (endian, byte_size)) } + #[inline] fn from_reader( container: &mut crate::container::Container, byte_size: ByteSize, ) -> Result<$typ, DekuError> { let endian = Endian::default(); - let mut bits = container.read_bits(byte_size.0 * 8).unwrap(); - let a = <$typ>::read(&mut bits, (endian, byte_size))?; - Ok(a.1) + let a = <$typ>::from_reader(container, (endian, byte_size))?; + Ok(a) } } // Only have `bit_size`, set `endian` to `Endian::default`. impl DekuRead<'_, BitSize> for $typ { + #[inline] fn read( input: &BitSlice, bit_size: BitSize, @@ -336,23 +376,25 @@ macro_rules! ForwardDekuRead { } } + #[inline] fn from_reader( container: &mut crate::container::Container, bit_size: BitSize, ) -> Result<$typ, DekuError> { let endian = Endian::default(); - let mut bits = container.read_bits(bit_size.0).unwrap(); - let a = <$typ>::read(&mut bits, bit_size)?; - Ok(a.1) + let a = <$typ>::from_reader(container, (endian, bit_size))?; + Ok(a) } } impl DekuRead<'_> for $typ { + #[inline] fn read(input: &BitSlice, _: ()) -> Result<(usize, Self), DekuError> { <$typ>::read(input, Endian::default()) } + #[inline] fn from_reader( container: &mut crate::container::Container, _: (), @@ -366,6 +408,7 @@ macro_rules! ForwardDekuRead { macro_rules! ImplDekuWrite { ($typ:ty) => { impl DekuWrite<(Endian, BitSize)> for $typ { + #[inline] fn write( &self, output: &mut BitVec, @@ -411,6 +454,7 @@ macro_rules! ImplDekuWrite { } impl DekuWrite<(Endian, ByteSize)> for $typ { + #[inline] fn write( &self, output: &mut BitVec, @@ -457,6 +501,7 @@ macro_rules! ImplDekuWrite { // Only have `endian`, return all input impl DekuWrite for $typ { + #[inline] fn write( &self, output: &mut BitVec, @@ -477,6 +522,7 @@ macro_rules! ForwardDekuWrite { ($typ:ty) => { // Only have `bit_size`, set `endian` to `Endian::default`. impl DekuWrite for $typ { + #[inline] fn write( &self, output: &mut BitVec, @@ -488,6 +534,7 @@ macro_rules! ForwardDekuWrite { // Only have `bit_size`, set `endian` to `Endian::default`. impl DekuWrite for $typ { + #[inline] fn write( &self, output: &mut BitVec, @@ -498,6 +545,7 @@ macro_rules! ForwardDekuWrite { } impl DekuWrite for $typ { + #[inline] fn write(&self, output: &mut BitVec, _: ()) -> Result<(), DekuError> { <$typ>::write(self, output, Endian::default()) } diff --git a/src/impls/vec.rs b/src/impls/vec.rs index 03c2bf70..c1914764 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -48,6 +48,42 @@ where Ok((total_read, res)) } +/// Read `T`s into a vec until a given predicate returns true +/// * `capacity` - an optional capacity to pre-allocate the vector with +/// * `ctx` - The context required by `T`. It will be passed to every `T` when constructing. +/// * `predicate` - the predicate that decides when to stop reading `T`s +/// The predicate takes two parameters: the number of bits that have been read so far, +/// and a borrow of the latest value to have been read. It should return `true` if reading +/// should now stop, and `false` otherwise +fn reader_vec_with_predicate<'a, T, Ctx, Predicate, R: std::io::Read>( + container: &mut crate::container::Container, + capacity: Option, + ctx: Ctx, + mut predicate: Predicate, +) -> Result, DekuError> +where + T: DekuRead<'a, Ctx>, + Ctx: Copy, + Predicate: FnMut(usize, &T) -> bool, +{ + let mut res = capacity.map_or_else(Vec::new, Vec::with_capacity); + + let mut start_read = container.bits_read; + + loop { + let val = ::from_reader(container, ctx)?; + res.push(val); + + // This unwrap is safe as we are pushing to the vec immediately before it, + // so there will always be a last element + if predicate(container.bits_read - start_read, res.last().unwrap()) { + break; + } + } + + Ok(res) +} + impl<'a, T, Ctx, Predicate> DekuRead<'a, (Limit, Ctx)> for Vec where T: DekuRead<'a, Ctx>, @@ -111,6 +147,66 @@ where } } } + + /// Read `T`s until the given limit + /// * `limit` - the limiting factor on the amount of `T`s to read + /// * `inner_ctx` - The context required by `T`. It will be passed to every `T`s when constructing. + /// # Examples + /// ```rust + /// # use deku::ctx::*; + /// # use deku::DekuRead; + /// # use deku::bitvec::BitView; + /// let input = vec![1u8, 2, 3, 4]; + /// let (amt_read, v) = Vec::::read(input.view_bits(), (1.into(), Endian::Little)).unwrap(); + /// assert_eq!(amt_read, 32); + /// assert_eq!(vec![0x04030201], v) + /// ``` + fn from_reader( + container: &mut crate::container::Container, + (limit, inner_ctx): (Limit, Ctx), + ) -> Result + where + Self: Sized, + { + match limit { + // Read a given count of elements + Limit::Count(mut count) => { + // Handle the trivial case of reading an empty vector + if count == 0 { + return Ok(Vec::new()); + } + + // Otherwise, read until we have read `count` elements + reader_vec_with_predicate(container, Some(count), inner_ctx, move |_, _| { + count -= 1; + count == 0 + }) + } + + // Read until a given predicate returns true + Limit::Until(mut predicate, _) => { + reader_vec_with_predicate(container, None, inner_ctx, move |_, value| { + predicate(value) + }) + } + + // Read until a given quantity of bits have been read + Limit::BitSize(size) => { + let bit_size = size.0; + reader_vec_with_predicate(container, None, inner_ctx, move |read_bits, _| { + read_bits == bit_size + }) + } + + // Read until a given quantity of bits have been read + Limit::ByteSize(size) => { + let bit_size = size.0 * 8; + reader_vec_with_predicate(container, None, inner_ctx, move |read_bits, _| { + read_bits == bit_size + }) + } + } + } } impl<'a, T: DekuRead<'a>, Predicate: FnMut(&T) -> bool> DekuRead<'a, Limit> diff --git a/src/lib.rs b/src/lib.rs index dc2018e1..e4dec3bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -304,7 +304,10 @@ pub trait DekuRead<'a, Ctx = ()> { ctx: Ctx, ) -> Result<(usize, Self), DekuError> where - Self: Sized; + Self: Sized, + { + todo!(); + } fn from_reader( container: &mut crate::container::Container,