From 7da29a63c8d848a63fe6b6652cc9124ac0c9b18e Mon Sep 17 00:00:00 2001 From: wcampbell Date: Fri, 31 May 2024 17:56:40 -0400 Subject: [PATCH 1/5] Make bitvec completely optional --- Cargo.toml | 9 ++++---- deku-derive/Cargo.toml | 1 + deku-derive/src/macros/deku_write.rs | 8 ++++++- src/impls/nonzero.rs | 2 +- src/impls/primitive.rs | 20 ++++++++++++++++- src/lib.rs | 2 ++ src/reader.rs | 27 +++++++++++++++++++---- src/writer.rs | 32 ++++++++++++++++++++++++++-- tests/test_bit_container_size.rs | 2 ++ tests/test_enum.rs | 4 ++++ tests/test_to_slice.rs | 2 ++ 11 files changed, 96 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d3ad5f0..65a46711 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,16 +20,17 @@ members = [ ] [features] -default = ["std"] -std = ["deku_derive/std", "bitvec/std", "alloc", "no_std_io/std"] -alloc = ["bitvec/alloc"] +default = ["std", "bits"] +std = ["deku_derive/std", "bitvec?/std", "alloc", "no_std_io/std"] +alloc = ["bitvec?/alloc"] logging = ["deku_derive/logging", "log"] no-assert-string = ["deku_derive/no-assert-string"] error_in_core = [] +bits = ["dep:bitvec", "deku_derive/bits"] [dependencies] deku_derive = { version = "^0.17.0", path = "deku-derive", default-features = false} -bitvec = { version = "1.0.1", default-features = false } +bitvec = { version = "1.0.1", default-features = false, optional = true } log = { version = "0.4.21", optional = true } no_std_io = { version = "0.8.0", default-features = false, features = ["alloc"], package = "no_std_io2" } rustversion = "1.0.16" diff --git a/deku-derive/Cargo.toml b/deku-derive/Cargo.toml index b7c3d3a5..02d7b701 100644 --- a/deku-derive/Cargo.toml +++ b/deku-derive/Cargo.toml @@ -16,6 +16,7 @@ proc-macro = true std = ["proc-macro-crate"] logging = [] no-assert-string = [] +bits = [] [dependencies] quote = "1.0" diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs index 4ca8d0d5..7f7c6cf1 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -91,6 +91,7 @@ fn emit_struct(input: &DekuData) -> Result { // Implement `DekuContainerWrite` for types that don't need a context if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) { + #[cfg(feature = "bits")] tokens.extend(quote! { impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { type Error = ::#crate_::DekuError; @@ -100,7 +101,9 @@ fn emit_struct(input: &DekuData) -> Result { input.to_bits() } } + }); + tokens.extend(quote! { impl #imp core::convert::TryFrom<#ident> for Vec #wher { type Error = ::#crate_::DekuError; @@ -288,6 +291,7 @@ fn emit_enum(input: &DekuData) -> Result { // Implement `DekuContainerWrite` for types that don't need a context if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) { + #[cfg(feature = "bits")] tokens.extend(quote! { impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { type Error = ::#crate_::DekuError; @@ -297,7 +301,9 @@ fn emit_enum(input: &DekuData) -> Result { input.to_bits() } } + }); + tokens.extend(quote! { impl #imp core::convert::TryFrom<#ident> for Vec #wher { type Error = ::#crate_::DekuError; @@ -308,7 +314,7 @@ fn emit_enum(input: &DekuData) -> Result { } impl #imp DekuContainerWrite for #ident #wher {} - }) + }); } let (ctx_types, ctx_arg) = gen_ctx_types_and_arg(input.ctx.as_ref())?; diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index a9eefc86..c42419d7 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -43,6 +43,7 @@ macro_rules! ImplDekuTraitsCtx { macro_rules! ImplDekuTraits { ($typ:ty, $readtype:ty) => { ImplDekuTraitsCtx!($typ, $readtype, (), ()); + #[cfg(feature = "bits")] ImplDekuTraitsCtx!($typ, $readtype, (endian, bitsize), (Endian, BitSize)); ImplDekuTraitsCtx!($typ, $readtype, (endian, bytesize), (Endian, ByteSize)); ImplDekuTraitsCtx!($typ, $readtype, endian, Endian); @@ -66,7 +67,6 @@ ImplDekuTraits!(NonZeroIsize, isize); mod tests { use hexlit::hex; use rstest::rstest; - use std::io::Cursor; use crate::reader::Reader; diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index 27905c43..d113fc18 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -3,6 +3,7 @@ use alloc::borrow::Cow; use alloc::format; use core::convert::TryInto; +#[cfg(feature = "bits")] use bitvec::prelude::*; use no_std_io::io::{Read, Seek, Write}; @@ -12,6 +13,7 @@ use crate::writer::Writer; use crate::{DekuError, DekuReader, DekuWriter}; /// "Read" trait: read bits and construct type +#[cfg(feature = "bits")] trait DekuRead<'a, Ctx = ()> { /// Read bits and construct type /// * **input** - Input as bits @@ -32,8 +34,8 @@ trait DekuRead<'a, Ctx = ()> { } // specialize u8 for ByteSize +#[cfg(feature = "bits")] impl DekuRead<'_, (Endian, ByteSize)> for u8 { - /// Ignore endian and byte_size, as this is a `u8` #[inline] fn read( input: &BitSlice, @@ -59,6 +61,7 @@ impl DekuReader<'_, (Endian, ByteSize)> for u8 { let ret = reader.read_bytes_const::(&mut buf)?; let a = match ret { ReaderRet::Bytes => ::from_be_bytes(buf), + #[cfg(feature = "bits")] ReaderRet::Bits(bits) => { let Some(bits) = bits else { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); @@ -71,6 +74,7 @@ impl DekuReader<'_, (Endian, ByteSize)> for u8 { } } +#[cfg(feature = "bits")] impl DekuWriter<(Endian, BitSize)> for u8 { /// Ignore endian, as this is a `u8` #[inline(always)] @@ -126,6 +130,7 @@ impl DekuWriter<(Endian, ByteSize)> for u8 { macro_rules! ImplDekuReadBits { ($typ:ty, $inner:ty) => { + #[cfg(feature = "bits")] impl DekuRead<'_, (Endian, BitSize)> for $typ { #[inline(never)] fn read( @@ -202,6 +207,7 @@ macro_rules! ImplDekuReadBits { } } + #[cfg(feature = "bits")] impl DekuReader<'_, (Endian, BitSize)> for $typ { #[inline(always)] fn from_reader_with_ctx( @@ -228,6 +234,7 @@ macro_rules! ImplDekuReadBits { macro_rules! ImplDekuReadBytes { ($typ:ty, $inner:ty) => { + #[cfg(feature = "bits")] impl DekuRead<'_, (Endian, ByteSize)> for $typ { #[inline(never)] fn read( @@ -268,10 +275,12 @@ macro_rules! ImplDekuReadBytes { <$typ>::from_be_bytes(buf.try_into().unwrap()) } } + #[cfg(feature = "bits")] ReaderRet::Bits(Some(bits)) => { let a = <$typ>::read(&bits, (endian, size))?; a.1 } + #[cfg(feature = "bits")] ReaderRet::Bits(None) => { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); } @@ -284,6 +293,7 @@ macro_rules! ImplDekuReadBytes { macro_rules! ImplDekuReadSignExtend { ($typ:ty, $inner:ty) => { + #[cfg(feature = "bits")] impl DekuRead<'_, (Endian, ByteSize)> for $typ { #[inline(never)] fn read( @@ -317,6 +327,7 @@ macro_rules! ImplDekuReadSignExtend { <$typ>::from_be_bytes(buf.try_into()?) } } + #[cfg(feature = "bits")] ReaderRet::Bits(bits) => { let Some(bits) = bits else { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); @@ -334,6 +345,7 @@ macro_rules! ImplDekuReadSignExtend { } } + #[cfg(feature = "bits")] impl DekuRead<'_, (Endian, BitSize)> for $typ { #[inline(never)] fn read( @@ -351,6 +363,7 @@ macro_rules! ImplDekuReadSignExtend { } } + #[cfg(feature = "bits")] impl DekuReader<'_, (Endian, BitSize)> for $typ { #[inline(always)] fn from_reader_with_ctx( @@ -395,10 +408,12 @@ macro_rules! ForwardDekuRead { <$typ>::from_be_bytes(buf) } } + #[cfg(feature = "bits")] ReaderRet::Bits(Some(bits)) => { let a = <$typ>::read(&bits, (endian, ByteSize(MAX_TYPE_BYTES)))?; a.1 } + #[cfg(feature = "bits")] ReaderRet::Bits(None) => { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); } @@ -422,6 +437,7 @@ macro_rules! ForwardDekuRead { } //// Only have `bit_size`, set `endian` to `Endian::default`. + #[cfg(feature = "bits")] impl DekuReader<'_, BitSize> for $typ { #[inline(always)] fn from_reader_with_ctx( @@ -452,6 +468,7 @@ macro_rules! ForwardDekuRead { macro_rules! ImplDekuWrite { ($typ:ty) => { + #[cfg(feature = "bits")] impl DekuWriter<(Endian, BitSize)> for $typ { #[inline(always)] fn to_writer( @@ -578,6 +595,7 @@ macro_rules! ImplDekuWriteOnlyEndian { macro_rules! ForwardDekuWrite { ($typ:ty) => { + #[cfg(feature = "bits")] impl DekuWriter for $typ { #[inline(always)] fn to_writer( diff --git a/src/lib.rs b/src/lib.rs index c20622cd..ddc3fc19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,6 +360,7 @@ pub mod no_std_io { } /// re-export of bitvec +#[cfg(feature = "bits")] pub mod bitvec { pub use bitvec::prelude::*; pub use bitvec::view::BitView; @@ -568,6 +569,7 @@ pub trait DekuContainerWrite: DekuWriter<()> { /// assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); /// ``` #[inline(always)] + #[cfg(feature = "bits")] fn to_bits(&self) -> Result, DekuError> { let mut out_buf = Vec::new(); let mut cursor = no_std_io::Cursor::new(&mut out_buf); diff --git a/src/reader.rs b/src/reader.rs index d85414f8..e1e3857d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -2,6 +2,7 @@ use core::cmp::Ordering; +#[cfg(feature = "bits")] use bitvec::prelude::*; use no_std_io::io::{ErrorKind, Read, Seek, SeekFrom}; @@ -16,6 +17,7 @@ pub enum ReaderRet { /// Successfully read bytes Bytes, /// Successfully read bits + #[cfg(feature = "bits")] Bits(Option>), } @@ -24,6 +26,7 @@ pub const MAX_BITS_AMT: usize = 128; enum Leftover { Byte(u8), + #[cfg(feature = "bits")] Bits(BitVec), } @@ -120,6 +123,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> { /// ``` #[inline] pub fn rest(&mut self) -> Vec { + #[cfg(feature = "bits")] match &self.leftover { Some(Leftover::Bits(bits)) => bits.iter().by_vals().collect(), Some(Leftover::Byte(byte)) => { @@ -129,6 +133,8 @@ impl<'a, R: Read + Seek> Reader<'a, R> { } None => alloc::vec![], } + #[cfg(not(feature = "bits"))] + alloc::vec![] } /// Return true if we are at the end of a reader and there are no cached bits in the reader. @@ -164,10 +170,20 @@ impl<'a, R: Read + Seek> Reader<'a, R> { // TODO: maybe send into read_bytes() if amt >= 8 #[inline] pub fn skip_bits(&mut self, amt: usize) -> Result<(), DekuError> { - #[cfg(feature = "logging")] - log::trace!("skip_bits: {amt}"); - // Save, and keep the leftover bits since the read will most likely be less than a byte - self.read_bits(amt)?; + #[cfg(feature = "bits")] + { + #[cfg(feature = "logging")] + log::trace!("skip_bits: {amt}"); + // Save, and keep the leftover bits since the read will most likely be less than a byte + self.read_bits(amt)?; + } + + #[cfg(not(feature = "bits"))] + { + if amt > 0 { + panic!("deku features no-bits was used"); + } + } Ok(()) } @@ -183,6 +199,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> { /// # Params /// `amt` - Amount of bits that will be read. Must be <= [`MAX_BITS_AMT`]. #[inline(never)] + #[cfg(feature = "bits")] pub fn read_bits(&mut self, amt: usize) -> Result>, DekuError> { #[cfg(feature = "logging")] log::trace!("read_bits: requesting {amt} bits"); @@ -318,6 +335,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> { fn read_bytes_other(&mut self, amt: usize, buf: &mut [u8]) -> Result { match self.leftover { Some(Leftover::Byte(byte)) => self.read_bytes_leftover(buf, byte, amt), + #[cfg(feature = "bits")] Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(amt * 8)?)), _ => unreachable!(), } @@ -404,6 +422,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> { ) -> Result { match self.leftover { Some(Leftover::Byte(byte)) => self.read_bytes_const_leftover(buf, byte), + #[cfg(feature = "bits")] Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(N * 8)?)), _ => unreachable!(), } diff --git a/src/writer.rs b/src/writer.rs index 188e0fb7..ce3f3910 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,6 +1,8 @@ //! Writer for writer functions +#[cfg(feature = "bits")] use bitvec::bitvec; +#[cfg(feature = "bits")] use bitvec::{field::BitField, prelude::*}; use no_std_io::io::{Seek, SeekFrom, Write}; @@ -20,6 +22,7 @@ pub const MAX_BITS_AMT: usize = 128; pub struct Writer { pub(crate) inner: W, /// Leftover bits + #[cfg(feature = "bits")] pub leftover: BitVec, /// Total bits written pub bits_written: usize, @@ -29,8 +32,13 @@ impl Seek for Writer { fn seek(&mut self, pos: SeekFrom) -> no_std_io::io::Result { #[cfg(feature = "logging")] log::trace!("seek: {pos:?}"); + // clear leftover - self.leftover = BitVec::new(); + #[cfg(feature = "bits")] + { + self.leftover = BitVec::new(); + } + self.inner.seek(pos) } } @@ -41,6 +49,7 @@ impl Writer { pub fn new(inner: W) -> Self { Self { inner, + #[cfg(feature = "bits")] leftover: BitVec::new(), bits_written: 0, } @@ -49,7 +58,15 @@ impl Writer { /// Return the unused bits #[inline] pub fn rest(&mut self) -> alloc::vec::Vec { - self.leftover.iter().by_vals().collect() + #[cfg(feature = "bits")] + { + self.leftover.iter().by_vals().collect() + } + + #[cfg(not(feature = "bits"))] + { + alloc::vec![] + } } /// Write all `bits` to `Writer` buffer if bits can fit into a byte buffer. @@ -60,6 +77,7 @@ impl Writer { /// # Params /// `bits` - Amount of bits that will be written. length must be <= [`MAX_BITS_AMT`]. #[inline(never)] + #[cfg(feature = "bits")] pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { #[cfg(feature = "logging")] log::trace!("attempting {} bits", bits.len()); @@ -119,6 +137,7 @@ impl Writer { #[cfg(feature = "logging")] log::trace!("writing {} bytes: {buf:02x?}", buf.len()); + #[cfg(feature = "bits")] if !self.leftover.is_empty() { #[cfg(feature = "logging")] log::trace!("leftover exists"); @@ -133,6 +152,14 @@ impl Writer { self.bits_written += buf.len() * 8; } + #[cfg(not(feature = "bits"))] + { + if let Err(e) = self.inner.write_all(buf) { + return Err(DekuError::Io(e.kind())); + } + self.bits_written += buf.len() * 8; + } + Ok(()) } @@ -140,6 +167,7 @@ impl Writer { /// into a byte buffer #[inline] pub fn finalize(&mut self) -> Result<(), DekuError> { + #[cfg(feature = "bits")] if !self.leftover.is_empty() { #[cfg(feature = "logging")] log::trace!("finalized: {} bits leftover", self.leftover.len()); diff --git a/tests/test_bit_container_size.rs b/tests/test_bit_container_size.rs index 35c5f5eb..a3ddf150 100644 --- a/tests/test_bit_container_size.rs +++ b/tests/test_bit_container_size.rs @@ -1,6 +1,7 @@ use deku::prelude::*; use rstest::*; +#[cfg(feature = "bits")] #[derive(Debug, Default, PartialEq, DekuWrite, DekuRead)] #[deku(endian = "big")] struct Test { @@ -14,6 +15,7 @@ struct Test { field_u32_be: u32, } +#[cfg(feature = "bits")] #[rstest(input, #[should_panic( expected = "InvalidParam(\"bit size 5 of input is larger than bit requested size 4\")" diff --git a/tests/test_enum.rs b/tests/test_enum.rs index 56e61eb8..1c0393e6 100644 --- a/tests/test_enum.rs +++ b/tests/test_enum.rs @@ -178,7 +178,11 @@ fn test_id_pat_with_id() { } ); assert_eq!(input, &*v.to_bytes().unwrap()); +} +#[test] +#[cfg(feature = "bits")] +fn id_pat_with_id_bits() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8", bits = "2")] pub enum IdPatBits { diff --git a/tests/test_to_slice.rs b/tests/test_to_slice.rs index 78c6c113..e54b4c95 100644 --- a/tests/test_to_slice.rs +++ b/tests/test_to_slice.rs @@ -20,6 +20,7 @@ fn test_to_slice_bytes() { assert_eq!(amt_written, 3); } +#[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] pub struct B { #[deku(bits = "3", endian = "little")] @@ -27,6 +28,7 @@ pub struct B { } #[test] +#[cfg(feature = "bits")] fn test_to_slice_bits() { let b = B { inner: 0b111 }; From f7d8bce80f045bccddae49ce5ad2343a39ca6b8a Mon Sep 17 00:00:00 2001 From: wcampbell Date: Fri, 31 May 2024 18:17:43 -0400 Subject: [PATCH 2/5] Add bits feature to bench --- benches/deku.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/benches/deku.rs b/benches/deku.rs index d6e086cc..79514336 100644 --- a/benches/deku.rs +++ b/benches/deku.rs @@ -3,6 +3,7 @@ use std::io::{Cursor, Read, Seek}; use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; use deku::prelude::*; +#[cfg(feature = "bits")] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] struct DekuBits { #[deku(bits = 1)] @@ -61,6 +62,7 @@ fn criterion_benchmark(c: &mut Criterion) { })) }) }); + #[cfg(feature = "bits")] c.bench_function("deku_read_bits", |b| { let reader = Cursor::new(&[0x01; 1]); b.iter_batched( @@ -69,6 +71,7 @@ fn criterion_benchmark(c: &mut Criterion) { BatchSize::SmallInput, ) }); + #[cfg(feature = "bits")] c.bench_function("deku_write_bits", |b| { b.iter(|| { deku_write(black_box(&DekuBits { From 2d52758b5e9688650c2c80794b67679a0f4fda8f Mon Sep 17 00:00:00 2001 From: wcampbell Date: Fri, 31 May 2024 18:26:47 -0400 Subject: [PATCH 3/5] Add docs --- src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ddc3fc19..845cb8e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -338,6 +338,11 @@ environment, you will see logging messages as Deku does its deserialising. - `DekuError` whenever possible will use a `'static str`, to make the errors compile away when following a guide such as [min-sized-rust](/~https://github.com/johnthagen/min-sized-rust). +# Performance: Compile without `bitvec` +The feature `bits` enables the `bitvec` crate to use when reading and writing, which is enabled by default. +This however slows down the reading and writing process if your code doesn't use `bits` and the `bit_offset` +in `from_bytes`. + */ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] From cb807206b0b7acb7da6a495ba9d3de63f8b258a5 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Fri, 31 May 2024 18:32:45 -0400 Subject: [PATCH 4/5] Add bits to ensure_no_std features --- ensure_no_std/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ensure_no_std/Cargo.toml b/ensure_no_std/Cargo.toml index f5d70c5a..e6c88a08 100644 --- a/ensure_no_std/Cargo.toml +++ b/ensure_no_std/Cargo.toml @@ -20,6 +20,6 @@ alloc = [] [dependencies] cortex-m-rt = "0.7.3" -deku = { path = "../", default-features = false, features = ["alloc"] } +deku = { path = "../", default-features = false, features = ["alloc", "bits"] } embedded-alloc = "0.5.1" From 32b6779b6747076de4923777023d76e1776d875f Mon Sep 17 00:00:00 2001 From: wcampbell Date: Fri, 9 Aug 2024 22:24:55 -0400 Subject: [PATCH 5/5] Add bits feature to deku-derive * Use bits feature to correctly disallow bit related attributes from being used --- .github/workflows/main.yml | 2 + Cargo.toml | 28 +++++++ deku-derive/src/lib.rs | 43 +++++++++- deku-derive/src/macros/deku_read.rs | 84 ++++++++++++++----- deku-derive/src/macros/deku_write.rs | 38 ++++++++- deku-derive/src/macros/mod.rs | 12 +++ src/impls/bool.rs | 7 +- src/impls/boxed.rs | 2 + src/impls/hashmap.rs | 2 + src/impls/hashset.rs | 3 + src/impls/nonzero.rs | 5 +- src/impls/primitive.rs | 14 ++++ src/impls/slice.rs | 2 + src/impls/vec.rs | 4 + src/reader.rs | 2 + src/writer.rs | 15 +++- tests/mod.rs | 2 + tests/test_alloc.rs | 5 ++ tests/test_attributes/test_limits/mod.rs | 1 + tests/test_attributes/test_padding/mod.rs | 4 + .../test_padding/test_pad_bytes_after.rs | 4 + .../test_padding/test_pad_bytes_before.rs | 4 + tests/test_compile/mod.rs | 1 + tests/test_enum.rs | 2 + tests/test_from_bytes.rs | 2 + tests/test_from_reader.rs | 3 +- tests/test_regression.rs | 3 + tests/test_struct.rs | 5 ++ tests/test_to_bits.rs | 7 ++ 29 files changed, 275 insertions(+), 31 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4e3fd019..81684c51 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,6 +22,8 @@ jobs: - run: cargo test --all # run examples - run: cargo run --example 2>&1 | grep -P ' ' | awk '{print $1}' | xargs -i cargo run --example {} + # test with no bits feature (don't test docs) + - run: cargo test --lib --examples --tests --features std --no-default-features # Only build on MSRV, since trybuild will fail on older version build-msrv: diff --git a/Cargo.toml b/Cargo.toml index 65a46711..9a871510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,3 +56,31 @@ workspace = true # Triggers in macro generated code of darling # /~https://github.com/rust-lang/rust-clippy/issues/12643 manual-unwrap-or-default = "allow" + +[[example]] +name = "custom_reader_and_writer" +required-features = ["bits"] + +[[example]] +name = "deku_input" + +[[example]] +name = "enums_catch_all" +required-features = ["bits"] + +[[example]] +name = "enums" + +[[example]] +name = "example" +required-features = ["bits"] + +[[example]] +name = "ipv4" +required-features = ["bits"] + +[[example]] +name = "many" + +[[example]] +name = "read_all" diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs index a686b3b9..4c346734 100644 --- a/deku-derive/src/lib.rs +++ b/deku-derive/src/lib.rs @@ -136,6 +136,7 @@ struct DekuData { id_type: Option, /// enum only: bit size of the enum `id` + #[cfg(feature = "bits")] bits: Option, /// enum only: byte size of the enum `id` @@ -198,6 +199,7 @@ impl DekuData { magic: receiver.magic, id: receiver.id, id_type: receiver.id_type?, + #[cfg(feature = "bits")] bits: receiver.bits, bytes: receiver.bytes, seek_rewind: receiver.seek_rewind, @@ -224,7 +226,7 @@ impl DekuData { match data.data { ast::Data::Struct(_) => { // Validate id_* attributes are being used on an enum - if data.id_type.is_some() { + let ret = if data.id_type.is_some() { Err(cerror( data.id_type.span(), "`id_type` only supported on enum", @@ -233,11 +235,16 @@ impl DekuData { Err(cerror(data.id.span(), "`id` only supported on enum")) } else if data.bytes.is_some() { Err(cerror(data.bytes.span(), "`bytes` only supported on enum")) - } else if data.bits.is_some() { - Err(cerror(data.bits.span(), "`bits` only supported on enum")) } else { Ok(()) + }; + + #[cfg(feature = "bits")] + if ret.is_ok() && data.bits.is_some() { + return Err(cerror(data.bits.span(), "`bits` only supported on enum")); } + + ret } ast::Data::Enum(_) => { // Validate `id_type` or `id` is specified @@ -257,6 +264,7 @@ impl DekuData { } // Validate `id_*` used correctly + #[cfg(feature = "bits")] if data.id.is_some() && data.bits.is_some() { return Err(cerror( data.ident.span(), @@ -271,6 +279,7 @@ impl DekuData { } // Validate either `bits` or `bytes` is specified + #[cfg(feature = "bits")] if data.bits.is_some() && data.bytes.is_some() { return Err(cerror( data.bits.span(), @@ -336,7 +345,10 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataEnum<'a> { let id_args = crate::macros::gen_id_args( deku_data.endian.as_ref(), + #[cfg(feature = "bits")] deku_data.bits.as_ref(), + #[cfg(not(feature = "bits"))] + None, deku_data.bytes.as_ref(), )?; @@ -393,6 +405,7 @@ struct FieldData { endian: Option, /// field bit size + #[cfg(feature = "bits")] bits: Option, /// field byte size @@ -402,6 +415,7 @@ struct FieldData { count: Option, /// tokens providing the number of bits for the length of the container + #[cfg(feature = "bits")] bits_read: Option, /// tokens providing the number of bytes for the length of the container @@ -432,12 +446,14 @@ struct FieldData { skip: bool, /// pad a number of bits before + #[cfg(feature = "bits")] pad_bits_before: Option, /// pad a number of bytes before pad_bytes_before: Option, /// pad a number of bits after + #[cfg(feature = "bits")] pad_bits_after: Option, /// pad a number of bytes after @@ -486,9 +502,11 @@ impl FieldData { ident: receiver.ident, ty: receiver.ty, endian: receiver.endian, + #[cfg(feature = "bits")] bits: receiver.bits, bytes: receiver.bytes, count: receiver.count?, + #[cfg(feature = "bits")] bits_read: receiver.bits_read?, bytes_read: receiver.bytes_read?, until: receiver.until?, @@ -499,8 +517,10 @@ impl FieldData { reader: receiver.reader?, writer: receiver.writer?, skip: receiver.skip, + #[cfg(feature = "bits")] pad_bits_before: receiver.pad_bits_before?, pad_bytes_before: receiver.pad_bytes_before?, + #[cfg(feature = "bits")] pad_bits_after: receiver.pad_bits_after?, pad_bytes_after: receiver.pad_bytes_after?, temp: receiver.temp, @@ -524,6 +544,7 @@ impl FieldData { fn validate(data: &FieldData) -> Result<(), TokenStream> { // Validate either `read_bytes` or `read_bits` is specified + #[cfg(feature = "bits")] if data.bits_read.is_some() && data.bytes_read.is_some() { return Err(cerror( data.bits_read.span(), @@ -532,6 +553,7 @@ impl FieldData { } // Validate either `count` or `bits_read`/`bytes_read` is specified + #[cfg(feature = "bits")] if data.count.is_some() && (data.bits_read.is_some() || data.bytes_read.is_some()) { if data.bits_read.is_some() { return Err(cerror( @@ -546,7 +568,16 @@ impl FieldData { } } + #[cfg(not(feature = "bits"))] + if data.count.is_some() && data.bytes_read.is_some() { + return Err(cerror( + data.count.span(), + "conflicting: both `count` and `bytes_read` specified on field", + )); + } + // Validate either `bits` or `bytes` is specified + #[cfg(feature = "bits")] if data.bits.is_some() && data.bytes.is_some() { // FIXME: Use `Span::join` once out of nightly return Err(cerror( @@ -565,6 +596,7 @@ impl FieldData { } // Validate usage of read_all + #[cfg(feature = "bits")] if data.read_all && (data.until.is_some() || data.count.is_some() @@ -707,6 +739,7 @@ struct DekuReceiver { id_type: Result, ReplacementError>, /// enum only: bit size of the enum `id` + #[cfg(feature = "bits")] #[darling(default)] bits: Option, @@ -816,6 +849,7 @@ struct DekuFieldReceiver { endian: Option, /// field bit size + #[cfg(feature = "bits")] #[darling(default)] bits: Option, @@ -828,6 +862,7 @@ struct DekuFieldReceiver { count: Result, ReplacementError>, /// tokens providing the number of bits for the length of the container + #[cfg(feature = "bits")] #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] bits_read: Result, ReplacementError>, @@ -871,6 +906,7 @@ struct DekuFieldReceiver { skip: bool, /// pad a number of bits before + #[cfg(feature = "bits")] #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] pad_bits_before: Result, ReplacementError>, @@ -879,6 +915,7 @@ struct DekuFieldReceiver { pad_bytes_before: Result, ReplacementError>, /// pad a number of bits after + #[cfg(feature = "bits")] #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] pad_bits_after: Result, ReplacementError>, diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs index 587c05ab..130aaf9b 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -7,7 +7,7 @@ use quote::quote; use crate::macros::{ gen_ctx_types_and_arg, gen_field_args, gen_internal_field_ident, gen_internal_field_idents, - gen_type_from_ctx_id, pad_bits, token_contains_string, wrap_default_ctx, + gen_type_from_ctx_id, token_contains_string, wrap_default_ctx, }; use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id}; @@ -558,6 +558,7 @@ fn emit_bit_byte_offsets( (bit_offset, byte_offset) } +#[cfg(feature = "bits")] fn emit_padding(bit_size: &TokenStream) -> TokenStream { let crate_ = super::get_crate_name(); quote! { @@ -585,6 +586,29 @@ fn emit_padding(bit_size: &TokenStream) -> TokenStream { } } +// TODO: if this is a simple calculation such as "8 + 2", this could be const +#[cfg(not(feature = "bits"))] +fn emit_padding_bytes(bit_size: &TokenStream) -> TokenStream { + let crate_ = super::get_crate_name(); + quote! { + { + use core::convert::TryFrom; + extern crate alloc; + use alloc::borrow::Cow; + let __deku_pad = usize::try_from(#bit_size).map_err(|e| + ::#crate_::DekuError::InvalidParam(Cow::from(format!( + "Invalid padding param \"({})\": cannot convert to usize", + stringify!(#bit_size) + ))) + )?; + + + let mut buf = vec![0; __deku_pad]; + let _ = __deku_reader.read_bytes(__deku_pad, &mut buf)?; + } + } +} + fn emit_field_read( input: &DekuData, i: usize, @@ -602,6 +626,7 @@ fn emit_field_read( // fields to check usage of bit/byte offset let field_check_vars = [ &f.count, + #[cfg(feature = "bits")] &f.bits_read, &f.bytes_read, &f.until, @@ -705,7 +730,10 @@ fn emit_field_read( } else { let read_args = gen_field_args( field_endian, + #[cfg(feature = "bits")] f.bits.as_ref(), + #[cfg(not(feature = "bits"))] + None, f.bytes.as_ref(), f.ctx.as_ref(), )?; @@ -751,17 +779,6 @@ fn emit_field_read( )? } } - } else if let Some(field_bits) = &f.bits_read { - quote! { - { - use core::borrow::Borrow; - #type_as_deku_read::from_reader_with_ctx - ( - __deku_reader, - (::#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! { { @@ -795,27 +812,54 @@ fn emit_field_read( } } } else { - quote! { - #type_as_deku_read::from_reader_with_ctx - ( - __deku_reader, - (#read_args) - )? + let mut ret = quote! {}; + + #[cfg(feature = "bits")] + if let Some(field_bits) = &f.bits_read { + ret.extend(quote! { + { + use core::borrow::Borrow; + #type_as_deku_read::from_reader_with_ctx + ( + __deku_reader, + (::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args)) + )? + } + }) + } + if ret.is_empty() { + ret.extend(quote! { + #type_as_deku_read::from_reader_with_ctx + ( + __deku_reader, + (#read_args) + )? + }) } + + ret } }; - let pad_bits_before = pad_bits( + #[cfg(feature = "bits")] + let pad_bits_before = crate::macros::pad_bits( f.pad_bits_before.as_ref(), f.pad_bytes_before.as_ref(), emit_padding, ); - let pad_bits_after = pad_bits( + #[cfg(feature = "bits")] + let pad_bits_after = crate::macros::pad_bits( f.pad_bits_after.as_ref(), f.pad_bytes_after.as_ref(), emit_padding, ); + #[cfg(not(feature = "bits"))] + let pad_bits_before = crate::macros::pad_bytes(f.pad_bytes_before.as_ref(), emit_padding_bytes); + + #[cfg(not(feature = "bits"))] + let pad_bits_after = crate::macros::pad_bytes(f.pad_bytes_after.as_ref(), emit_padding_bytes); + let field_read_normal = quote! { let __deku_value = #field_read_func; let __deku_value: #field_type = #field_map(__deku_value)?; diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs index 7f7c6cf1..c0183eef 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -5,7 +5,7 @@ use proc_macro2::TokenStream; use quote::quote; use crate::macros::{ - assertion_failed, gen_ctx_types_and_arg, gen_field_args, gen_struct_destruction, pad_bits, + assertion_failed, gen_ctx_types_and_arg, gen_field_args, gen_struct_destruction, token_contains_string, wrap_default_ctx, }; use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id}; @@ -460,6 +460,7 @@ fn emit_bit_byte_offsets( (bit_offset, byte_offset) } +#[cfg(feature = "bits")] fn emit_padding(bit_size: &TokenStream) -> TokenStream { let crate_ = super::get_crate_name(); quote! { @@ -478,6 +479,26 @@ fn emit_padding(bit_size: &TokenStream) -> TokenStream { } } +// TODO: if this is a simple calculation such as "8 + 2", this could be const +#[cfg(not(feature = "bits"))] +fn emit_padding_bytes(bit_size: &TokenStream) -> TokenStream { + let crate_ = super::get_crate_name(); + quote! { + { + use core::convert::TryFrom; + extern crate alloc; + use alloc::borrow::Cow; + let __deku_pad = usize::try_from(#bit_size).map_err(|e| + ::#crate_::DekuError::InvalidParam(Cow::from(format!( + "Invalid padding param \"({})\": cannot convert to usize", + stringify!(#bit_size) + ))) + )?; + __deku_writer.write_bytes(&vec![0; __deku_pad])?; + } + } +} + fn emit_field_write( input: &DekuData, i: usize, @@ -582,7 +603,10 @@ fn emit_field_write( } else { let write_args = gen_field_args( field_endian, + #[cfg(feature = "bits")] f.bits.as_ref(), + #[cfg(not(feature = "bits"))] + None, f.bytes.as_ref(), f.ctx.as_ref(), )?; @@ -602,17 +626,25 @@ fn emit_field_write( } }; - let pad_bits_before = pad_bits( + #[cfg(feature = "bits")] + let pad_bits_before = crate::macros::pad_bits( f.pad_bits_before.as_ref(), f.pad_bytes_before.as_ref(), emit_padding, ); - let pad_bits_after = pad_bits( + #[cfg(feature = "bits")] + let pad_bits_after = crate::macros::pad_bits( f.pad_bits_after.as_ref(), f.pad_bytes_after.as_ref(), emit_padding, ); + #[cfg(not(feature = "bits"))] + let pad_bits_before = crate::macros::pad_bytes(f.pad_bytes_before.as_ref(), emit_padding_bytes); + + #[cfg(not(feature = "bits"))] + let pad_bits_after = crate::macros::pad_bytes(f.pad_bytes_after.as_ref(), emit_padding_bytes); + let field_write_normal = quote! { #field_write_func ?; }; diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs index 847db153..19555dd0 100644 --- a/deku-derive/src/macros/mod.rs +++ b/deku-derive/src/macros/mod.rs @@ -340,6 +340,7 @@ fn token_contains_string(tok: &Option, s: &str) -> bool { .unwrap_or(false) } +#[cfg(feature = "bits")] fn pad_bits( bits: Option<&TokenStream>, bytes: Option<&TokenStream>, @@ -355,6 +356,17 @@ fn pad_bits( } } +#[cfg(not(feature = "bits"))] +fn pad_bytes( + bytes: Option<&TokenStream>, + emit_padding: fn(&TokenStream) -> TokenStream, +) -> TokenStream { + match bytes { + Some(pad_bytes) => emit_padding("e! {((#pad_bytes))}), + None => quote!(), + } +} + /// assertion is false, raise error fn assertion_failed( v: &TokenStream, diff --git a/src/impls/bool.rs b/src/impls/bool.rs index e8f27432..f0bda1f4 100644 --- a/src/impls/bool.rs +++ b/src/impls/bool.rs @@ -73,6 +73,7 @@ mod tests { assert_eq!(expected, res_read); } + #[cfg(feature = "bits")] #[test] fn test_bool_with_context() { let input = &[0b01_000000]; @@ -83,12 +84,16 @@ mod tests { assert!(res_read); } + #[cfg(feature = "bits")] #[test] - fn test_writer() { + fn test_writer_bits() { let mut writer = Writer::new(Cursor::new(vec![])); true.to_writer(&mut writer, BitSize(1)).unwrap(); assert_eq!(vec![true], writer.rest()); + } + #[test] + fn test_writer() { let mut writer = Writer::new(Cursor::new(vec![])); true.to_writer(&mut writer, ()).unwrap(); assert_eq!(vec![1], writer.inner.into_inner()); diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs index 46011b0b..af3af822 100644 --- a/src/impls/boxed.rs +++ b/src/impls/boxed.rs @@ -81,6 +81,7 @@ mod tests { use crate::ctx::*; use crate::native_endian; use crate::reader::Reader; + #[cfg(feature = "bits")] use bitvec::prelude::*; #[rstest(input, expected, @@ -101,6 +102,7 @@ mod tests { } // Note: Copied tests from vec.rs impl + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write, case::normal_le([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Little, Some(16), 2.into(), vec![0xBBAA, 0xDDCC].into_boxed_slice(), bits![u8, Msb0;], &[], vec![0xAA, 0xBB, 0xCC, 0xDD]), case::normal_be([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Big, Some(16), 2.into(), vec![0xAABB, 0xCCDD].into_boxed_slice(), bits![u8, Msb0;], &[], vec![0xAA, 0xBB, 0xCC, 0xDD]), diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs index ea5cc3c3..642be7dc 100644 --- a/src/impls/hashmap.rs +++ b/src/impls/hashmap.rs @@ -231,6 +231,7 @@ mod tests { use crate::reader::Reader; use super::*; + #[cfg(feature = "bits")] use bitvec::prelude::*; // Macro to create a deterministic HashMap for tests @@ -250,6 +251,7 @@ mod tests { }; ); + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, case::count_0([0xAA].as_ref(), Endian::Little, Some(8), 0.into(), FxHashMap::default(), bits![u8, Msb0;], &[0xaa]), case::count_1([0x01, 0xAA, 0x02, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0x02, 0xbb]), diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs index d061aed6..7f11e092 100644 --- a/src/impls/hashset.rs +++ b/src/impls/hashset.rs @@ -212,6 +212,7 @@ impl, S, Ctx: Copy> DekuWriter for HashSet { #[cfg(test)] mod tests { + #[cfg(feature = "bits")] use crate::bitvec::{bits, BitSlice, Msb0}; use no_std_io::io::Cursor; use rstest::rstest; @@ -221,6 +222,7 @@ mod tests { use super::*; + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, case::count_0([0xAA].as_ref(), Endian::Little, Some(8), 0.into(), FxHashSet::default(), bits![u8, Msb0;], &[0xaa]), case::count_1([0xAA, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]), @@ -285,6 +287,7 @@ mod tests { } // Note: These tests also exist in boxed.rs + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write, case::normal_le([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Little, Some(16), 2.into(), vec![0xBBAA, 0xDDCC].into_iter().collect(), bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0xAA, 0xBB]), case::normal_be([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Big, Some(16), 2.into(), vec![0xAABB, 0xCCDD].into_iter().collect(), bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0xAA, 0xBB]), diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index c42419d7..6000742b 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -65,13 +65,14 @@ ImplDekuTraits!(NonZeroIsize, isize); #[cfg(test)] mod tests { + use std::io::Cursor; + use hexlit::hex; use rstest::rstest; use crate::reader::Reader; use super::*; - use bitvec::prelude::*; #[rstest(input, expected, case(&hex!("FF"), NonZeroU8::new(0xFF).unwrap()), @@ -80,7 +81,7 @@ mod tests { case(&hex!("00"), NonZeroU8::new(0xFF).unwrap()), )] fn test_non_zero(input: &[u8], expected: NonZeroU8) { - let mut cursor = std::io::Cursor::new(input); + let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); let res_read = NonZeroU8::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index d113fc18..a3aff12b 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -803,6 +803,7 @@ mod tests { native_endian!(-0.006_f64) ); + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, expected, expected_rest_bits, expected_rest_bytes, case::normal([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Little, Some(32), 0xAABB_CCDD, bits![u8, Msb0;], &[]), case::normal([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Big, Some(32), 0xDDCC_BBAA, bits![u8, Msb0;], &[]), @@ -880,6 +881,7 @@ mod tests { assert_eq!(expected_rest_bytes, buf); } + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, expected, expected_leftover, case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD], vec![]), case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA], vec![]), @@ -925,6 +927,7 @@ mod tests { assert_hex::assert_eq_hex!(expected, writer.inner.into_inner()); } + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, expected, expected_write, case::normal([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Little, Some(32), 0xAABB_CCDD, vec![0xDD, 0xCC, 0xBB, 0xAA]), )] @@ -970,11 +973,17 @@ mod tests { }; } + #[cfg(feature = "bits")] TestSignExtending!(test_sign_extend_i8, i8); + #[cfg(feature = "bits")] TestSignExtending!(test_sign_extend_i16, i16); + #[cfg(feature = "bits")] TestSignExtending!(test_sign_extend_i32, i32); + #[cfg(feature = "bits")] TestSignExtending!(test_sign_extend_i64, i64); + #[cfg(feature = "bits")] TestSignExtending!(test_sign_extend_i128, i128); + #[cfg(feature = "bits")] TestSignExtending!(test_sign_extend_isize, isize); macro_rules! TestSignExtendingPanic { @@ -998,9 +1007,14 @@ mod tests { }; } + #[cfg(feature = "bits")] TestSignExtendingPanic!(test_sign_extend_i8_panic, i8, 8); + #[cfg(feature = "bits")] TestSignExtendingPanic!(test_sign_extend_i16_panic, i16, 16); + #[cfg(feature = "bits")] TestSignExtendingPanic!(test_sign_extend_i32_panic, i32, 32); + #[cfg(feature = "bits")] TestSignExtendingPanic!(test_sign_extend_i64_panic, i64, 64); + #[cfg(feature = "bits")] TestSignExtendingPanic!(test_sign_extend_i128_panic, i128, 128); } diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 40269062..15b3e18b 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -96,12 +96,14 @@ where #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "bits")] use bitvec::prelude::*; use rstest::rstest; use std::io::Cursor; use crate::{ctx::Endian, reader::Reader, writer::Writer, DekuReader}; + #[cfg(feature = "bits")] #[rstest(input,endian,expected, case::normal_le([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Little, [0xCCDD, 0xAABB]), case::normal_be([0xDD, 0xCC, 0xBB, 0xAA].as_ref(), Endian::Big, [0xDDCC, 0xBBAA]), diff --git a/src/impls/vec.rs b/src/impls/vec.rs index da3bd471..33c89c99 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -176,6 +176,7 @@ impl, Ctx: Copy> DekuWriter for Vec { #[cfg(test)] mod tests { + #[cfg(feature = "bits")] use crate::bitvec::{bits, BitSlice, Msb0}; use rstest::rstest; use std::io::Cursor; @@ -184,6 +185,7 @@ mod tests { use super::*; + #[cfg(feature = "bits")] #[rstest(input, limit, expected, expected_rest_bits, expected_rest_bytes, case::count_0([0xAA].as_ref(), 0.into(), vec![], bits![u8, Msb0;], &[0xaa]), case::count_1([0xAA, 0xBB].as_ref(), 1.into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]), @@ -211,6 +213,7 @@ mod tests { assert_eq!(expected_rest_bytes, buf); } + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, case::count_0([0xAA].as_ref(), Endian::Little, Some(8), 0.into(), vec![], bits![u8, Msb0;], &[0xaa]), case::count_1([0xAA, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]), @@ -271,6 +274,7 @@ mod tests { } // Note: These tests also exist in boxed.rs + #[cfg(feature = "bits")] #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write, case::normal_le([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Little, Some(16), 2.into(), vec![0xBBAA, 0xDDCC], bits![u8, Msb0;], &[], vec![0xAA, 0xBB, 0xCC, 0xDD]), case::normal_be([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Big, Some(16), 2.into(), vec![0xAABB, 0xCCDD], bits![u8, Msb0;], &[], vec![0xAA, 0xBB, 0xCC, 0xDD]), diff --git a/src/reader.rs b/src/reader.rs index e1e3857d..d60b5b16 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -478,6 +478,7 @@ mod tests { use no_std_io::io::Cursor; #[test] + #[cfg(feature = "bits")] fn test_end() { let input = hex!("aabb"); let mut cursor = Cursor::new(input); @@ -498,6 +499,7 @@ mod tests { } #[test] + #[cfg(feature = "bits")] fn test_bits_less() { let input = hex!("aa"); let mut cursor = Cursor::new(input); diff --git a/src/writer.rs b/src/writer.rs index ce3f3910..bfe25769 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -206,7 +206,8 @@ mod tests { use hexlit::hex; #[test] - fn test_writer() { + #[cfg(feature = "bits")] + fn test_writer_bits() { let mut out_buf = Cursor::new(vec![]); let mut writer = Writer::new(&mut out_buf); @@ -243,4 +244,16 @@ mod tests { &mut vec![0xaa, 0xbb, 0xf1, 0xaa, 0x1f, 0x1a, 0xaf] ); } + + #[test] + #[cfg(feature = "bits")] + fn test_writer_bytes() { + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + assert_eq!(&mut out_buf.into_inner(), &mut vec![0xaa]); + } } diff --git a/tests/mod.rs b/tests/mod.rs index a61389cf..eece9c90 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -2,3 +2,5 @@ mod test_attributes; mod test_compile; +#[cfg(feature = "bits")] +mod test_to_bits; diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs index 5da05c81..a5348d6e 100644 --- a/tests/test_alloc.rs +++ b/tests/test_alloc.rs @@ -7,12 +7,14 @@ use deku::prelude::*; #[global_allocator] static A: AllocCounterSystem = AllocCounterSystem; +#[cfg(feature = "bits")] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[deku(ctx = "_endian: Endian")] struct NestedStruct { field_a: u8, } +#[cfg(feature = "bits")] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[deku(id_type = "u8", ctx = "_endian: Endian")] enum NestedEnum { @@ -20,6 +22,7 @@ enum NestedEnum { VarA(u8), } +#[cfg(feature = "bits")] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[deku(id_type = "u32", bytes = 2, ctx = "_endian: Endian")] enum NestedEnum2 { @@ -27,6 +30,7 @@ enum NestedEnum2 { VarA(u8), } +#[cfg(feature = "bits")] #[derive(Debug, PartialEq, DekuRead, DekuWrite)] #[deku(endian = "big")] struct TestDeku { @@ -44,6 +48,7 @@ struct TestDeku { //field_i: NestedEnum2, } +#[cfg(feature = "bits")] mod tests { use alloc_counter::count_alloc; use hexlit::hex; diff --git a/tests/test_attributes/test_limits/mod.rs b/tests/test_attributes/test_limits/mod.rs index c7e74d0a..b4ce8478 100644 --- a/tests/test_attributes/test_limits/mod.rs +++ b/tests/test_attributes/test_limits/mod.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "bits")] mod test_bits_read; mod test_bytes_read; mod test_count; diff --git a/tests/test_attributes/test_padding/mod.rs b/tests/test_attributes/test_padding/mod.rs index 5c1d5e7a..8ac0311d 100644 --- a/tests/test_attributes/test_padding/mod.rs +++ b/tests/test_attributes/test_padding/mod.rs @@ -2,11 +2,14 @@ use std::convert::{TryFrom, TryInto}; use deku::prelude::*; +#[cfg(feature = "bits")] mod test_pad_bits_after; +#[cfg(feature = "bits")] mod test_pad_bits_before; mod test_pad_bytes_after; mod test_pad_bytes_before; +#[cfg(feature = "bits")] #[test] #[allow(clippy::identity_op)] fn test_pad_bits_before_and_pad_bytes_before() { @@ -34,6 +37,7 @@ fn test_pad_bits_before_and_pad_bytes_before() { assert_eq!(vec![0b10_000000, 0x00, 0xbb], ret_write); } +#[cfg(feature = "bits")] #[test] fn test_pad_bits_after_and_pad_bytes_after() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] diff --git a/tests/test_attributes/test_padding/test_pad_bytes_after.rs b/tests/test_attributes/test_padding/test_pad_bytes_after.rs index 846c61c7..d8baa16b 100644 --- a/tests/test_attributes/test_padding/test_pad_bytes_after.rs +++ b/tests/test_attributes/test_padding/test_pad_bytes_after.rs @@ -42,6 +42,8 @@ fn test_pad_bytes_after_not_enough() { let _ret_read = TestStruct::try_from(data.as_slice()).unwrap(); } +// TODO: add cfg test with updated msg for not(bits) +#[cfg(feature = "bits")] #[test] #[should_panic( expected = r#"InvalidParam("Invalid padding param \"(((- 2) * 8))\": cannot convert to usize")"# @@ -59,6 +61,8 @@ fn test_pad_bytes_after_read_err() { let _ret_read = TestStruct::try_from(data.as_slice()).unwrap(); } +// TODO: add cfg test with updated msg for not(bits) +#[cfg(feature = "bits")] #[test] #[should_panic( expected = r#"InvalidParam("Invalid padding param \"(((- 2) * 8))\": cannot convert to usize")"# diff --git a/tests/test_attributes/test_padding/test_pad_bytes_before.rs b/tests/test_attributes/test_padding/test_pad_bytes_before.rs index f9a92e39..6ebbb9cb 100644 --- a/tests/test_attributes/test_padding/test_pad_bytes_before.rs +++ b/tests/test_attributes/test_padding/test_pad_bytes_before.rs @@ -42,6 +42,8 @@ fn test_pad_bytes_before_not_enough() { let _ret_read = TestStruct::try_from(data.as_slice()).unwrap(); } +// TODO: add cfg test with updated msg for not(bits) +#[cfg(feature = "bits")] #[test] #[should_panic( expected = r#"InvalidParam("Invalid padding param \"(((- 2) * 8))\": cannot convert to usize")"# @@ -59,6 +61,8 @@ fn test_pad_bytes_before_read_err() { let _ret_read = TestStruct::try_from(data.as_slice()).unwrap(); } +// TODO: add cfg test with updated msg for not(bits) +#[cfg(feature = "bits")] #[test] #[should_panic( expected = r#"InvalidParam("Invalid padding param \"(((- 2) * 8))\": cannot convert to usize")"# diff --git a/tests/test_compile/mod.rs b/tests/test_compile/mod.rs index 92a37681..30a680e2 100644 --- a/tests/test_compile/mod.rs +++ b/tests/test_compile/mod.rs @@ -1,4 +1,5 @@ #[test] +#[cfg(feature = "bits")] #[cfg(not(tarpaulin))] #[cfg_attr(miri, ignore)] fn test_compile() { diff --git a/tests/test_enum.rs b/tests/test_enum.rs index 1c0393e6..f839487d 100644 --- a/tests/test_enum.rs +++ b/tests/test_enum.rs @@ -8,6 +8,7 @@ use rstest::*; /// General smoke tests for enums /// TODO: These should be divided into smaller tests +#[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8")] enum TestEnum { @@ -32,6 +33,7 @@ enum TestEnum { VarDefault { id: u8, value: u8 }, } +#[cfg(feature = "bits")] #[rstest(input,expected, case(&hex!("01AB"), TestEnum::VarA(0xAB)), case(&hex!("0269"), TestEnum::VarB(0b0110, 0b1001)), diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs index a992c5ba..aac1f4d7 100644 --- a/tests/test_from_bytes.rs +++ b/tests/test_from_bytes.rs @@ -1,5 +1,6 @@ use deku::prelude::*; +#[cfg(feature = "bits")] #[test] fn test_from_bytes_struct() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] @@ -28,6 +29,7 @@ fn test_from_bytes_struct() { assert_eq!(0, i); } +#[cfg(feature = "bits")] #[test] fn test_from_bytes_enum() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] diff --git a/tests/test_from_reader.rs b/tests/test_from_reader.rs index 02c927d3..1e7e87dc 100644 --- a/tests/test_from_reader.rs +++ b/tests/test_from_reader.rs @@ -1,6 +1,7 @@ use deku::prelude::*; use no_std_io::io::Seek; +#[cfg(feature = "bits")] #[test] fn test_from_reader_struct() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] @@ -21,7 +22,6 @@ fn test_from_reader_struct() { total_read = amt_read; assert_eq!(TestDeku(0b0110), ret_read); - env_logger::init(); c.rewind().unwrap(); let (amt_read, ret_read) = TestDeku::from_reader((&mut c, total_read)).unwrap(); assert_eq!(amt_read, 12); @@ -34,6 +34,7 @@ fn test_from_reader_struct() { assert_eq!(TestDeku(0b1010), ret_read); } +#[cfg(feature = "bits")] #[test] fn test_from_reader_enum() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] diff --git a/tests/test_regression.rs b/tests/test_regression.rs index 0bf30b85..e3a75890 100644 --- a/tests/test_regression.rs +++ b/tests/test_regression.rs @@ -5,6 +5,7 @@ use std::io::Cursor; // BitSlice to type // // /~https://github.com/sharksforarms/deku/issues/224 +#[cfg(feature = "bits")] #[test] fn issue_224() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] @@ -66,6 +67,7 @@ fn issue_224() { let _packet = Packet::from_reader((&mut c, 0)).unwrap(); } +#[cfg(feature = "bits")] // Extra zeroes added when reading fewer bytes than needed to fill a number // // /~https://github.com/sharksforarms/deku/issues/282 @@ -128,6 +130,7 @@ mod issue_282 { // Invalid alignment assumptions when converting doing Bits and Bytes optimizations // // /~https://github.com/sharksforarms/deku/issues/292 +#[cfg(feature = "bits")] #[test] fn test_regression_292() { let test_data = [0x0f, 0xf0]; diff --git a/tests/test_struct.rs b/tests/test_struct.rs index c5bb117b..92ed213e 100644 --- a/tests/test_struct.rs +++ b/tests/test_struct.rs @@ -10,11 +10,13 @@ mod test_common; /// TODO: These should be divided into smaller tests // Common struct to test nesting +#[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] pub struct DoubleNestedDeku { pub data: u16, } +#[cfg(feature = "bits")] // Common struct to test nesting #[derive(PartialEq, Debug, DekuRead, DekuWrite)] pub struct NestedDeku { @@ -26,6 +28,7 @@ pub struct NestedDeku { pub inner: DoubleNestedDeku, } +#[cfg(feature = "bits")] #[test] #[should_panic(expected = r#"Parse("Too much data")"#)] fn test_read_too_much_data() { @@ -39,6 +42,7 @@ fn test_read_too_much_data() { TestStruct::try_from(test_data).unwrap(); } +#[cfg(feature = "bits")] #[test] fn test_unnamed_struct() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] @@ -96,6 +100,7 @@ fn test_unnamed_struct() { assert_eq!(test_data, ret_write); } +#[cfg(feature = "bits")] #[test] fn test_named_struct() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] diff --git a/tests/test_to_bits.rs b/tests/test_to_bits.rs index 1515a403..860aaa76 100644 --- a/tests/test_to_bits.rs +++ b/tests/test_to_bits.rs @@ -1,8 +1,10 @@ use std::convert::TryFrom; +#[cfg(feature = "bits")] use deku::bitvec::Lsb0; use deku::prelude::*; +#[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] pub struct Test { #[deku(bits = "4")] @@ -11,6 +13,7 @@ pub struct Test { pub b: u8, } +#[cfg(feature = "bits")] #[test] fn test_to_bits_correct() { let test_data: &[u8] = &[0xf1]; @@ -19,6 +22,7 @@ fn test_to_bits_correct() { assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1], bits); } +#[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] pub struct TestOver { #[deku(bits = "4")] @@ -29,6 +33,7 @@ pub struct TestOver { pub c: u8, } +#[cfg(feature = "bits")] #[test] fn test_to_bits_correct_over() { let test_data: &[u8] = &[0xf1, 0x80]; @@ -37,6 +42,7 @@ fn test_to_bits_correct_over() { assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); } +#[cfg(feature = "bits")] #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8", bits = "4")] enum TestEnum { @@ -44,6 +50,7 @@ enum TestEnum { VarA, } +#[cfg(feature = "bits")] #[test] fn test_to_bits_enum() { let test_data: &[u8] = &[0b1010_0000];