From 07eac20f5b7df7b6d015c20b9b0d2f63dbadba32 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Sat, 16 Dec 2023 18:33:58 -0500 Subject: [PATCH 1/9] Add skipping to logging --- deku-derive/src/macros/deku_write.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs index 6e0b7154..e1be538b 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -568,11 +568,20 @@ fn emit_field_write( #field_write_func ?; }; + let skipping_log = if cfg!(feature = "logging") { + quote! { + log::trace!("skipping"); + } + } else { + quote! {} + }; + let field_write_tokens = match (f.skip, &f.cond) { (true, Some(field_cond)) => { // #[deku(skip, cond = "...")] ==> `skip` if `cond` quote! { if (#field_cond) { + #skipping_log // skipping, no write } else { #field_write_normal @@ -582,6 +591,7 @@ fn emit_field_write( (true, None) => { // #[deku(skip)] ==> `skip` quote! { + #skipping_log // skipping, no write } } From 360d06ba596a44cde31c7429a563a9d870f6be04 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Fri, 18 Aug 2023 18:29:50 -0400 Subject: [PATCH 2/9] Impl Write support --- Cargo.toml | 1 + deku-derive/src/lib.rs | 2 +- deku-derive/src/macros/deku_read.rs | 2 +- deku-derive/src/macros/deku_write.rs | 116 +++++------ examples/custom_reader_and_writer.rs | 13 +- src/attributes.rs | 6 +- src/error.rs | 4 + src/impls/bool.rs | 42 ++-- src/impls/boxed.rs | 50 ++--- src/impls/cow.rs | 25 +-- src/impls/cstring.rs | 25 +-- src/impls/hashmap.rs | 70 ++----- src/impls/hashset.rs | 41 ++-- src/impls/ipaddr.rs | 69 ++++--- src/impls/nonzero.rs | 26 +-- src/impls/option.rs | 23 +-- src/impls/primitive.rs | 188 ++++++++++------- src/impls/slice.rs | 113 +++------- src/impls/tuple.rs | 22 +- src/impls/unit.rs | 22 +- src/impls/vec.rs | 73 +++++-- src/lib.rs | 65 ++++-- src/prelude.rs | 4 +- src/reader.rs | 2 +- src/writer.rs | 194 ++++++++++++++++++ tests/test_attributes/test_ctx.rs | 27 ++- .../test_compile/cases/enum_validation.stderr | 4 +- .../test_compile/cases/internal_variables.rs | 5 +- .../cases/internal_variables.stderr | 4 +- tests/test_compile/cases/temp_field.stderr | 2 +- tests/test_generic.rs | 4 +- tests/test_struct.rs | 25 +++ tests/test_to_bits.rs | 53 +++++ 33 files changed, 818 insertions(+), 504 deletions(-) create mode 100644 src/writer.rs create mode 100644 tests/test_to_bits.rs diff --git a/Cargo.toml b/Cargo.toml index 21c42b18..80ba0159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ alloc_counter = "0.0.4" trybuild = "1.0.77" rustc-hash = "1.1.0" env_logger = "0.10.0" +assert_hex = "0.2.2" [[bench]] name = "deku" diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs index 061a0c2e..ccc32cb0 100644 --- a/deku-derive/src/lib.rs +++ b/deku-derive/src/lib.rs @@ -671,7 +671,7 @@ fn apply_replacements(input: &syn::LitStr) -> Result, Repla let input_str = input_value .replace("deku::reader", "__deku_reader") - .replace("deku::output", "__deku_output") // part of the public API `write` + .replace("deku::writer", "__deku_writer") .replace("deku::bit_offset", "__deku_bit_offset") .replace("deku::byte_offset", "__deku_byte_offset"); diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs index 9e7a788d..262c57b1 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -605,7 +605,7 @@ fn emit_field_read( let trace_field_log = if cfg!(feature = "logging") { quote! { - log::trace!("Reading: {}::{}", #ident, #field_ident_str); + log::trace!("Reading: {}.{}", #ident, #field_ident_str); } } else { quote! {} diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs index e1be538b..1f9d2592 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -47,26 +47,8 @@ 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()) { - let to_bits_body = wrap_default_ctx( - quote! { - match *self { - #destructured => { - let mut __deku_acc: ::#crate_::bitvec::BitVec = ::#crate_::bitvec::BitVec::new(); - let __deku_output = &mut __deku_acc; - - #magic_write - #(#field_writes)* - - Ok(__deku_acc) - } - } - }, - &input.ctx, - &input.ctx_default, - ); - tokens.extend(quote! { - impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { + impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { type Error = ::#crate_::DekuError; fn try_from(input: #ident) -> core::result::Result { @@ -84,13 +66,22 @@ fn emit_struct(input: &DekuData) -> Result { impl #imp DekuContainerWrite for #ident #wher { fn to_bytes(&self) -> core::result::Result, ::#crate_::DekuError> { - let mut acc: ::#crate_::bitvec::BitVec = self.to_bits()?; - Ok(acc.into_vec()) + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + __deku_writer.finalize()?; + Ok(out_buf) } #[allow(unused_variables)] fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - #to_bits_body + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + let mut leftover = __deku_writer.leftover; + let mut bv = ::#crate_::bitvec::BitVec::from_slice(&out_buf); + bv.append(&mut leftover); + Ok(bv) } } }); @@ -122,9 +113,9 @@ fn emit_struct(input: &DekuData) -> Result { } } - impl #imp DekuWrite<#ctx_types> for #ident #wher { + impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher { #[allow(unused_variables)] - fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> { + fn to_writer(&self, __deku_writer: &mut ::#crate_::writer::Writer, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> { #write_body } } @@ -134,9 +125,9 @@ fn emit_struct(input: &DekuData) -> Result { let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default); tokens.extend(quote! { - impl #imp DekuWrite for #ident #wher { + impl #imp ::#crate_::DekuWriter for #ident #wher { #[allow(unused_variables)] - fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec, _: ()) -> core::result::Result<(), ::#crate_::DekuError> { + fn to_writer(&self, __deku_writer: &mut ::#crate_::writer::Writer, _: ()) -> core::result::Result<(), ::#crate_::DekuError> { #write_body } } @@ -200,19 +191,19 @@ fn emit_enum(input: &DekuData) -> Result { Id::TokenStream(v) => { quote! { let mut __deku_variant_id: #id_type = #v; - __deku_variant_id.write(__deku_output, (#id_args))?; + __deku_variant_id.to_writer(__deku_writer, (#id_args))?; } } Id::Int(v) => { quote! { let mut __deku_variant_id: #id_type = #v; - __deku_variant_id.write(__deku_output, (#id_args))?; + __deku_variant_id.to_writer(__deku_writer, (#id_args))?; } } Id::LitByteStr(v) => { quote! { let mut __deku_variant_id: #id_type = *#v; - __deku_variant_id.write(__deku_output, (#id_args))?; + __deku_variant_id.to_writer(__deku_writer, (#id_args))?; } } } @@ -221,7 +212,7 @@ fn emit_enum(input: &DekuData) -> Result { } else if has_discriminant { quote! { let mut __deku_variant_id: #id_type = Self::#variant_ident as #id_type; - __deku_variant_id.write(__deku_output, (#id_args))?; + __deku_variant_id.to_writer(__deku_writer, (#id_args))?; } } else { return Err(syn::Error::new( @@ -266,25 +257,8 @@ 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()) { - let to_bits_body = wrap_default_ctx( - quote! { - let mut __deku_acc: ::#crate_::bitvec::BitVec = ::#crate_::bitvec::BitVec::new(); - let __deku_output = &mut __deku_acc; - - #magic_write - - match self { - #(#variant_writes),* - } - - Ok(__deku_acc) - }, - &input.ctx, - &input.ctx_default, - ); - tokens.extend(quote! { - impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { + impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { type Error = ::#crate_::DekuError; fn try_from(input: #ident) -> core::result::Result { @@ -302,13 +276,22 @@ fn emit_enum(input: &DekuData) -> Result { impl #imp DekuContainerWrite for #ident #wher { fn to_bytes(&self) -> core::result::Result, ::#crate_::DekuError> { - let mut acc: ::#crate_::bitvec::BitVec = self.to_bits()?; - Ok(acc.into_vec()) + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + __deku_writer.finalize()?; + Ok(out_buf) } #[allow(unused_variables)] fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - #to_bits_body + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + let mut leftover = __deku_writer.leftover; + let mut bv = ::#crate_::bitvec::BitVec::from_slice(&out_buf); + bv.append(&mut leftover); + Ok(bv) } } }) @@ -342,9 +325,9 @@ fn emit_enum(input: &DekuData) -> Result { } } - impl #imp DekuWrite<#ctx_types> for #ident #wher { + impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher { #[allow(unused_variables)] - fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> { + fn to_writer(&self, __deku_writer: &mut ::#crate_::writer::Writer, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> { #write_body } } @@ -354,9 +337,9 @@ fn emit_enum(input: &DekuData) -> Result { let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default); tokens.extend(quote! { - impl #imp DekuWrite for #ident #wher { + impl #imp ::#crate_::DekuWriter for #ident #wher { #[allow(unused_variables)] - fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec, _: ()) -> core::result::Result<(), ::#crate_::DekuError> { + fn to_writer(&self, __deku_writer: &mut ::#crate_::writer::Writer, _: ()) -> core::result::Result<(), ::#crate_::DekuError> { #write_body } } @@ -368,9 +351,10 @@ fn emit_enum(input: &DekuData) -> Result { } fn emit_magic_write(input: &DekuData) -> TokenStream { + let crate_ = super::get_crate_name(); if let Some(magic) = &input.magic { quote! { - #magic.write(__deku_output, ())?; + ::#crate_::DekuWriter::to_writer(#magic, __deku_writer, ())?; } } else { quote! {} @@ -432,7 +416,7 @@ fn emit_bit_byte_offsets( .any(|v| token_contains_string(v, "__deku_byte_offset")) { Some(quote! { - let __deku_byte_offset = __deku_bit_offset / 8; + let __deku_byte_offset = __deku_writer.bits_written / 8; }) } else { None @@ -444,7 +428,7 @@ fn emit_bit_byte_offsets( || byte_offset.is_some() { Some(quote! { - let __deku_bit_offset = __deku_output.len(); + let __deku_bit_offset = __deku_writer.bits_written; }) } else { None @@ -464,8 +448,7 @@ fn emit_padding(bit_size: &TokenStream) -> TokenStream { stringify!(#bit_size) )) )?; - let new_len = __deku_output.len() + __deku_pad; - __deku_output.resize(new_len, false); + __deku_writer.write_bits(::#crate_::bitvec::bitvec![u8, ::#crate_::bitvec::Msb0; 0; __deku_pad].as_bitslice())?; } } } @@ -528,6 +511,14 @@ fn emit_field_write( } }); + let trace_field_log = if cfg!(feature = "logging") { + quote! { + log::trace!("Writing: {}.{}", #ident, #field_ident_str); + } + } else { + quote! {} + }; + let field_write_func = if field_writer.is_some() { quote! { #field_writer } } else { @@ -543,13 +534,13 @@ fn emit_field_write( let field_type = &f.ty; quote! { let #field_ident: #field_type = #temp_value; - ::#crate_::DekuWrite::write(#object_prefix &#field_ident, __deku_output, (#write_args)) + ::#crate_::DekuWriter::to_writer(#object_prefix &#field_ident, __deku_writer, (#write_args)) } } else { quote! { core::result::Result::<(), ::#crate_::DekuError>::Ok(()) } } } else { - quote! { ::#crate_::DekuWrite::write(#object_prefix #field_ident, __deku_output, (#write_args)) } + quote! { ::#crate_::DekuWriter::to_writer(#object_prefix #field_ident, __deku_writer, (#write_args)) } } }; @@ -608,6 +599,7 @@ fn emit_field_write( #bit_offset #byte_offset + #trace_field_log #field_assert #field_assert_eq diff --git a/examples/custom_reader_and_writer.rs b/examples/custom_reader_and_writer.rs index 908696c4..10a55114 100644 --- a/examples/custom_reader_and_writer.rs +++ b/examples/custom_reader_and_writer.rs @@ -1,8 +1,9 @@ use std::convert::TryInto; -use deku::bitvec::{BitVec, Msb0}; use deku::ctx::BitSize; -use deku::prelude::*; +use deku::writer::Writer; +use deku::{prelude::*, DekuWriter}; +use no_std_io::io::Write; fn bit_flipper_read( field_a: u8, @@ -24,10 +25,10 @@ fn bit_flipper_read( Ok(value) } -fn bit_flipper_write( +fn bit_flipper_write( field_a: u8, field_b: u8, - output: &mut BitVec, + writer: &mut Writer, bit_size: BitSize, ) -> Result<(), DekuError> { // Access to previously written fields @@ -42,7 +43,7 @@ fn bit_flipper_write( // flip the bits on value if field_a is 0x01 let value = if field_a == 0x01 { !field_b } else { field_b }; - value.write(output, bit_size) + value.to_writer(writer, bit_size) } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] @@ -51,7 +52,7 @@ struct DekuTest { #[deku( reader = "bit_flipper_read(*field_a, deku::reader, BitSize(8))", - writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))" + writer = "bit_flipper_write(*field_a, *field_b, deku::writer, BitSize(8))" )] field_b: u8, } diff --git a/src/attributes.rs b/src/attributes.rs index bd90cddd..50525423 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -803,7 +803,7 @@ use deku::prelude::*; struct DekuTest { #[deku( reader = "DekuTest::read(deku::reader)", - writer = "DekuTest::write(deku::output, &self.field_a)" + writer = "DekuTest::write(deku::writer, &self.field_a)" )] field_a: String, } @@ -818,9 +818,9 @@ impl DekuTest { } /// Parse from String to u8 and write - fn write(output: &mut BitVec, field_a: &str) -> Result<(), DekuError> { + fn write(writer: &mut Writer, field_a: &str) -> Result<(), DekuError> { let value = field_a.parse::().unwrap(); - value.write(output, ()) + value.to_writer(writer, ()) } } diff --git a/src/error.rs b/src/error.rs index 061f58dc..f39e1a20 100644 --- a/src/error.rs +++ b/src/error.rs @@ -44,6 +44,8 @@ pub enum DekuError { Assertion(String), /// Could not resolve `id` for variant IdVariantNotFound, + /// IO error while writing + Write, } impl From for DekuError { @@ -78,6 +80,7 @@ impl core::fmt::Display for DekuError { DekuError::Unexpected(ref err) => write!(f, "Unexpected error: {err}"), DekuError::Assertion(ref err) => write!(f, "Assertion error: {err}"), DekuError::IdVariantNotFound => write!(f, "Could not resolve `id` for variant"), + DekuError::Write => write!(f, "write error"), } } } @@ -100,6 +103,7 @@ impl From for std::io::Error { DekuError::Unexpected(_) => io::Error::new(io::ErrorKind::Other, error), DekuError::Assertion(_) => io::Error::new(io::ErrorKind::InvalidData, error), DekuError::IdVariantNotFound => io::Error::new(io::ErrorKind::NotFound, error), + DekuError::Write => io::Error::new(io::ErrorKind::BrokenPipe, error), } } } diff --git a/src/impls/bool.rs b/src/impls/bool.rs index 71e85a16..eac90ea5 100644 --- a/src/impls/bool.rs +++ b/src/impls/bool.rs @@ -1,11 +1,11 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; #[cfg(feature = "alloc")] use alloc::format; -use bitvec::prelude::*; - -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, Ctx> DekuReader<'a, Ctx> for bool where @@ -13,7 +13,7 @@ where u8: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let val = u8::from_reader_with_ctx(reader, inner_ctx)?; @@ -28,15 +28,15 @@ where } } -impl DekuWrite for bool +impl DekuWriter for bool where - u8: DekuWrite, + u8: DekuWriter, { /// wrapper around u8::write with consideration to context, such as bit size - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { match self { - true => (0x01u8).write(output, inner_ctx), - false => (0x00u8).write(output, inner_ctx), + true => (0x01u8).to_writer(writer, inner_ctx), + false => (0x00u8).to_writer(writer, inner_ctx), } } } @@ -47,7 +47,7 @@ mod tests { use no_std_io::io::Cursor; use rstest::rstest; - use crate::reader::Reader; + use crate::{ctx::BitSize, reader::Reader}; use super::*; @@ -72,9 +72,23 @@ mod tests { let mut reader = Reader::new(&mut cursor); let res_read = bool::from_reader_with_ctx(&mut reader, crate::ctx::BitSize(2)).unwrap(); assert!(res_read); + } + + #[test] + fn test_writer() { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + true.to_writer(&mut writer, BitSize(1)).unwrap(); + assert_eq!(vec![true], writer.rest()); + + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + true.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![1], out_buf); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(vec![0b01], res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + false.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![0], out_buf); } } diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs index bd733efa..565dc8a8 100644 --- a/src/impls/boxed.rs +++ b/src/impls/boxed.rs @@ -1,12 +1,12 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use alloc::boxed::Box; use alloc::vec::Vec; -use bitvec::prelude::*; - use crate::ctx::Limit; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, T, Ctx> DekuReader<'a, Ctx> for Box where @@ -14,7 +14,7 @@ where Ctx: Copy, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let val = ::from_reader_with_ctx(reader, inner_ctx)?; @@ -22,17 +22,6 @@ where } } -impl DekuWrite for Box -where - T: DekuWrite, - Ctx: Copy, -{ - /// Write T from box - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { - self.as_ref().write(output, inner_ctx) - } -} - impl<'a, T, Ctx, Predicate> DekuReader<'a, (Limit, Ctx)> for Box<[T]> where T: DekuReader<'a, Ctx>, @@ -40,7 +29,7 @@ where Predicate: FnMut(&T) -> bool, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, (limit, inner_ctx): (Limit, Ctx), ) -> Result { // use Vec's implementation and convert to Box<[T]> @@ -49,15 +38,15 @@ where } } -impl DekuWrite for Box<[T]> +impl DekuWriter for Box<[T]> where - T: DekuWrite, + T: DekuWriter, Ctx: Copy, { /// Write all `T`s to bits - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { for v in self.as_ref() { - v.write(output, ctx)?; + v.to_writer(writer, ctx)?; } Ok(()) } @@ -72,6 +61,7 @@ mod tests { use crate::ctx::*; use crate::native_endian; use crate::reader::Reader; + use bitvec::prelude::*; #[rstest(input, expected, case( @@ -85,9 +75,10 @@ mod tests { let res_read = >::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } // Note: Copied tests from vec.rs impl @@ -105,7 +96,7 @@ mod tests { bit_size: Option, limit: Limit, expected: Box<[u16]>, - expected_rest_bits: &BitSlice, + expected_rest_bits: &bitvec::slice::BitSlice, expected_rest_bytes: &[u8], expected_write: Vec, ) { @@ -126,11 +117,14 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + assert_eq!(input[..expected_write.len()].to_vec(), expected_write); + + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + assert_eq!(expected_write, out_buf.to_vec()); assert_eq!(input[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/impls/cow.rs b/src/impls/cow.rs index d7a4ff56..078bf4da 100644 --- a/src/impls/cow.rs +++ b/src/impls/cow.rs @@ -1,10 +1,10 @@ use std::borrow::{Borrow, Cow}; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use bitvec::prelude::*; - -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, T, Ctx> DekuReader<'a, Ctx> for Cow<'a, T> where @@ -12,7 +12,7 @@ where Ctx: Copy, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let val = ::from_reader_with_ctx(reader, inner_ctx)?; @@ -20,14 +20,14 @@ where } } -impl DekuWrite for Cow<'_, T> +impl DekuWriter for Cow<'_, T> where - T: DekuWrite + Clone, + T: DekuWriter + Clone, Ctx: Copy, { /// Write T from Cow - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { - (self.borrow() as &T).write(output, inner_ctx) + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { + (self.borrow() as &T).to_writer(writer, inner_ctx) } } @@ -51,8 +51,9 @@ mod tests { let res_read = >::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } } diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs index c95e41ff..34c46337 100644 --- a/src/impls/cstring.rs +++ b/src/impls/cstring.rs @@ -1,18 +1,18 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use std::ffi::CString; -use bitvec::prelude::*; - +use crate::reader::Reader; +use crate::writer::Writer; use crate::{ctx::*, DekuReader}; -use crate::{DekuError, DekuWrite}; +use crate::{DekuError, DekuWriter}; -impl DekuWrite for CString +impl DekuWriter for CString where - u8: DekuWrite, + u8: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let bytes = self.as_bytes_with_nul(); - bytes.write(output, ctx) + bytes.to_writer(writer, ctx) } } @@ -21,7 +21,7 @@ where u8: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let bytes = @@ -67,8 +67,9 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest, buf); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(vec![b't', b'e', b's', b't', b'\0'], res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![b't', b'e', b's', b't', b'\0'], out_buf.to_vec()); } } diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs index b51ea801..7762a5a4 100644 --- a/src/impls/hashmap.rs +++ b/src/impls/hashmap.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; -use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use crate::ctx::*; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; /// Read `K, V`s into a hashmap until a given predicate returns true /// * `capacity` - an optional capacity to pre-allocate the hashmap with @@ -159,7 +159,7 @@ where } } -impl, V: DekuWrite, S, Ctx: Copy> DekuWrite for HashMap { +impl, V: DekuWriter, S, Ctx: Copy> DekuWriter for HashMap { /// Write all `K, V`s in a `HashMap` to bits. /// * **inner_ctx** - The context required by `K, V`. /// Note: depending on the Hasher `S`, the order in which the `K, V` pairs are @@ -167,19 +167,21 @@ impl, V: DekuWrite, S, Ctx: Copy> DekuWrite for Hash /// instead of the default RandomState hasher if you don't want the order written to change. /// # Examples /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; + /// # use deku::{ctx::Endian, DekuWriter}; + /// # use deku::writer::Writer; /// # use deku::bitvec::{Msb0, bitvec}; /// # use std::collections::HashMap; - /// let mut output = bitvec![u8, Msb0;]; + /// let mut out_buf = vec![]; + /// let mut writer = Writer::new(&mut out_buf); /// let mut map = HashMap::::default(); /// map.insert(100, 0x04030201); - /// map.write(&mut output, Endian::Big).unwrap(); + /// map.to_writer(&mut writer, Endian::Big).unwrap(); /// let expected: Vec = vec![100, 4, 3, 2, 1]; - /// assert_eq!(expected, output.into_vec()) + /// assert_eq!(expected, out_buf); /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { for kv in self { - kv.write(output, inner_ctx)?; + kv.to_writer(writer, inner_ctx)?; } Ok(()) } @@ -194,6 +196,7 @@ mod tests { use crate::reader::Reader; use super::*; + use bitvec::prelude::*; // Macro to create a deterministic HashMap for tests // This is needed for tests since the default HashMap Hasher @@ -217,7 +220,11 @@ mod tests { case::count_1([0x01, 0xAA, 0x02, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0x02, 0xbb]), case::count_2([0x01, 0xAA, 0x02, 0xBB, 0xBB].as_ref(), Endian::Little, Some(8), 2.into(), fxhashmap!{0x01 => 0xAA, 0x02 => 0xBB}, bits![u8, Msb0;], &[0xbb]), case::until_null([0x01, 0xAA, 0, 0, 0xBB].as_ref(), Endian::Little, None, (|kv: &(u8, u8)| kv.0 == 0u8 && kv.1 == 0u8).into(), fxhashmap!{0x01 => 0xAA, 0 => 0}, bits![u8, Msb0;], &[0xbb]), + case::until_empty_bits([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(0).into(), FxHashMap::default(), bits![u8, Msb0;], &[0x01, 0xaa, 0xbb]), + case::until_empty_bytes([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(0).into(), FxHashMap::default(), bits![u8, Msb0;], &[0x01, 0xaa, 0xbb]), case::until_bits([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(16).into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]), + case::until_bytes([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(2).into(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]), + case::until_count([0x01, 0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::from(1), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[0xbb]), case::bits_6([0b0000_0100, 0b1111_0000, 0b1000_0000].as_ref(), Endian::Little, Some(6), 2.into(), fxhashmap!{0x01 => 0x0F, 0x02 => 0}, bits![u8, Msb0;], &[]), #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")] case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), FxHashMap::default(), bits![u8, Msb0;], &[]), @@ -267,44 +274,9 @@ mod tests { case::normal(fxhashmap!{0x11u8 => 0xAABBu16, 0x23u8 => 0xCCDDu16}, Endian::Little, vec![0x11, 0xBB, 0xAA, 0x23, 0xDD, 0xCC]), )] fn test_hashmap_write(input: FxHashMap, endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); - } - - // Note: These tests also exist in boxed.rs - #[rstest(input, endian, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write, - case::normal_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, 2.into(), fxhashmap!{0xBBAA => 0, 0xDDCC => 0}, bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0, 0xAA, 0xBB, 0]), - case::normal_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, 2.into(), fxhashmap!{0xAABB => 0, 0xCCDD => 0}, bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0, 0xAA, 0xBB, 0]), - case::predicate_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, (|kv: &(u16, u8)| kv.0 == 0xBBAA && kv.1 == 0).into(), fxhashmap!{0xBBAA => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - case::predicate_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, (|kv: &(u16, u8)| kv.0 == 0xAABB && kv.1 == 0).into(), fxhashmap!{0xAABB => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - case::bytes_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, BitSize(24).into(), fxhashmap!{0xBBAA => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - case::bytes_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, BitSize(24).into(), fxhashmap!{0xAABB => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - )] - fn test_hashmap_read_write bool + Copy>( - input: &[u8], - endian: Endian, - limit: Limit<(u16, u8), Predicate>, - expected: FxHashMap, - expected_rest_bits: &BitSlice, - expected_rest_bytes: &[u8], - expected_write: Vec, - ) { - let mut cursor = Cursor::new(input); - let mut reader = Reader::new(&mut cursor); - let res_read = - FxHashMap::::from_reader_with_ctx(&mut reader, (limit, endian)).unwrap(); - assert_eq!(expected, res_read); - assert_eq!( - reader.rest(), - expected_rest_bits.iter().by_vals().collect::>() - ); - let mut buf = vec![]; - cursor.read_to_end(&mut buf).unwrap(); - assert_eq!(expected_rest_bytes, buf); - - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, endian).unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf); } } diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs index 16c4dea4..f391429f 100644 --- a/src/impls/hashset.rs +++ b/src/impls/hashset.rs @@ -1,11 +1,11 @@ use std::collections::HashSet; use std::hash::{BuildHasher, Hash}; -use bitvec::prelude::*; -use no_std_io::io::Read; +use crate::writer::Writer; +use no_std_io::io::{Read, Write}; use crate::ctx::*; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::{DekuError, DekuReader, DekuWriter}; /// Read `T`s into a hashset until a given predicate returns true /// * `capacity` - an optional capacity to pre-allocate the hashset with @@ -150,7 +150,7 @@ impl<'a, T: DekuReader<'a> + Eq + Hash, S: BuildHasher + Default, Predicate: FnM } } -impl, S, Ctx: Copy> DekuWrite for HashSet { +impl, S, Ctx: Copy> DekuWriter for HashSet { /// Write all `T`s in a `HashSet` to bits. /// * **inner_ctx** - The context required by `T`. /// Note: depending on the Hasher `S`, the order in which the `T`'s are @@ -158,17 +158,19 @@ impl, S, Ctx: Copy> DekuWrite for HashSet { /// instead of the default RandomState hasher if you don't want the order written to change. /// # Examples /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; + /// # use deku::{ctx::Endian, DekuWriter}; + /// # use deku::writer::Writer; /// # use deku::bitvec::{Msb0, bitvec}; /// # use std::collections::HashSet; + /// let mut out_buf = vec![]; + /// let mut writer = Writer::new(&mut out_buf); /// let set: HashSet = vec![1].into_iter().collect(); - /// let mut output = bitvec![u8, Msb0;]; - /// set.write(&mut output, Endian::Big).unwrap(); - /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1]) + /// set.to_writer(&mut writer, Endian::Big).unwrap(); + /// assert_eq!(out_buf, vec![1]); /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { for v in self { - v.write(output, inner_ctx)?; + v.to_writer(writer, inner_ctx)?; } Ok(()) } @@ -176,6 +178,7 @@ impl, S, Ctx: Copy> DekuWrite for HashSet { #[cfg(test)] mod tests { + use crate::bitvec::{bits, BitSlice, Msb0}; use no_std_io::io::Cursor; use rstest::rstest; use rustc_hash::FxHashSet; @@ -189,7 +192,11 @@ mod tests { case::count_1([0xAA, 0xBB].as_ref(), Endian::Little, Some(8), 1.into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]), case::count_2([0xAA, 0xBB, 0xCC].as_ref(), Endian::Little, Some(8), 2.into(), vec![0xAA, 0xBB].into_iter().collect(), bits![u8, Msb0;], &[0xcc]), case::until_null([0xAA, 0, 0xBB].as_ref(), Endian::Little, None, (|v: &u8| *v == 0u8).into(), vec![0xAA, 0].into_iter().collect(), bits![u8, Msb0;], &[0xbb]), + case::until_empty_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(0).into(), HashSet::default(), bits![u8, Msb0;], &[0xaa, 0xbb]), + case::until_empty_bytes([0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(0).into(), HashSet::default(), bits![u8, Msb0;], &[0xaa, 0xbb]), case::until_bits([0xAA, 0xBB].as_ref(), Endian::Little, None, BitSize(8).into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]), + case::until_bytes([0xAA, 0xBB].as_ref(), Endian::Little, None, ByteSize(1).into(), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]), + case::until_count([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::from(1), vec![0xAA].into_iter().collect(), bits![u8, Msb0;], &[0xbb]), case::bits_6([0b0110_1001, 0b1110_1001].as_ref(), Endian::Little, Some(6), 2.into(), vec![0b00_011010, 0b00_011110].into_iter().collect(), bits![u8, Msb0; 1, 0, 0, 1], &[]), #[should_panic(expected = "Parse(\"too much data: container of 8 bits cannot hold 9 bits\")")] case::not_enough_data([].as_ref(), Endian::Little, Some(9), 1.into(), FxHashSet::default(), bits![u8, Msb0;], &[]), @@ -237,9 +244,10 @@ mod tests { case::normal(vec![0xAABB, 0xCCDD].into_iter().collect(), Endian::Little, vec![0xDD, 0xCC, 0xBB, 0xAA]), )] fn test_hashset_write(input: FxHashSet, endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf); } // Note: These tests also exist in boxed.rs @@ -280,10 +288,11 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + assert_eq!(expected_write, out_buf); } } diff --git a/src/impls/ipaddr.rs b/src/impls/ipaddr.rs index 9ad5bf2c..f3886fdb 100644 --- a/src/impls/ipaddr.rs +++ b/src/impls/ipaddr.rs @@ -1,17 +1,17 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use bitvec::prelude::*; - -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, Ctx> DekuReader<'a, Ctx> for Ipv4Addr where u32: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let ip = u32::from_reader_with_ctx(reader, inner_ctx)?; @@ -19,13 +19,13 @@ where } } -impl DekuWrite for Ipv4Addr +impl DekuWriter for Ipv4Addr where - u32: DekuWrite, + u32: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let ip: u32 = (*self).into(); - ip.write(output, ctx) + ip.to_writer(writer, ctx) } } @@ -34,7 +34,7 @@ where u128: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let ip = u128::from_reader_with_ctx(reader, inner_ctx)?; @@ -42,25 +42,25 @@ where } } -impl DekuWrite for Ipv6Addr +impl DekuWriter for Ipv6Addr where - u128: DekuWrite, + u128: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let ip: u128 = (*self).into(); - ip.write(output, ctx) + ip.to_writer(writer, ctx) } } -impl DekuWrite for IpAddr +impl DekuWriter for IpAddr where - Ipv6Addr: DekuWrite, - Ipv4Addr: DekuWrite, + Ipv6Addr: DekuWriter, + Ipv4Addr: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { match self { - IpAddr::V4(ipv4) => ipv4.write(output, ctx), - IpAddr::V6(ipv6) => ipv6.write(output, ctx), + IpAddr::V4(ipv4) => ipv4.to_writer(writer, ctx), + IpAddr::V6(ipv6) => ipv6.to_writer(writer, ctx), } } } @@ -83,9 +83,10 @@ mod tests { let res_read = Ipv4Addr::from_reader_with_ctx(&mut reader, endian).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, endian).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, endian).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } #[rstest(input, endian, expected, @@ -98,27 +99,31 @@ mod tests { let res_read = Ipv6Addr::from_reader_with_ctx(&mut reader, endian).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, endian).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, endian).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } #[test] fn test_ip_addr_write() { let ip_addr = IpAddr::V4(Ipv4Addr::new(145, 254, 160, 237)); - let mut ret_write = bitvec![u8, Msb0;]; - ip_addr.write(&mut ret_write, Endian::Little).unwrap(); - assert_eq!(vec![237, 160, 254, 145], ret_write.into_vec()); + + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); + assert_eq!(vec![237, 160, 254, 145], out_buf.to_vec()); let ip_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x02ff)); - let mut ret_write = bitvec![u8, Msb0;]; - ip_addr.write(&mut ret_write, Endian::Little).unwrap(); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); assert_eq!( vec![ 0xff, 0x02, 0x0a, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - ret_write.into_vec() + out_buf.to_vec() ); } } diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index d85b245b..5ea5b557 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -1,18 +1,18 @@ #[cfg(feature = "alloc")] use alloc::format; use core::num::*; -use no_std_io::io::Read; - -use bitvec::prelude::*; +use no_std_io::io::{Read, Write}; use crate::ctx::*; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; macro_rules! ImplDekuTraitsCtx { ($typ:ty, $readtype:ty, $ctx_arg:tt, $ctx_type:tt) => { impl DekuReader<'_, $ctx_type> for $typ { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, $ctx_arg: $ctx_type, ) -> Result { let value = <$readtype>::from_reader_with_ctx(reader, $ctx_arg)?; @@ -25,14 +25,14 @@ macro_rules! ImplDekuTraitsCtx { } } - impl DekuWrite<$ctx_type> for $typ { - fn write( + impl DekuWriter<$ctx_type> for $typ { + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, $ctx_arg: $ctx_type, ) -> Result<(), DekuError> { let value = self.get(); - value.write(output, $ctx_arg) + value.to_writer(writer, $ctx_arg) } } }; @@ -68,6 +68,7 @@ mod tests { use crate::reader::Reader; use super::*; + use bitvec::prelude::*; #[rstest(input, expected, case(&hex!("FF"), NonZeroU8::new(0xFF).unwrap()), @@ -82,8 +83,9 @@ mod tests { let res_read = NonZeroU8::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } } diff --git a/src/impls/option.rs b/src/impls/option.rs index 3eabefac..59dfd25a 100644 --- a/src/impls/option.rs +++ b/src/impls/option.rs @@ -1,7 +1,6 @@ -use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::{writer::Writer, DekuError, DekuReader, DekuWriter}; impl<'a, T: DekuReader<'a, Ctx>, Ctx: Copy> DekuReader<'a, Ctx> for Option { fn from_reader_with_ctx( @@ -13,20 +12,10 @@ impl<'a, T: DekuReader<'a, Ctx>, Ctx: Copy> DekuReader<'a, Ctx> for Option { } } -impl, Ctx: Copy> DekuWrite for Option { - /// Write T if Some - /// * **inner_ctx** - The context required by `T`. - /// # Examples - /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; - /// # use deku::bitvec::{bitvec, Msb0}; - /// let data = Some(1u8); - /// let mut output = bitvec![u8, Msb0;]; - /// data.write(&mut output, Endian::Big).unwrap(); - /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1]) - /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { - self.as_ref().map_or(Ok(()), |v| v.write(output, inner_ctx)) +impl, Ctx: Copy> DekuWriter for Option { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { + self.as_ref() + .map_or(Ok(()), |v| v.to_writer(writer, inner_ctx)) } } diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index 63aeeb09..09114ac6 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -5,11 +5,12 @@ use alloc::string::ToString; use core::convert::TryInto; use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use crate::ctx::*; use crate::reader::{Reader, ReaderRet}; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; /// "Read" trait: read bits and construct type trait DekuRead<'a, Ctx = ()> { @@ -31,6 +32,32 @@ trait DekuRead<'a, Ctx = ()> { Self: Sized; } +/// "Writer" trait: write from type to bits +trait DekuWrite { + /// Write type to bits + /// * **output** - Sink to store resulting bits + /// * **ctx** - A context required by context-sensitive reading. A unit type `()` means no context + /// needed. + fn write( + &self, + output: &mut crate::bitvec::BitVec, + ctx: Ctx, + ) -> Result<(), DekuError>; +} + +/// Implements DekuWrite for references of types that implement DekuWrite +impl DekuWrite for &T +where + T: DekuWrite, + Ctx: Copy, +{ + /// Write value of type to bits + fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + ::write(self, output, ctx)?; + Ok(()) + } +} + // specialize u8 for ByteSize impl DekuRead<'_, (Endian, ByteSize)> for u8 { #[inline] @@ -392,11 +419,11 @@ macro_rules! ForwardDekuRead { macro_rules! ImplDekuWrite { ($typ:ty) => { - impl DekuWrite<(Endian, BitSize)> for $typ { + impl DekuWriter<(Endian, BitSize)> for $typ { #[inline] - fn write( + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, (endian, size): (Endian, BitSize), ) -> Result<(), DekuError> { let input = match endian { @@ -422,27 +449,27 @@ macro_rules! ImplDekuWrite { let mut remaining_bits = bit_size; for chunk in input_bits.chunks(8) { if chunk.len() > remaining_bits { - output.extend_from_bitslice(&chunk[chunk.len() - remaining_bits..]); + writer.write_bits(&chunk[chunk.len() - remaining_bits..])?; break; } else { - output.extend_from_bitslice(chunk) + writer.write_bits(&chunk)?; } remaining_bits -= chunk.len(); } } else { // Example read 10 bits u32 [0xAB, 0b11_000000] // => [00000000, 00000000, 00000010, 10101111] - output.extend_from_bitslice(&input_bits[input_bits.len() - bit_size..]); + writer.write_bits(&input_bits[input_bits.len() - bit_size..])?; } Ok(()) } } - impl DekuWrite<(Endian, ByteSize)> for $typ { + impl DekuWriter<(Endian, ByteSize)> for $typ { #[inline] - fn write( + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, (endian, size): (Endian, ByteSize), ) -> Result<(), DekuError> { let input = match endian { @@ -450,53 +477,37 @@ macro_rules! ImplDekuWrite { Endian::Big => self.to_be_bytes(), }; - let bit_size: usize = size.0 * 8; - - let input_bits = input.view_bits::(); - - if bit_size > input_bits.len() { + const TYPE_SIZE: usize = core::mem::size_of::<$typ>(); + if size.0 > TYPE_SIZE { return Err(DekuError::InvalidParam(format!( - "bit size {} is larger than input {}", - bit_size, - input_bits.len() + "byte size {} is larger then input {}", + size.0, TYPE_SIZE ))); } - if matches!(endian, Endian::Little) { - // Example read 10 bits u32 [0xAB, 0b11_000000] - // => [10101011, 00000011, 00000000, 00000000] - let mut remaining_bits = bit_size; - for chunk in input_bits.chunks(8) { - if chunk.len() > remaining_bits { - output.extend_from_bitslice(&chunk[chunk.len() - remaining_bits..]); - break; - } else { - output.extend_from_bitslice(chunk) - } - remaining_bits -= chunk.len(); - } + let input = if matches!(endian, Endian::Big) { + &input[TYPE_SIZE - size.0 as usize..] } else { - // Example read 10 bits u32 [0xAB, 0b11_000000] - // => [00000000, 00000000, 00000010, 10101111] - output.extend_from_bitslice(&input_bits[input_bits.len() - bit_size..]); - } + &input[..size.0 as usize] + }; + + writer.write_bytes(&input)?; Ok(()) } } - // Only have `endian`, return all input - impl DekuWrite for $typ { - #[inline] - fn write( + impl DekuWriter for $typ { + #[inline(always)] + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, endian: Endian, ) -> Result<(), DekuError> { let input = match endian { Endian::Little => self.to_le_bytes(), Endian::Big => self.to_be_bytes(), }; - output.extend_from_bitslice(input.view_bits::()); + writer.write_bytes(&input)?; Ok(()) } } @@ -505,34 +516,32 @@ macro_rules! ImplDekuWrite { macro_rules! ForwardDekuWrite { ($typ:ty) => { - // Only have `bit_size`, set `endian` to `Endian::default`. - impl DekuWrite for $typ { + impl DekuWriter for $typ { #[inline] - fn write( + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, bit_size: BitSize, ) -> Result<(), DekuError> { - <$typ>::write(self, output, (Endian::default(), bit_size)) + <$typ>::to_writer(self, writer, (Endian::default(), bit_size)) } } - // Only have `bit_size`, set `endian` to `Endian::default`. - impl DekuWrite for $typ { + impl DekuWriter for $typ { #[inline] - fn write( + fn to_writer( &self, - output: &mut BitVec, - bit_size: ByteSize, + writer: &mut Writer, + byte_size: ByteSize, ) -> Result<(), DekuError> { - <$typ>::write(self, output, (Endian::default(), bit_size)) + <$typ>::to_writer(self, writer, (Endian::default(), byte_size)) } } - impl DekuWrite for $typ { + impl DekuWriter for $typ { #[inline] - fn write(&self, output: &mut BitVec, _: ()) -> Result<(), DekuError> { - <$typ>::write(self, output, Endian::default()) + fn to_writer(&self, writer: &mut Writer, _: ()) -> Result<(), DekuError> { + <$typ>::to_writer(self, writer, Endian::default()) } } }; @@ -615,9 +624,10 @@ mod tests { let res_read = <$typ>::from_reader_with_ctx(&mut reader, ENDIAN).unwrap(); assert_eq!($expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ENDIAN).unwrap(); - assert_eq!($input, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ENDIAN).unwrap(); + assert_eq!($input, out_buf); } }; } @@ -791,23 +801,51 @@ mod tests { assert_eq!(expected_rest_bytes, buf); } - #[rstest(input, endian, bit_size, expected, - case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD]), - case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA]), - case::bit_size_le_smaller(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]), - case::bit_size_be_smaller(0x03AB, Endian::Big, Some(10), vec![0b11_1010_10, 0b11_000000]), + #[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![]), + case::bit_size_le_smaller(0x03AB, Endian::Little, Some(10), vec![0xAB], vec![true, true]), + case::bit_size_be_smaller(0x03AB, Endian::Big, Some(10), vec![0b11_1010_10], vec![true, true]), #[should_panic(expected = "InvalidParam(\"bit size 100 is larger than input 32\")")] - case::bit_size_le_bigger(0x03AB, Endian::Little, Some(100), vec![0xAB, 0b11_000000]), + case::bit_size_le_bigger(0x03AB, Endian::Little, Some(100), vec![0xAB, 0b11_000000], vec![true, true]), )] - fn test_bit_write(input: u32, endian: Endian, bit_size: Option, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; + fn test_bit_writer( + input: u32, + endian: Endian, + bit_size: Option, + expected: Vec, + expected_leftover: Vec, + ) { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); match bit_size { Some(bit_size) => input - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(), - None => input.write(&mut res_write, endian).unwrap(), + None => input.to_writer(&mut writer, endian).unwrap(), }; - assert_eq!(expected, res_write.into_vec()); + assert_eq!(expected_leftover, writer.rest()); + assert_eq!(expected, out_buf); + } + + #[rstest(input, endian, byte_size, expected, + case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD]), + case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA]), + case::byte_size_le_smaller(0x00ffABAA, Endian::Little, Some(2), vec![0xaa, 0xab]), + case::byte_size_be_smaller(0x00ffABAA, Endian::Big, Some(2), vec![0xab, 0xaa]), + #[should_panic(expected = "InvalidParam(\"byte size 10 is larger then input 4\")")] + case::byte_size_le_bigger(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]), + )] + fn test_byte_writer(input: u32, endian: Endian, byte_size: Option, expected: Vec) { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + match byte_size { + Some(byte_size) => input + .to_writer(&mut writer, (endian, ByteSize(byte_size))) + .unwrap(), + None => input.to_writer(&mut writer, endian).unwrap(), + }; + assert_hex::assert_eq_hex!(expected, out_buf); } #[rstest(input, endian, bit_size, expected, expected_write, @@ -831,15 +869,15 @@ mod tests { }; assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); match bit_size { Some(bit_size) => res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(), - None => res_read.write(&mut res_write, endian).unwrap(), + None => res_read.to_writer(&mut writer, endian).unwrap(), }; - - assert_eq!(expected_write, res_write.into_vec()); + assert_hex::assert_eq_hex!(expected_write, out_buf); } macro_rules! TestSignExtending { diff --git a/src/impls/slice.rs b/src/impls/slice.rs index a6b60408..9d38fb52 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,20 +1,18 @@ //! Implementations of DekuRead and DekuWrite for [T; N] where 0 < N <= 32 -use crate::{DekuError, DekuWrite}; -use bitvec::prelude::*; -use core::mem::MaybeUninit; -use no_std_io::io::Read; +pub use deku_derive::*; -use crate::DekuReader; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; +use core::mem::MaybeUninit; +use no_std_io::io::{Read, Write}; impl<'a, Ctx: Copy, T, const N: usize> DekuReader<'a, Ctx> for [T; N] where T: DekuReader<'a, Ctx>, { - fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, - ctx: Ctx, - ) -> Result + fn from_reader_with_ctx(reader: &mut Reader, ctx: Ctx) -> Result where Self: Sized, { @@ -39,32 +37,32 @@ where } let val = unsafe { - // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/80908 + // TODO: array_assume_init: https://github.com/rust-lang/rust/issues/96097 (core::ptr::addr_of!(slice) as *const [T; N]).read() }; Ok(val) } } -impl DekuWrite for [T; N] +impl DekuWriter for [T; N] where - T: DekuWrite, + T: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { for v in self { - v.write(output, ctx)?; + v.to_writer(writer, ctx)?; } Ok(()) } } -impl DekuWrite for &[T] +impl DekuWriter for &[T] where - T: DekuWrite, + T: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { for v in *self { - v.write(output, ctx)?; + v.to_writer(writer, ctx)?; } Ok(()) } @@ -72,15 +70,17 @@ where #[cfg(test)] mod tests { - use crate::DekuWrite; + use super::*; use bitvec::prelude::*; use rstest::rstest; - use crate::{ctx::Endian, reader::Reader, DekuReader}; + use crate::{ctx::Endian, reader::Reader, writer::Writer, DekuReader}; #[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]), + #[should_panic(expected = "Incomplete(NeedSize { bits: 16 })")] + case::normal_be([0xDD, 0xCC].as_ref(), Endian::Big, [0xDDCC, 0xBBAA]), )] fn test_bit_read(input: &[u8], endian: Endian, expected: [u16; 2]) { let mut bit_slice = input.view_bits::(); @@ -95,72 +95,17 @@ mod tests { case::normal_be([0xDDCC, 0xBBAA], Endian::Big, vec![0xDD, 0xCC, 0xBB, 0xAA]), )] fn test_bit_write(input: [u16; 2], endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); - - // test &slice - let input = input.as_ref(); - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); - } - - #[cfg(feature = "const_generics")] - #[rstest(input,endian,expected,expected_rest, - case::normal_le( - [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(), - Endian::Little, - [[0xCCDD, 0xAABB], [0x8899, 0x6677]], - bits![u8, Msb0;], - ), - case::normal_le( - [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(), - Endian::Big, - [[0xDDCC, 0xBBAA], [0x9988, 0x7766]], - bits![u8, Msb0;], - ), - )] - fn test_nested_array_bit_read( - input: &[u8], - endian: Endian, - expected: [[u16; 2]; 2], - expected_rest: &BitSlice, - ) { - use no_std_io::io::Cursor; - - use crate::reader::Reader; - - let bit_slice = input.view_bits::(); - - let mut cursor = Cursor::new(input); - let mut reader = Reader::new(&mut cursor); - let res_read = <[[u16; 2]; 2]>::from_reader_with_ctx(&mut reader, endian).unwrap(); - assert_eq!(expected, res_read); - } - - #[cfg(feature = "const_generics")] - #[rstest(input,endian,expected, - case::normal_le( - [[0xCCDD, 0xAABB], [0x8899, 0x6677]], - Endian::Little, - vec![0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66], - ), - case::normal_be( - [[0xDDCC, 0xBBAA], [0x9988, 0x7766]], - Endian::Big, - vec![0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66], - ), - )] - fn test_nested_array_bit_write(input: [[u16; 2]; 2], endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + // test writer + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); // test &slice let input = input.as_ref(); - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); } } diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index d527d39f..fe228f81 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -1,9 +1,10 @@ //! Implementations of DekuRead and DekuWrite for tuples of length 1 to 11 -use bitvec::prelude::*; -use no_std_io::io::Read; +use crate::writer::Writer; -use crate::{DekuError, DekuReader, DekuWrite}; +use no_std_io::io::{Read, Write}; + +use crate::{DekuError, DekuReader, DekuWriter}; // Trait to help us build intermediate tuples while DekuRead'ing each element // from the tuple @@ -54,13 +55,13 @@ macro_rules! ImplDekuTupleTraits { } } - impl),+> DekuWrite for ($($T,)+) + impl),+> DekuWriter for ($($T,)+) { #[allow(non_snake_case)] - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let ($(ref $T,)+) = *self; $( - $T.write(output, ctx)?; + $T.to_writer(writer, ctx)?; )+ Ok(()) } @@ -94,10 +95,11 @@ mod tests { )] fn test_tuple_write(input: T, expected: Vec) where - T: DekuWrite, + T: DekuWriter, { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, ()).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, ()).unwrap(); + assert_eq!(expected, out_buf); } } diff --git a/src/impls/unit.rs b/src/impls/unit.rs index d5e3d895..b6b4ab3f 100644 --- a/src/impls/unit.rs +++ b/src/impls/unit.rs @@ -1,20 +1,23 @@ -use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::{reader::Reader, writer::Writer, DekuError, DekuReader, DekuWriter}; impl DekuReader<'_, Ctx> for () { fn from_reader_with_ctx( - _reader: &mut crate::reader::Reader, + _reader: &mut Reader, _inner_ctx: Ctx, ) -> Result { Ok(()) } } -impl DekuWrite for () { +impl DekuWriter for () { /// NOP on write - fn write(&self, _output: &mut BitVec, _inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer( + &self, + _writer: &mut Writer, + _inner_ctx: Ctx, + ) -> Result<(), DekuError> { Ok(()) } } @@ -37,8 +40,9 @@ mod tests { let res_read = <()>::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!((), res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(0, res_write.len()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(0, out_buf.len()); } } diff --git a/src/impls/vec.rs b/src/impls/vec.rs index c19abea3..5d930384 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -1,12 +1,12 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use bitvec::prelude::*; - +use crate::reader::Reader; +use crate::writer::Writer; use crate::{ctx::*, DekuReader}; -use crate::{DekuError, DekuWrite}; +use crate::{DekuError, DekuWriter}; /// Read `T`s into a vec until a given predicate returns true /// * `capacity` - an optional capacity to pre-allocate the vector with @@ -16,7 +16,7 @@ use crate::{DekuError, DekuWrite}; /// 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: Read>( - reader: &mut crate::reader::Reader, + reader: &mut Reader, capacity: Option, ctx: Ctx, mut predicate: Predicate, @@ -51,7 +51,7 @@ where Predicate: FnMut(&T) -> bool, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, (limit, inner_ctx): (Limit, Ctx), ) -> Result where @@ -113,7 +113,7 @@ impl<'a, T: DekuReader<'a>, Predicate: FnMut(&T) -> bool> DekuReader<'a, Limit( - reader: &mut crate::reader::Reader, + reader: &mut Reader, limit: Limit, ) -> Result where @@ -123,21 +123,23 @@ impl<'a, T: DekuReader<'a>, Predicate: FnMut(&T) -> bool> DekuReader<'a, Limit, Ctx: Copy> DekuWrite for Vec { +impl, Ctx: Copy> DekuWriter for Vec { /// Write all `T`s in a `Vec` to bits. /// * **inner_ctx** - The context required by `T`. /// # Examples /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; + /// # use deku::{ctx::Endian, DekuWriter}; + /// # use deku::writer::Writer; /// # use deku::bitvec::{Msb0, bitvec}; /// let data = vec![1u8]; - /// let mut output = bitvec![u8, Msb0;]; - /// data.write(&mut output, Endian::Big).unwrap(); - /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1]) + /// let mut out_buf = vec![]; + /// let mut writer = Writer::new(&mut out_buf); + /// data.to_writer(&mut writer, Endian::Big).unwrap(); + /// assert_eq!(data, out_buf.to_vec()); /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { for v in self { - v.write(output, inner_ctx)?; + v.to_writer(writer, inner_ctx)?; } Ok(()) } @@ -145,13 +147,40 @@ impl, Ctx: Copy> DekuWrite for Vec { #[cfg(test)] mod tests { + use crate::bitvec::{bits, BitSlice, Msb0}; use rstest::rstest; use crate::reader::Reader; use super::*; - #[rstest(input,endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, + #[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]), + case::count_2([0xAA, 0xBB, 0xCC].as_ref(), 2.into(), vec![0xAA, 0xBB], bits![u8, Msb0;], &[0xcc]), + case::until_null([0xAA, 0, 0xBB].as_ref(), (|v: &u8| *v == 0u8).into(), vec![0xAA, 0], bits![u8, Msb0;], &[0xbb]), + case::until_bits([0xAA, 0xBB].as_ref(), BitSize(8).into(), vec![0xAA], bits![u8, Msb0;], &[0xbb]), + )] + fn test_vec_reader_no_ctx bool>( + mut input: &[u8], + limit: Limit, + expected: Vec, + expected_rest_bits: &BitSlice, + expected_rest_bytes: &[u8], + ) { + let mut reader = Reader::new(&mut input); + let res_read = Vec::::from_reader_with_ctx(&mut reader, limit).unwrap(); + assert_eq!(expected, res_read); + assert_eq!( + reader.rest(), + expected_rest_bits.iter().by_vals().collect::>() + ); + let mut buf = vec![]; + input.read_to_end(&mut buf).unwrap(); + assert_eq!(expected_rest_bytes, buf); + } + + #[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]), case::count_2([0xAA, 0xBB, 0xCC].as_ref(), Endian::Little, Some(8), 2.into(), vec![0xAA, 0xBB], bits![u8, Msb0;], &[0xcc]), @@ -202,9 +231,10 @@ mod tests { case::normal(vec![0xAABB, 0xCCDD], Endian::Little, vec![0xBB, 0xAA, 0xDD, 0xCC]), )] fn test_vec_write(input: Vec, endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); } // Note: These tests also exist in boxed.rs @@ -243,11 +273,12 @@ mod tests { input.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + assert_eq!(expected_write, out_buf.to_vec()); assert_eq!(input_clone[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/lib.rs b/src/lib.rs index 64d36de5..feedda13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -331,6 +331,7 @@ pub mod no_std_io { pub use no_std_io::io::Cursor; pub use no_std_io::io::Read; pub use no_std_io::io::Result; + pub use no_std_io::io::Write; } /// re-export of bitvec @@ -347,8 +348,10 @@ pub mod error; mod impls; pub mod prelude; pub mod reader; +pub mod writer; pub use crate::error::DekuError; +use crate::writer::Writer; /// "Reader" trait: read bytes and bits from [`no_std_io::Read`]er pub trait DekuReader<'a, Ctx = ()> { @@ -422,26 +425,58 @@ pub trait DekuContainerRead<'a>: DekuReader<'a, ()> { Self: Sized; } -/// "Writer" trait: write from type to bits -pub trait DekuWrite { - /// Write type to bits - /// * **output** - Sink to store resulting bits - /// * **ctx** - A context required by context-sensitive reading. A unit type `()` means no context - /// needed. - fn write( +/// "Writer" trait: write from type to bytes +pub trait DekuWriter { + /// Write type to bytes + fn to_writer( &self, - output: &mut bitvec::BitVec, + writer: &mut Writer, ctx: Ctx, ) -> Result<(), DekuError>; } /// "Writer" trait: implemented on DekuWrite struct and enum containers. A `container` is a type which /// doesn't need any context information. -pub trait DekuContainerWrite: DekuWrite<()> { +pub trait DekuContainerWrite: DekuWriter<()> { /// Write struct/enum to Vec + /// + /// ```rust + /// # use deku::prelude::*; + /// #[derive(Debug, PartialEq, DekuRead, DekuWrite)] + /// #[deku(endian = "little")] + /// struct S { + /// data_00: u8, + /// data_01: u16, + /// data_02: u32, + /// } + /// + /// let s = S { data_00: 0x01, data_01: 0x02, data_02: 0x03 }; + /// let bytes = s.to_bytes().unwrap(); + /// assert_eq!(bytes, [0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00]); + /// ```` fn to_bytes(&self) -> Result, DekuError>; /// Write struct/enum to BitVec + /// + /// ```rust + /// # use deku::prelude::*; + /// # use deku::bitvec::Lsb0; + /// #[derive(PartialEq, Debug, DekuRead, DekuWrite)] + /// #[deku(endian = "little")] + /// pub struct TestOver { + /// #[deku(bits = "4")] + /// pub a: u8, + /// #[deku(bits = "4")] + /// pub b: u8, + /// #[deku(bits = "1")] + /// pub c: u8, + /// } + /// + /// let test_data: &[u8] = &[0xf1, 0x80]; + /// let test = TestOver::from_bytes((test_data, 0)).unwrap().1; + /// let bits = test.to_bits().unwrap(); + /// assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); + /// ``` fn to_bits(&self) -> Result, DekuError>; } @@ -457,19 +492,17 @@ pub trait DekuEnumExt<'a, T> { fn deku_id(&self) -> Result; } -/// Implements DekuWrite for references of types that implement DekuWrite -impl DekuWrite for &T +impl DekuWriter for &T where - T: DekuWrite, + T: DekuWriter, Ctx: Copy, { - /// Write value of type to bits - fn write( + fn to_writer( &self, - output: &mut bitvec::BitVec, + writer: &mut Writer, ctx: Ctx, ) -> Result<(), DekuError> { - ::write(self, output, ctx)?; + ::to_writer(self, writer, ctx)?; Ok(()) } } diff --git a/src/prelude.rs b/src/prelude.rs index 6a70b6d4..da52e982 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,6 +4,6 @@ */ pub use crate::error::{DekuError, NeedSize}; pub use crate::{ - deku_derive, reader::Reader, DekuContainerRead, DekuContainerWrite, DekuEnumExt, DekuRead, - DekuReader, DekuUpdate, DekuWrite, + deku_derive, reader::Reader, writer::Writer, DekuContainerRead, DekuContainerWrite, + DekuEnumExt, DekuRead, DekuReader, DekuUpdate, DekuWrite, DekuWriter, }; diff --git a/src/reader.rs b/src/reader.rs index f31ed1b9..cbcbf398 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -218,7 +218,7 @@ impl<'a, R: Read> Reader<'a, R> { self.bits_read += amt * 8; #[cfg(feature = "logging")] - log::trace!("read_bytes: returning {buf:02x?}"); + log::trace!("read_bytes: returning {:02x?}", &buf[..amt]); Ok(ReaderRet::Bytes) } else { diff --git a/src/writer.rs b/src/writer.rs new file mode 100644 index 00000000..a5075e2d --- /dev/null +++ b/src/writer.rs @@ -0,0 +1,194 @@ +//! Writer for writer functions + +use bitvec::bitvec; +use bitvec::{field::BitField, prelude::*}; +use no_std_io::io::Write; + +#[cfg(feature = "logging")] +use log; + +use crate::DekuError; + +const fn bits_of() -> usize { + core::mem::size_of::().saturating_mul(::BITS as usize) +} + +/// Container to use with `to_writer` +pub struct Writer<'a, W: Write> { + pub(crate) inner: &'a mut W, + /// Leftover bits + pub leftover: BitVec, + /// Total bits written + pub bits_written: usize, +} + +impl<'a, W: Write> Writer<'a, W> { + /// Create a new `Writer` + #[inline] + pub fn new(inner: &'a mut W) -> Self { + Self { + inner, + leftover: BitVec::new(), + bits_written: 0, + } + } + + /// Return the unused bits + #[inline] + pub fn rest(&mut self) -> alloc::vec::Vec { + self.leftover.iter().by_vals().collect() + } + + /// Write all bits to `Writer` buffer if bits can fit into a byte buffer + #[inline] + pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { + #[cfg(feature = "logging")] + log::trace!("attempting {} bits", bits.len()); + + // quick return if we can't write to the bytes buffer + if (self.leftover.len() + bits.len()) < 8 { + self.leftover.extend_from_bitslice(bits); + return Ok(()); + } + + // pre-pend the previous attempt to write if needed + let mut bits = if self.leftover.is_empty() { + bits + } else { + #[cfg(feature = "logging")] + log::trace!("pre-pending {} bits", self.leftover.len()); + + self.leftover.extend_from_bitslice(bits); + &mut self.leftover + }; + + // one shot impl of BitSlice::read(no read_exact), but for no_std + let mut buf = alloc::vec![0x00; bits.len() / 8]; + let mut count = 0; + bits.chunks_exact(bits_of::()) + .zip(buf.iter_mut()) + .for_each(|(byte, slot)| { + *slot = byte.load_be(); + count += 1; + }); + + // SAFETY: This does noto have a safety comment in bitvec. But this is safe + // because of `count` here will always still be within the bounds + // of `bits` + bits = unsafe { bits.get_unchecked(count * bits_of::()..) }; + + // TODO: with_capacity? + self.leftover = bits.to_bitvec(); + if self.inner.write_all(&buf).is_err() { + return Err(DekuError::Write); + } + + self.bits_written += buf.len() * 8; + #[cfg(feature = "logging")] + log::trace!("wrote {} bits: {buf:02x?}", buf.len() * 8); + + Ok(()) + } + + /// Write `buf` into `Writer` + // The following inline(always) helps performance significantly + #[inline(always)] + pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), DekuError> { + #[cfg(feature = "logging")] + log::trace!("writing {} bytes: {buf:02x?}", buf.len()); + + if !self.leftover.is_empty() { + #[cfg(feature = "logging")] + log::trace!("leftover exists"); + + // TODO: we could check here and only send the required bits to finish the byte? + // (instead of sending the entire thing) + self.write_bits(&BitVec::from_slice(buf))?; + } else { + if self.inner.write_all(buf).is_err() { + return Err(DekuError::Write); + } + self.bits_written += buf.len() * 8; + } + + Ok(()) + } + + /// Write all remaining bits into `Writer`, adding empty bits to the end so that we can write + /// into a byte buffer + #[inline] + pub fn finalize(&mut self) -> Result<(), DekuError> { + if !self.leftover.is_empty() { + #[cfg(feature = "logging")] + log::trace!("finalized: {} bits leftover", self.leftover.len()); + + // add bits to be byte aligned so we can write + self.leftover + .extend_from_bitslice(&bitvec![u8, Msb0; 0; 8 - self.leftover.len()]); + let mut buf = alloc::vec![0x00; self.leftover.len() / 8]; + + // write as many leftover to the buffer (as we can, can't write bits just bytes) + // TODO: error if bits are leftover? (not bytes aligned) + self.leftover + .chunks_exact(bits_of::()) + .zip(buf.iter_mut()) + .for_each(|(byte, slot)| { + *slot = byte.load_be(); + }); + + if self.inner.write_all(&buf).is_err() { + return Err(DekuError::Write); + } + self.bits_written += buf.len() * 8; + + #[cfg(feature = "logging")] + log::trace!("finalized: wrote {} bits: {buf:02x?}", buf.len() * 8); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hexlit::hex; + + #[test] + fn test_writer() { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + let mut bv = BitVec::::from_slice(&[0xbb]); + writer.write_bits(&mut bv).unwrap(); + + let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1]; + writer.write_bits(&mut bv).unwrap(); + let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1]; + writer.write_bits(&mut bv).unwrap(); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1]; + writer.write_bits(&mut bv).unwrap(); + let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1]; + writer.write_bits(&mut bv).unwrap(); + + let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1]; + writer.write_bits(&mut bv).unwrap(); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1]; + writer.write_bits(&mut bv).unwrap(); + + assert_eq!( + &mut out_buf, + &mut vec![0xaa, 0xbb, 0xf1, 0xaa, 0x1f, 0x1a, 0xaf] + ); + } +} diff --git a/tests/test_attributes/test_ctx.rs b/tests/test_attributes/test_ctx.rs index 3d16fcdf..cc55c4c1 100644 --- a/tests/test_attributes/test_ctx.rs +++ b/tests/test_attributes/test_ctx.rs @@ -5,6 +5,7 @@ use bitvec::bitvec; use deku::bitvec::Msb0; use deku::prelude::*; use deku::reader::Reader; +use deku::writer::Writer; /// General smoke tests for ctx /// TODO: These should be divided into smaller units @@ -16,7 +17,7 @@ fn test_ctx_struct() { struct SubTypeNeedCtx { #[deku( reader = "(u8::from_reader_with_ctx(deku::reader,()).map(|c|(a+b+c) as usize))", - writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(self.i as u8)" + writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(self.i as u8)" )] i: usize, } @@ -56,7 +57,7 @@ fn test_top_level_ctx_enum() { VariantA( #[deku( reader = "(u8::from_reader_with_ctx(deku::reader,()).map(|c|(a+b+c)))", - writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(field_0)" + writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(field_0)" )] u8, ), @@ -70,9 +71,10 @@ fn test_top_level_ctx_enum() { .unwrap(); assert_eq!(ret_read, TopLevelCtxEnum::VariantA(0x06)); - let mut ret_write = bitvec![u8, Msb0;]; - ret_read.write(&mut ret_write, (1, 2)).unwrap(); - assert_eq!(ret_write.into_vec(), &test_data[..]); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ret_read.to_writer(&mut writer, (1, 2)).unwrap(); + assert_eq!(out_buf.to_vec(), &test_data[..]); } #[test] @@ -84,7 +86,7 @@ fn test_top_level_ctx_enum_default() { VariantA( #[deku( reader = "(u8::from_reader_with_ctx(deku::reader, ()).map(|c|(a+b+c)))", - writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(field_0)" + writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(field_0)" )] u8, ), @@ -106,9 +108,10 @@ fn test_top_level_ctx_enum_default() { ) .unwrap(); assert_eq!(ret_read, TopLevelCtxEnumDefault::VariantA(0x06)); - let mut ret_write = bitvec![u8, Msb0;]; - ret_read.write(&mut ret_write, (1, 2)).unwrap(); - assert_eq!(test_data.to_vec(), ret_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ret_read.to_writer(&mut writer, (1, 2)).unwrap(); + assert_eq!(test_data.to_vec(), out_buf.to_vec()); } #[test] @@ -228,8 +231,10 @@ fn test_ctx_default_struct() { .unwrap(); assert_eq!(expected, ret_read); let mut ret_write = bitvec![u8, Msb0;]; - ret_read.write(&mut ret_write, (1, 2)).unwrap(); - assert_eq!(test_data.to_vec(), ret_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ret_read.to_writer(&mut writer, (1, 2)).unwrap(); + assert_eq!(test_data.to_vec(), out_buf.to_vec()); } #[test] diff --git a/tests/test_compile/cases/enum_validation.stderr b/tests/test_compile/cases/enum_validation.stderr index 1ebfc690..ee9715b4 100644 --- a/tests/test_compile/cases/enum_validation.stderr +++ b/tests/test_compile/cases/enum_validation.stderr @@ -16,8 +16,8 @@ error: conflicting: both `id` and `id_pat` specified on variant 16 | #[deku(id = "1", id_pat = "2..=3")] | ^^^ -error: `type` only supported on enum - --> $DIR/enum_validation.rs:22:15 + --> $DIR/enum_validation.rs:22:18 +error: `id_type` only supported on enum | 22 | #[deku(type = "u8")] | ^^^^ diff --git a/tests/test_compile/cases/internal_variables.rs b/tests/test_compile/cases/internal_variables.rs index 0108c685..561f9100 100644 --- a/tests/test_compile/cases/internal_variables.rs +++ b/tests/test_compile/cases/internal_variables.rs @@ -1,4 +1,3 @@ -use deku::bitvec::{BitVec, Msb0}; use deku::prelude::*; #[derive(DekuRead, DekuWrite)] @@ -73,13 +72,13 @@ struct TestCtx { field_b: ChildCtx, } -fn dummy_writer(_offset: usize, _output: &mut BitVec) -> Result<(), DekuError> { +fn dummy_writer(_offset: usize, _writer: &mut deku::writer::Writer) -> Result<(), DekuError> { Ok(()) } #[derive(DekuRead, DekuWrite)] struct TestWriter { field_a: u8, - #[deku(writer = "dummy_writer(deku::byte_offset, deku::output)")] + #[deku(writer = "dummy_writer(deku::byte_offset, deku::writer)")] field_b: usize, } diff --git a/tests/test_compile/cases/internal_variables.stderr b/tests/test_compile/cases/internal_variables.stderr index 891a145d..78531216 100644 --- a/tests/test_compile/cases/internal_variables.stderr +++ b/tests/test_compile/cases/internal_variables.stderr @@ -1,5 +1,5 @@ error: Unexpected meta-item format `attribute cannot contain `__deku_` these are internal variables. Please use the `deku::` instead.` - --> tests/test_compile/cases/internal_variables.rs:89:19 + --> tests/test_compile/cases/internal_variables.rs:88:19 | -89 | #[deku(cond = "__deku_bit_offset == *field_a as usize")] +88 | #[deku(cond = "__deku_bit_offset == *field_a as usize")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/test_compile/cases/temp_field.stderr b/tests/test_compile/cases/temp_field.stderr index 86b0716d..de5ba05a 100644 --- a/tests/test_compile/cases/temp_field.stderr +++ b/tests/test_compile/cases/temp_field.stderr @@ -7,7 +7,7 @@ error[E0063]: missing field `field_a` in initializer of `Test1` = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info) error: pattern requires `..` due to inaccessible fields - --> $DIR/temp_field.rs:4:20 + --> tests/test_compile/cases/temp_field.rs:4:20 | 4 | #[derive(DekuRead, DekuWrite)] | ^^^^^^^^^ diff --git a/tests/test_generic.rs b/tests/test_generic.rs index dfb3b8f9..09b3a3c1 100644 --- a/tests/test_generic.rs +++ b/tests/test_generic.rs @@ -7,7 +7,7 @@ fn test_generic_struct() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] struct TestStruct where - T: deku::DekuWrite + for<'a> deku::DekuReader<'a>, + T: deku::DekuWriter + for<'a> deku::DekuReader<'a>, { field_a: T, } @@ -27,7 +27,7 @@ fn test_generic_enum() { #[deku(type = "u8")] enum TestEnum where - T: deku::DekuWrite + for<'a> deku::DekuReader<'a>, + T: deku::DekuWriter + for<'a> deku::DekuReader<'a>, { #[deku(id = "1")] VariantT(T), diff --git a/tests/test_struct.rs b/tests/test_struct.rs index 98803092..41e3b3d4 100644 --- a/tests/test_struct.rs +++ b/tests/test_struct.rs @@ -179,3 +179,28 @@ fn test_raw_identifiers_struct() { let ret_write: Vec = ret_read.try_into().unwrap(); assert_eq!(test_data, ret_write); } + +#[test] +fn test_big_endian() { + #[derive(PartialEq, Debug, DekuRead, DekuWrite)] + pub struct A { + #[deku(bytes = "3", endian = "big")] + address: u32, + } + + let bytes = [0x11, 0x22, 0x33]; + let a = A::from_bytes((&bytes, 0)).unwrap().1; + let new_bytes = a.to_bytes().unwrap(); + assert_eq!(bytes, &*new_bytes); + + #[derive(PartialEq, Debug, DekuRead, DekuWrite)] + pub struct B { + #[deku(bytes = "2", endian = "big")] + address: u32, + } + + let bytes = [0x00, 0xff, 0xab, 0xaa]; + let a = B::from_bytes((&bytes, 0)).unwrap().1; + let new_bytes = a.to_bytes().unwrap(); + assert_eq!(&bytes[..2], &*new_bytes); +} diff --git a/tests/test_to_bits.rs b/tests/test_to_bits.rs new file mode 100644 index 00000000..129770ac --- /dev/null +++ b/tests/test_to_bits.rs @@ -0,0 +1,53 @@ +use std::convert::TryFrom; + +use deku::bitvec::Lsb0; +use deku::prelude::*; + +#[derive(PartialEq, Debug, DekuRead, DekuWrite)] +pub struct Test { + #[deku(bits = "4")] + pub a: u8, + #[deku(bits = "4")] + pub b: u8, +} + +#[test] +fn test_to_bits_correct() { + let test_data: &[u8] = &[0xf1]; + let test = Test::try_from(test_data).unwrap(); + let bits = test.to_bits().unwrap(); + assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1], bits); +} + +#[derive(PartialEq, Debug, DekuRead, DekuWrite)] +pub struct TestOver { + #[deku(bits = "4")] + pub a: u8, + #[deku(bits = "4")] + pub b: u8, + #[deku(bits = "1")] + pub c: u8, +} + +#[test] +fn test_to_bits_correct_over() { + let test_data: &[u8] = &[0xf1, 0x80]; + let test = TestOver::from_bytes((test_data, 0)).unwrap().1; + let bits = test.to_bits().unwrap(); + assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); +} + +#[derive(PartialEq, Debug, DekuRead, DekuWrite)] +#[deku(type = "u8", bits = "4")] +enum TestEnum { + #[deku(id = "0b1010")] + VarA, +} + +#[test] +fn test_to_bits_enum() { + let test_data: &[u8] = &[0b1010_0000]; + let test = TestEnum::from_bytes((test_data, 0)).unwrap().1; + let bits = test.to_bits().unwrap(); + assert_eq!(deku::bitvec::bitvec![1, 0, 1, 0], bits); +} From 93644d1d5f071551efa01537fa1ed319b5ac19bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rubens=20Brand=C3=A3o?= Date: Sun, 17 Dec 2023 12:11:46 -0300 Subject: [PATCH 3/9] remove the writer reference requirement (#384) --- src/writer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/writer.rs b/src/writer.rs index a5075e2d..bdcc46cf 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -14,18 +14,18 @@ const fn bits_of() -> usize { } /// Container to use with `to_writer` -pub struct Writer<'a, W: Write> { - pub(crate) inner: &'a mut W, +pub struct Writer { + pub(crate) inner: W, /// Leftover bits pub leftover: BitVec, /// Total bits written pub bits_written: usize, } -impl<'a, W: Write> Writer<'a, W> { +impl Writer { /// Create a new `Writer` #[inline] - pub fn new(inner: &'a mut W) -> Self { + pub fn new(inner: W) -> Self { Self { inner, leftover: BitVec::new(), From 6134243165dadb36b7613e469f4007ef1adc6b73 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Wed, 27 Dec 2023 21:26:23 -0500 Subject: [PATCH 4/9] Update to syn 2.0, darling 0.20 (#386) * Update to syn 2.0, darling 0.20 - Change `type` for enums into `id_type`, as this is no longer accepted through the syn parser library * Update to rstest 0.18 --- CHANGELOG.md | 4 +-- Cargo.toml | 2 +- benches/deku.rs | 2 +- deku-derive/Cargo.toml | 6 ++-- deku-derive/src/lib.rs | 30 ++++++++-------- examples/enums.rs | 2 +- examples/enums_catch_all.rs | 2 +- src/attributes.rs | 10 +++--- src/lib.rs | 2 +- tests/test_alloc.rs | 4 +-- tests/test_attributes/test_ctx.rs | 6 ++-- tests/test_attributes/test_temp.rs | 4 +-- tests/test_catch_all.rs | 4 +-- .../test_compile/cases/bits_bytes_conflict.rs | 4 +-- .../cases/bits_bytes_conflict.stderr | 6 ++-- .../test_compile/cases/catch_all_multiple.rs | 2 +- tests/test_compile/cases/enum_validation.rs | 32 ++++++++--------- .../test_compile/cases/enum_validation.stderr | 34 +++++++++---------- tests/test_compile/cases/temp_field.stderr | 2 +- tests/test_compile/cases/unknown_endian.rs | 4 +-- tests/test_deku_id.rs | 14 ++++---- tests/test_enum.rs | 8 ++--- tests/test_from_bytes.rs | 2 +- tests/test_from_reader.rs | 2 +- tests/test_generic.rs | 2 +- tests/test_magic.rs | 2 +- tests/test_regression.rs | 4 +-- tests/test_to_bits.rs | 2 +- tests/test_tuple.rs | 2 +- 29 files changed, 99 insertions(+), 101 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a18b9e83..f6a187ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,7 +111,7 @@ This also disallows using tuples for storing the id: old: ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id_pat = "_")] VariantC((u8, u8)), @@ -121,7 +121,7 @@ enum DekuTest { new: ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id_pat = "_")] VariantC { diff --git a/Cargo.toml b/Cargo.toml index 80ba0159..432138d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ log = { version = "0.4.17", optional = true } no_std_io = { version = "0.5.0", default-features = false, features = ["alloc"] } [dev-dependencies] -rstest = "0.16.0" +rstest = "0.18.0" hexlit = "0.5.5" criterion = "0.4.0" alloc_counter = "0.0.4" diff --git a/benches/deku.rs b/benches/deku.rs index d6bad653..903ef432 100644 --- a/benches/deku.rs +++ b/benches/deku.rs @@ -21,7 +21,7 @@ struct DekuBytes { } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuEnum { #[deku(id = "0x01")] VariantA(u8), diff --git a/deku-derive/Cargo.toml b/deku-derive/Cargo.toml index 5d475345..402e5063 100644 --- a/deku-derive/Cargo.toml +++ b/deku-derive/Cargo.toml @@ -18,12 +18,12 @@ logging = [] [dependencies] quote = "1.0" -syn = "1.0" +syn = "2.0" # extra-traits gives us Debug # syn = {version = "1.0", features = ["extra-traits"]} proc-macro2 = "1.0" -darling = "0.14" +darling = "0.20" proc-macro-crate = { version = "1.3.0", optional = true } [dev-dependencies] -rstest = "0.16" +rstest = "0.18" diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs index ccc32cb0..df541fd3 100644 --- a/deku-derive/src/lib.rs +++ b/deku-derive/src/lib.rs @@ -12,7 +12,6 @@ use proc_macro2::TokenStream; use quote::quote; use syn::punctuated::Punctuated; use syn::spanned::Spanned; -use syn::AttributeArgs; use crate::macros::deku_read::emit_deku_read; use crate::macros::deku_write::emit_deku_write; @@ -208,7 +207,10 @@ impl DekuData { ast::Data::Struct(_) => { // Validate id_* attributes are being used on an enum if data.id_type.is_some() { - Err(cerror(data.id_type.span(), "`type` only supported on enum")) + Err(cerror( + data.id_type.span(), + "`id_type` only supported on enum", + )) } else if data.id.is_some() { Err(cerror(data.id.span(), "`id` only supported on enum")) } else if data.bytes.is_some() { @@ -220,19 +222,19 @@ impl DekuData { } } ast::Data::Enum(_) => { - // Validate `type` or `id` is specified + // Validate `id_type` or `id` is specified if data.id_type.is_none() && data.id.is_none() { return Err(cerror( data.ident.span(), - "`type` or `id` must be specified on enum", + "`id_type` or `id` must be specified on enum", )); } - // Validate either `type` or `id` is specified + // Validate either `id_type` or `id` is specified if data.id_type.is_some() && data.id.is_some() { return Err(cerror( data.ident.span(), - "conflicting: both `type` and `id` specified on enum", + "conflicting: both `id_type` and `id` specified on enum", )); } @@ -638,11 +640,7 @@ struct DekuReceiver { id: Option, /// enum only: type of the enum `id` - #[darling( - rename = "type", - default = "default_res_opt", - map = "map_litstr_as_tokenstream" - )] + #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] id_type: Result, ReplacementError>, /// enum only: bit size of the enum `id` @@ -878,7 +876,7 @@ pub fn proc_deku_write(input: proc_macro::TokenStream) -> proc_macro::TokenStrea } fn is_not_deku(attr: &syn::Attribute) -> bool { - attr.path + attr.path() .get_ident() .map(|ident| ident != "deku" && ident != "deku_derive") .unwrap_or(true) @@ -942,8 +940,8 @@ pub fn deku_derive( item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { // Parse `deku_derive` attribute - let attr_args = syn::parse_macro_input!(attr as AttributeArgs); - let args = match DekuDerive::from_list(&attr_args) { + let nested_meta = darling::ast::NestedMeta::parse_meta_list(attr.into()).unwrap(); + let args = match DekuDerive::from_list(&nested_meta) { Ok(v) => v, Err(e) => { return proc_macro::TokenStream::from(e.write_errors()); @@ -1042,9 +1040,9 @@ mod tests { }"#), // Valid Enum - case::enum_empty(r#"#[deku(type = "u8")] enum Test {}"#), + case::enum_empty(r#"#[deku(id_type = "u8")] enum Test {}"#), case::enum_all(r#" - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum Test { #[deku(id = "1")] A, diff --git a/examples/enums.rs b/examples/enums.rs index 296ffc50..722c8aa4 100644 --- a/examples/enums.rs +++ b/examples/enums.rs @@ -6,7 +6,7 @@ use hexlit::hex; const THREE: u8 = 3; #[derive(Debug, PartialEq, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id = 0)] Var1, diff --git a/examples/enums_catch_all.rs b/examples/enums_catch_all.rs index 8126d1e0..882be0fd 100644 --- a/examples/enums_catch_all.rs +++ b/examples/enums_catch_all.rs @@ -4,7 +4,7 @@ use deku::prelude::*; use hexlit::hex; #[derive(Clone, Copy, PartialEq, Eq, Debug, DekuWrite, DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] #[non_exhaustive] #[repr(u8)] pub enum DekuTest { diff --git a/src/attributes.rs b/src/attributes.rs index 50525423..d159d227 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -1019,7 +1019,7 @@ Example: # use std::io::Cursor; # use std::convert::{TryInto, TryFrom}; # #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id = 0x01)] VariantA(u8), @@ -1057,7 +1057,7 @@ Example discriminant # use std::io::Cursor; # use std::convert::{TryInto, TryFrom}; # #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { VariantA = 0x01, VariantB, @@ -1099,7 +1099,7 @@ Example: # use std::io::Cursor; # use std::convert::{TryInto, TryFrom}; # #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id = 0x01)] VariantA(u8), @@ -1151,7 +1151,7 @@ Example: # use std::io::Cursor; # use std::convert::{TryInto, TryFrom}; # #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8", bits = 4)] +#[deku(id_type = "u8", bits = 4)] enum DekuTest { #[deku(id = 0b1001)] VariantA( #[deku(bits = 4)] u8, u8), @@ -1182,7 +1182,7 @@ Example: # use deku::prelude::*; # use std::convert::{TryInto, TryFrom}; # #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u32", bytes = 2)] +#[deku(id_type = "u32", bytes = 2)] enum DekuTest { #[deku(id = 0xBEEF)] VariantA(u8), diff --git a/src/lib.rs b/src/lib.rs index feedda13..ec328a8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,7 +174,7 @@ Example: use deku::prelude::*; #[derive(Debug, PartialEq, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id = 0x01)] VariantA, diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs index 32dd937f..0ac56ab8 100644 --- a/tests/test_alloc.rs +++ b/tests/test_alloc.rs @@ -14,14 +14,14 @@ struct NestedStruct { } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] -#[deku(type = "u8", ctx = "_endian: Endian")] +#[deku(id_type = "u8", ctx = "_endian: Endian")] enum NestedEnum { #[deku(id = "0x01")] VarA(u8), } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] -#[deku(type = "u32", bytes = 2, ctx = "_endian: Endian")] +#[deku(id_type = "u32", bytes = 2, ctx = "_endian: Endian")] enum NestedEnum2 { #[deku(id = "0x01")] VarA(u8), diff --git a/tests/test_attributes/test_ctx.rs b/tests/test_attributes/test_ctx.rs index cc55c4c1..f945cd34 100644 --- a/tests/test_attributes/test_ctx.rs +++ b/tests/test_attributes/test_ctx.rs @@ -51,7 +51,7 @@ fn test_ctx_struct() { #[test] fn test_top_level_ctx_enum() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "u8", ctx = "a: u8, b: u8")] + #[deku(id_type = "u8", ctx = "a: u8, b: u8")] enum TopLevelCtxEnum { #[deku(id = "1")] VariantA( @@ -80,7 +80,7 @@ fn test_top_level_ctx_enum() { #[test] fn test_top_level_ctx_enum_default() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "u8", ctx = "a: u8, b: u8", ctx_default = "1,2")] + #[deku(id_type = "u8", ctx = "a: u8, b: u8", ctx_default = "1,2")] enum TopLevelCtxEnumDefault { #[deku(id = "1")] VariantA( @@ -240,7 +240,7 @@ fn test_ctx_default_struct() { #[test] fn test_enum_endian_ctx() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "u32", endian = "endian", ctx = "endian: deku::ctx::Endian")] + #[deku(id_type = "u32", endian = "endian", ctx = "endian: deku::ctx::Endian")] enum EnumTypeEndianCtx { #[deku(id = "0xDEADBEEF")] VarA(u8), diff --git a/tests/test_attributes/test_temp.rs b/tests/test_attributes/test_temp.rs index 2a0cb44a..c4040b24 100644 --- a/tests/test_attributes/test_temp.rs +++ b/tests/test_attributes/test_temp.rs @@ -104,7 +104,7 @@ fn test_temp_field_unnamed_write() { fn test_temp_enum_field() { #[deku_derive(DekuRead, DekuWrite)] #[derive(PartialEq, Debug)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum TestEnum { #[deku(id = "0xAB")] VarA { @@ -133,7 +133,7 @@ fn test_temp_enum_field() { fn test_temp_enum_field_write() { #[deku_derive(DekuRead, DekuWrite)] #[derive(PartialEq, Debug)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum TestEnum { #[deku(id = "0xAB")] VarA { diff --git a/tests/test_catch_all.rs b/tests/test_catch_all.rs index 1565cf01..62bc4cfd 100644 --- a/tests/test_catch_all.rs +++ b/tests/test_catch_all.rs @@ -6,7 +6,7 @@ mod test { /// Basic test struct #[derive(Clone, Copy, PartialEq, Eq, Debug, DekuWrite, DekuRead)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] #[non_exhaustive] #[repr(u8)] pub enum BasicMapping { @@ -21,7 +21,7 @@ mod test { /// Advanced test struct #[derive(Clone, Copy, PartialEq, Eq, Debug, DekuWrite, DekuRead)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] #[non_exhaustive] #[repr(u8)] pub enum AdvancedRemapping { diff --git a/tests/test_compile/cases/bits_bytes_conflict.rs b/tests/test_compile/cases/bits_bytes_conflict.rs index bc43d3b1..a6c18c8a 100644 --- a/tests/test_compile/cases/bits_bytes_conflict.rs +++ b/tests/test_compile/cases/bits_bytes_conflict.rs @@ -1,11 +1,11 @@ use deku::prelude::*; #[derive(DekuRead)] -#[deku(type = "u8", bits = 1, bytes = 2)] +#[deku(id_type = "u8", bits = 1, bytes = 2)] enum Test1 {} #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum Test2 { A(#[deku(bits = 1, bytes = 2)] u8), B { diff --git a/tests/test_compile/cases/bits_bytes_conflict.stderr b/tests/test_compile/cases/bits_bytes_conflict.stderr index ff8f9d52..8ea84956 100644 --- a/tests/test_compile/cases/bits_bytes_conflict.stderr +++ b/tests/test_compile/cases/bits_bytes_conflict.stderr @@ -1,8 +1,8 @@ error: conflicting: both `bits` and `bytes` specified on enum - --> $DIR/bits_bytes_conflict.rs:4:28 + --> $DIR/bits_bytes_conflict.rs:4:31 | -4 | #[deku(type = "u8", bits = 1, bytes = 2)] - | ^ +4 | #[deku(id_type = "u8", bits = 1, bytes = 2)] + | ^ error: conflicting: both `bits` and `bytes` specified on field --> $DIR/bits_bytes_conflict.rs:10:21 diff --git a/tests/test_compile/cases/catch_all_multiple.rs b/tests/test_compile/cases/catch_all_multiple.rs index cdc80f5c..72a1f538 100644 --- a/tests/test_compile/cases/catch_all_multiple.rs +++ b/tests/test_compile/cases/catch_all_multiple.rs @@ -1,7 +1,7 @@ use deku::prelude::*; #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum Test1 { #[deku(default)] A = 1, diff --git a/tests/test_compile/cases/enum_validation.rs b/tests/test_compile/cases/enum_validation.rs index cf1aedcd..7adafe4f 100644 --- a/tests/test_compile/cases/enum_validation.rs +++ b/tests/test_compile/cases/enum_validation.rs @@ -4,22 +4,22 @@ use deku::prelude::*; #[derive(DekuRead)] enum Test1 {} -// test conflict `type` and `id` +// test conflict `type` and `id_type` #[derive(DekuRead)] -#[deku(type = "u8", id = "test")] +#[deku(id_type = "u8", id = "test")] enum Test2 {} -// test conflict `id` and `id_pat` +// test conflict `id_type` and `id_pat` #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum Test3 { #[deku(id = "1", id_pat = "2..=3")] A(u8), } -// test `type` only allowed on enum +// test `id_type` only allowed on enum #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] struct Test4 { a: u8, } @@ -38,38 +38,38 @@ struct Test6 { a: u8, } -// test `id` only allowed on enum +// test `id_type` only allowed on enum #[derive(DekuRead)] -#[deku(id = "test")] +#[deku(id_type = "test")] struct Test7 { a: u8, } -// test `bits` cannot be used with `id` +// test `bits` cannot be used with `id_type` #[derive(DekuRead)] -#[deku(id = "test", bits = 4)] +#[deku(id_type = "test", bits = 4)] enum Test8 { A, } -// test `bytes` cannot be used with `id` +// test `bytes` cannot be used with `id_type` #[derive(DekuRead)] -#[deku(id = "test", bytes = 4)] +#[deku(id_type = "test", bytes = 4)] enum Test9 { A, } -// test `id` cannot be `_` +// test `type_id` cannot be `_` #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum Test10 { #[deku(id = "_")] A, } -// test missing `id` +// test missing `id_type` #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum Test11 { #[deku(id = "1")] A, diff --git a/tests/test_compile/cases/enum_validation.stderr b/tests/test_compile/cases/enum_validation.stderr index ee9715b4..85e2d8c4 100644 --- a/tests/test_compile/cases/enum_validation.stderr +++ b/tests/test_compile/cases/enum_validation.stderr @@ -1,10 +1,10 @@ -error: `type` or `id` must be specified on enum +error: `id_type` or `id` must be specified on enum --> $DIR/enum_validation.rs:5:6 | 5 | enum Test1 {} | ^^^^^ -error: conflicting: both `type` and `id` specified on enum +error: conflicting: both `id_type` and `id` specified on enum --> $DIR/enum_validation.rs:10:6 | 10 | enum Test2 {} @@ -16,11 +16,11 @@ error: conflicting: both `id` and `id_pat` specified on variant 16 | #[deku(id = "1", id_pat = "2..=3")] | ^^^ - --> $DIR/enum_validation.rs:22:18 error: `id_type` only supported on enum + --> $DIR/enum_validation.rs:22:18 | -22 | #[deku(type = "u8")] - | ^^^^ +22 | #[deku(id_type = "u8")] + | ^^^^ error: `bits` only supported on enum --> $DIR/enum_validation.rs:29:15 @@ -34,23 +34,23 @@ error: `bits` only supported on enum 36 | #[deku(bits = 1)] | ^ -error: `id` only supported on enum - --> $DIR/enum_validation.rs:43:13 +error: `id_type` only supported on enum + --> $DIR/enum_validation.rs:43:18 | -43 | #[deku(id = "test")] - | ^^^^^^ +43 | #[deku(id_type = "test")] + | ^^^^^^ -error: error: cannot use `bits` with `id` - --> $DIR/enum_validation.rs:51:6 +error: DekuRead: `id` must be specified on non-unit variants + --> $DIR/enum_validation.rs:52:5 | -51 | enum Test8 { - | ^^^^^ +52 | A, + | ^ -error: error: cannot use `bytes` with `id` - --> $DIR/enum_validation.rs:58:6 +error: DekuRead: `id` must be specified on non-unit variants + --> $DIR/enum_validation.rs:59:5 | -58 | enum Test9 { - | ^^^^^ +59 | A, + | ^ error: error: `id_pat` should be used for `_` --> $DIR/enum_validation.rs:67:5 diff --git a/tests/test_compile/cases/temp_field.stderr b/tests/test_compile/cases/temp_field.stderr index de5ba05a..b64d3d81 100644 --- a/tests/test_compile/cases/temp_field.stderr +++ b/tests/test_compile/cases/temp_field.stderr @@ -1,5 +1,5 @@ error[E0063]: missing field `field_a` in initializer of `Test1` - --> $DIR/temp_field.rs:4:10 + --> tests/test_compile/cases/temp_field.rs:4:10 | 4 | #[derive(DekuRead, DekuWrite)] | ^^^^^^^^ missing `field_a` diff --git a/tests/test_compile/cases/unknown_endian.rs b/tests/test_compile/cases/unknown_endian.rs index de477260..6d787a3e 100644 --- a/tests/test_compile/cases/unknown_endian.rs +++ b/tests/test_compile/cases/unknown_endian.rs @@ -13,11 +13,11 @@ struct Test2 { } #[derive(DekuRead)] -#[deku(type = "u8", endian = "variable")] +#[deku(id_type = "u8", endian = "variable")] enum Test3 {} #[derive(DekuRead)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum Test4 { #[deku(id = "1")] A(#[deku(endian = "variable")] u8), diff --git a/tests/test_deku_id.rs b/tests/test_deku_id.rs index 6d0bc1d2..633847f9 100644 --- a/tests/test_deku_id.rs +++ b/tests/test_deku_id.rs @@ -3,7 +3,7 @@ use deku::prelude::*; #[test] fn test_regular() { #[derive(Debug, DekuRead, DekuWrite)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum Request1 { #[deku(id = "0x01")] Cats { toy: u8 }, @@ -19,7 +19,7 @@ fn test_regular() { #[test] fn test_custom_type() { #[derive(Debug, DekuRead, PartialEq, DekuWrite)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum Request2 { #[deku(id = "0x01")] Cats, @@ -29,7 +29,7 @@ fn test_custom_type() { } #[derive(Debug, DekuRead, DekuWrite)] - #[deku(type = "Request2")] + #[deku(id_type = "Request2")] enum Request3 { #[deku(id = "Request2::Cats")] Cats, @@ -56,7 +56,7 @@ fn test_ctx() { assert_eq!(Ok(1), EnumId::VarA(0).deku_id()); #[derive(Copy, Clone, PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum Nice { True = 0x00, False = 0x01, @@ -78,7 +78,7 @@ fn test_ctx() { #[test] fn test_ctx_and_type() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "u8", ctx = "_a: u8, _b: u8")] + #[deku(id_type = "u8", ctx = "_a: u8, _b: u8")] enum TopLevelCtxEnum { #[deku(id = "1")] VariantA(u8), @@ -90,7 +90,7 @@ fn test_ctx_and_type() { #[test] fn test_litbytestr() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "[u8; 3]")] + #[deku(id_type = "[u8; 3]")] enum TestEnumArray { #[deku(id = b"123")] VarA, @@ -105,7 +105,7 @@ fn test_litbytestr() { #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: IdVariantNotFound")] fn test_no_id_discriminant() { #[derive(Debug, DekuRead, PartialEq, DekuWrite)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum Discriminant { Cats = 0x01, Dogs, diff --git a/tests/test_enum.rs b/tests/test_enum.rs index a3ec4d71..cbe39478 100644 --- a/tests/test_enum.rs +++ b/tests/test_enum.rs @@ -8,7 +8,7 @@ use rstest::*; /// TODO: These should be divided into smaller tests #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum TestEnum { #[deku(id = "1")] VarA(u8), @@ -54,7 +54,7 @@ fn test_enum(input: &[u8], expected: TestEnum) { #[should_panic(expected = "Parse(\"Could not match enum variant id = 2 on enum `TestEnum`\")")] fn test_enum_error() { #[derive(DekuRead)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum TestEnum { #[deku(id = "1")] VarA(u8), @@ -65,7 +65,7 @@ fn test_enum_error() { } #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum TestEnumDiscriminant { VarA = 0x00, VarB, @@ -92,7 +92,7 @@ fn test_enum_discriminant(input: &[u8], expected: TestEnumDiscriminant) { #[test] fn test_enum_array_type() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "[u8; 3]")] + #[deku(id_type = "[u8; 3]")] enum TestEnumArray { #[deku(id = b"123")] VarA, diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs index b40e529e..a992c5ba 100644 --- a/tests/test_from_bytes.rs +++ b/tests/test_from_bytes.rs @@ -31,7 +31,7 @@ fn test_from_bytes_struct() { #[test] fn test_from_bytes_enum() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] - #[deku(type = "u8", bits = 4)] + #[deku(id_type = "u8", bits = 4)] enum TestDeku { #[deku(id = "0b0110")] VariantA(#[deku(bits = 4)] u8), diff --git a/tests/test_from_reader.rs b/tests/test_from_reader.rs index eb7780f5..02c927d3 100644 --- a/tests/test_from_reader.rs +++ b/tests/test_from_reader.rs @@ -37,7 +37,7 @@ fn test_from_reader_struct() { #[test] fn test_from_reader_enum() { #[derive(Debug, PartialEq, DekuRead, DekuWrite)] - #[deku(type = "u8", bits = 4)] + #[deku(id_type = "u8", bits = 4)] enum TestDeku { #[deku(id = "0b0110")] VariantA(#[deku(bits = 4)] u8), diff --git a/tests/test_generic.rs b/tests/test_generic.rs index 09b3a3c1..a3826dc5 100644 --- a/tests/test_generic.rs +++ b/tests/test_generic.rs @@ -24,7 +24,7 @@ fn test_generic_struct() { #[test] fn test_generic_enum() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(type = "u8")] + #[deku(id_type = "u8")] enum TestEnum where T: deku::DekuWriter + for<'a> deku::DekuReader<'a>, diff --git a/tests/test_magic.rs b/tests/test_magic.rs index 29d55c84..b87f0179 100644 --- a/tests/test_magic.rs +++ b/tests/test_magic.rs @@ -58,7 +58,7 @@ fn test_magic_struct(input: &[u8]) { )] fn test_magic_enum(input: &[u8]) { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] - #[deku(magic = b"deku", type = "u8")] + #[deku(magic = b"deku", id_type = "u8")] enum TestEnum { #[deku(id = "0")] Variant, diff --git a/tests/test_regression.rs b/tests/test_regression.rs index c3afb70d..073d5f2d 100644 --- a/tests/test_regression.rs +++ b/tests/test_regression.rs @@ -18,7 +18,7 @@ fn issue_224() { } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] - #[deku(type = "u8", bits = 2)] + #[deku(id_type = "u8", bits = 2)] pub enum One { Start = 0, Go = 1, @@ -26,7 +26,7 @@ fn issue_224() { } #[derive(Debug, PartialEq, DekuRead, DekuWrite)] - #[deku(type = "u8", bits = 4)] + #[deku(id_type = "u8", bits = 4)] pub enum Two { #[deku(id = "0b0000")] Put(Op1), diff --git a/tests/test_to_bits.rs b/tests/test_to_bits.rs index 129770ac..1515a403 100644 --- a/tests/test_to_bits.rs +++ b/tests/test_to_bits.rs @@ -38,7 +38,7 @@ fn test_to_bits_correct_over() { } #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8", bits = "4")] +#[deku(id_type = "u8", bits = "4")] enum TestEnum { #[deku(id = "0b1010")] VarA, diff --git a/tests/test_tuple.rs b/tests/test_tuple.rs index be955296..7c38bb7b 100644 --- a/tests/test_tuple.rs +++ b/tests/test_tuple.rs @@ -5,7 +5,7 @@ use hexlit::hex; use rstest::*; #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum TestEnum { #[deku(id = "1")] VarA((u8, u16)), From 12f40ef5dd9f4c66ab260b089959d271150fbf99 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Wed, 27 Dec 2023 21:41:34 -0500 Subject: [PATCH 5/9] Update CHANGELOG --- CHANGELOG.md | 110 ++++++++++++++++++++++++++++++++++++++++----- src/impls/slice.rs | 2 - src/lib.rs | 30 ++++++++++--- src/writer.rs | 8 +++- 4 files changed, 130 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6a187ac..2068fe0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,25 @@ ## [Unreleased] ## Changes -[#390](https://github.com/sharksforarms/deku/pull/390) added MSRV for `1.67.1`. -[#389](https://github.com/sharksforarms/deku/pull/389) changed edition to 2021 -[#352](https://github.com/sharksforarms/deku/pull/352) added a new function `from_reader` that uses `io::Read`. -`io::Read` is also now used internally, bringing massive performance and usability improvements. +- Added MSRV for `1.67.1` ([#390](https://github.com/sharksforarms/deku/pull/390)) +- Changed edition to 2021 ([#389](https://github.com/sharksforarms/deku/pull/389)) +- Refactored `logging` feature with massive usability increases ([#352](https://github.com/sharksforarms/deku/pull/352)), ([#355](https://github.com/sharksforarms/deku/pull/355)) +- Bumped the `syn` library to 2.0, which required replacing `type` for Enums with `id_type` ([#386](https://github.com/sharksforarms/deku/pull/386)) +```diff,rust + #[derive(PartialEq, Debug, DekuRead, DekuWrite)] +-#[deku(type = "u8")] ++#[deku(id_type = "u8")] + enum DekuTest { + #[deku(id_pat = "_")] + VariantC((u8, u8)), + } +``` + +### Updated Reader API +- Changed API of reading to use `io::Read`, bringing massive performance and usability improvements ([#352](https://github.com/sharksforarms/deku/pull/352)) +- Changed the trait `DekuRead` to `DekuReader` -### New `from_reader` +For example: ```rust use std::io::{Seek, SeekFrom, Read}; use std::fs::File; @@ -31,7 +44,7 @@ With the switch to internal streaming, the variables `deku::input`, `deku::input `deku::reader` is a replacement for some of the functionality. See [examples/deku_input.rs](examples/deku_input.rs) for a new example of caching all reads. -old: +Old: ```rust #[derive(Debug, PartialEq, DekuRead, DekuWrite)] struct DekuTest { @@ -56,7 +69,7 @@ fn custom_read( } ``` -new: +New: ```rust #[derive(Debug, PartialEq, DekuRead, DekuWrite)] struct DekuTest { @@ -83,7 +96,7 @@ fn custom_read( - With the addition of using `Read`, containing a byte slice with a reference is not supported: -old +Old ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] struct TestStruct<'a> { @@ -94,7 +107,7 @@ struct TestStruct<'a> { } ``` -new +New ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] struct TestStruct { @@ -108,7 +121,7 @@ struct TestStruct { - `id_pat` is now required to be the same type as stored id. This also disallows using tuples for storing the id: -old: +Old: ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8")] @@ -118,7 +131,7 @@ enum DekuTest { } ``` -new: +New: ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] #[deku(id_type = "u8")] @@ -133,6 +146,81 @@ enum DekuTest { - The feature `const_generics` was removed and is enabled by default. +### Updated Writer API +- Changed API of writing to use `io::Write`, bringing massive performance and usability improvements ([#355](https://github.com/sharksforarms/deku/pull/355)) +- Changed the trait `DekuWrite` to `DekuWriter` +- The more internal (with context) `write(..)` was replaced with `to_writer(..)`. +With the switch to internal streaming, the variables `deku::output` are now not possible and were removed. `deku::writer` is a replacement for some of the functionality. + +Old: +```rust +fn bit_flipper_write( + field_a: u8, + field_b: u8, + output: &mut BitVec, + bit_size: BitSize, +) -> Result<(), DekuError> { + // Access to previously written fields + println!("field_a = 0x{:X}", field_a); + + // value of field_b + println!("field_b = 0x{:X}", field_b); + + // Size of the current field + println!("bit_size: {:?}", bit_size); + + // flip the bits on value if field_a is 0x01 + let value = if field_a == 0x01 { !field_b } else { field_b }; + + value.write(output, bit_size) +} + +#[derive(Debug, PartialEq, DekuRead, DekuWrite)] +struct DekuTest { + field_a: u8, + + #[deku( + writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))" + )] + field_b: u8, +} +```` + +New: +```rust +fn bit_flipper_write( + field_a: u8, + field_b: u8, + writer: &mut Writer, + bit_size: BitSize, +) -> Result<(), DekuError> { + // Access to previously written fields + println!("field_a = 0x{:X}", field_a); + + // value of field_b + println!("field_b = 0x{:X}", field_b); + + // Size of the current field + println!("bit_size: {:?}", bit_size); + + // flip the bits on value if field_a is 0x01 + let value = if field_a == 0x01 { !field_b } else { field_b }; + + value.to_writer(writer, bit_size) +} + +#[derive(Debug, PartialEq, DekuRead, DekuWrite)] +struct DekuTest { + field_a: u8, + + #[deku( + writer = "bit_flipper_write(*field_a, *field_b, deku::writer, BitSize(8))" + )] + field_b: u8, +} +``` +- Added `DekuError::Write` to denote `io::Write` errors + ## [0.16.0] - 2023-02-28 ### Changes diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 9d38fb52..bae3c403 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,7 +1,5 @@ //! Implementations of DekuRead and DekuWrite for [T; N] where 0 < N <= 32 -pub use deku_derive::*; - use crate::reader::Reader; use crate::writer::Writer; use crate::{DekuError, DekuReader, DekuWriter}; diff --git a/src/lib.rs b/src/lib.rs index ec328a8c..1082a88f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -238,7 +238,7 @@ using `no_std`. # use std::io::{Seek, SeekFrom, Read}; # use std::fs::File; # use deku::prelude::*; -#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Hash)] +#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)] #[deku(endian = "big")] struct EcHdr { magic: [u8; 4], @@ -250,6 +250,23 @@ let mut file = File::options().read(true).open("file").unwrap(); let ec = EcHdr::from_reader((&mut file, 0)).unwrap(); ``` +# `Write` enabled +Parsers can be created that directly write to a source implementing [Write](crate::no_std_io::Write). + +```rust, no_run +# use std::io::{Seek, SeekFrom, Read}; +# use std::fs::File; +# use deku::prelude::*; +#[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)] +#[deku(endian = "big")] +struct Hdr { + version: u8, +} +let hdr = Hdr { version: 0xf0 }; +let mut file = File::options().write(true).open("file").unwrap(); +hdr.to_writer(&mut Writer::new(file), ()); +``` + # Internal variables and previously read fields Along similar lines to [Context](#context) variables, previously read variables @@ -273,8 +290,8 @@ tokens such as `reader`, `writer`, `map`, `count`, etc. These are provided as a convenience to the user. Always included: -- `deku::reader: &mut Reader` - Current [Reader](crate::reader::Reader) -- `deku::output: &mut BitSlice` - The output bit stream +- `deku::reader: &mut Reader` - Current [Reader] +- `deku::writer: &mut Writer` - Current [Writer] Conditionally included if referenced: - `deku::bit_offset: usize` - Current bit offset from the input @@ -351,6 +368,7 @@ pub mod reader; pub mod writer; pub use crate::error::DekuError; +use crate::reader::Reader; use crate::writer::Writer; /// "Reader" trait: read bytes and bits from [`no_std_io::Read`]er @@ -363,7 +381,7 @@ pub trait DekuReader<'a, Ctx = ()> { /// # use std::fs::File; /// # use deku::prelude::*; /// # use deku::ctx::Endian; - /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Hash)] + /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)] /// #[deku(endian = "ctx_endian", ctx = "ctx_endian: Endian")] /// struct EcHdr { /// magic: [u8; 4], @@ -376,7 +394,7 @@ pub trait DekuReader<'a, Ctx = ()> { /// let ec = EcHdr::from_reader_with_ctx(&mut reader, Endian::Big).unwrap(); /// ``` fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, ctx: Ctx, ) -> Result where @@ -399,7 +417,7 @@ pub trait DekuContainerRead<'a>: DekuReader<'a, ()> { /// # use std::io::{Seek, SeekFrom, Read}; /// # use std::fs::File; /// # use deku::prelude::*; - /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone, Hash)] + /// #[derive(Debug, DekuRead, DekuWrite, PartialEq, Eq, Clone)] /// #[deku(endian = "big")] /// struct EcHdr { /// magic: [u8; 4], diff --git a/src/writer.rs b/src/writer.rs index bdcc46cf..268c62d4 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -39,7 +39,10 @@ impl Writer { self.leftover.iter().by_vals().collect() } - /// Write all bits to `Writer` buffer if bits can fit into a byte buffer + /// Write all `bits` to `Writer` buffer if bits can fit into a byte buffer. + /// + /// Any leftover bits will be written before `bits`, and non-written bits will + /// be stored in `self.leftover`. #[inline] pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { #[cfg(feature = "logging")] @@ -91,6 +94,9 @@ impl Writer { } /// Write `buf` into `Writer` + /// + /// If no `self.leftover`, this will write directly into `Writer`, and if not will write + /// `buf` using `Self::write_bits()`. // The following inline(always) helps performance significantly #[inline(always)] pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), DekuError> { From 26c00d3b05f369f9699aae27bd53005d139a2d60 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Wed, 27 Dec 2023 22:56:18 -0500 Subject: [PATCH 6/9] Make DekuContainerWrite a default impl - Reduce codegen by making this a default impl --- deku-derive/src/macros/deku_write.rs | 42 ++-------------------------- src/lib.rs | 18 ++++++++++-- 2 files changed, 18 insertions(+), 42 deletions(-) diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs index 1f9d2592..7ecb47ac 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -64,26 +64,7 @@ fn emit_struct(input: &DekuData) -> Result { } } - impl #imp DekuContainerWrite for #ident #wher { - fn to_bytes(&self) -> core::result::Result, ::#crate_::DekuError> { - let mut out_buf = vec![]; - let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); - ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; - __deku_writer.finalize()?; - Ok(out_buf) - } - - #[allow(unused_variables)] - fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - let mut out_buf = vec![]; - let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); - ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; - let mut leftover = __deku_writer.leftover; - let mut bv = ::#crate_::bitvec::BitVec::from_slice(&out_buf); - bv.append(&mut leftover); - Ok(bv) - } - } + impl #imp DekuContainerWrite for #ident #wher {} }); } @@ -274,26 +255,7 @@ fn emit_enum(input: &DekuData) -> Result { } } - impl #imp DekuContainerWrite for #ident #wher { - fn to_bytes(&self) -> core::result::Result, ::#crate_::DekuError> { - let mut out_buf = vec![]; - let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); - ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; - __deku_writer.finalize()?; - Ok(out_buf) - } - - #[allow(unused_variables)] - fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - let mut out_buf = vec![]; - let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); - ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; - let mut leftover = __deku_writer.leftover; - let mut bv = ::#crate_::bitvec::BitVec::from_slice(&out_buf); - bv.append(&mut leftover); - Ok(bv) - } - } + impl #imp DekuContainerWrite for #ident #wher {} }) } diff --git a/src/lib.rs b/src/lib.rs index 1082a88f..30b8dfd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -472,7 +472,13 @@ pub trait DekuContainerWrite: DekuWriter<()> { /// let bytes = s.to_bytes().unwrap(); /// assert_eq!(bytes, [0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00]); /// ```` - fn to_bytes(&self) -> Result, DekuError>; + fn to_bytes(&self) -> Result, DekuError> { + let mut out_buf = Vec::new(); + let mut __deku_writer = Writer::new(&mut out_buf); + DekuWriter::to_writer(self, &mut __deku_writer, ())?; + __deku_writer.finalize()?; + Ok(out_buf) + } /// Write struct/enum to BitVec /// @@ -495,7 +501,15 @@ pub trait DekuContainerWrite: DekuWriter<()> { /// let bits = test.to_bits().unwrap(); /// assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); /// ``` - fn to_bits(&self) -> Result, DekuError>; + fn to_bits(&self) -> Result, DekuError> { + let mut out_buf = Vec::new(); + let mut __deku_writer = Writer::new(&mut out_buf); + DekuWriter::to_writer(self, &mut __deku_writer, ())?; + let mut leftover = __deku_writer.leftover; + let mut bv = bitvec::BitVec::from_slice(&out_buf); + bv.append(&mut leftover); + Ok(bv) + } } /// "Updater" trait: apply mutations to a type From 755a290246ed9a4cb869147bcc8f21a62c4957b0 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Sun, 7 Jan 2024 17:32:32 -0500 Subject: [PATCH 7/9] Impl DekuWriter for Box --- src/impls/boxed.rs | 12 ++++++++++++ tests/test_box.rs | 14 ++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/test_box.rs diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs index 565dc8a8..154a9188 100644 --- a/src/impls/boxed.rs +++ b/src/impls/boxed.rs @@ -52,6 +52,18 @@ where } } +impl DekuWriter for Box +where + T: DekuWriter, + Ctx: Copy, +{ + /// Write all `T`s to bits + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { + self.as_ref().to_writer(writer, ctx)?; + Ok(()) + } +} + #[cfg(test)] mod tests { use no_std_io::io::Cursor; diff --git a/tests/test_box.rs b/tests/test_box.rs new file mode 100644 index 00000000..d9cb6fc2 --- /dev/null +++ b/tests/test_box.rs @@ -0,0 +1,14 @@ +use deku::prelude::*; + +#[derive(DekuRead, DekuWrite)] +struct TestStruct { + field: Box, +} + +#[test] +fn test_box_smoke_test() { + let test_data: &[u8] = &[0xf0]; + let a = TestStruct::try_from(test_data).unwrap(); + let new_bytes = a.to_bytes().unwrap(); + assert_eq!(test_data, &*new_bytes); +} From 0ada1dc698814874405099bc87dfb5475f05ed6e Mon Sep 17 00:00:00 2001 From: wcampbell Date: Sun, 7 Jan 2024 17:32:49 -0500 Subject: [PATCH 8/9] Address MR feedback --- src/impls/bool.rs | 13 +++++-------- src/impls/boxed.rs | 10 ++++------ src/impls/cow.rs | 5 ++--- src/impls/cstring.rs | 5 ++--- src/impls/hashmap.rs | 5 ++--- src/impls/hashset.rs | 10 ++++------ src/impls/ipaddr.rs | 20 ++++++++------------ src/impls/nonzero.rs | 5 ++--- src/impls/option.rs | 9 ++++++++- src/impls/primitive.rs | 20 ++++++++------------ src/impls/slice.rs | 10 ++++------ src/impls/tuple.rs | 5 ++--- src/impls/unit.rs | 5 ++--- src/impls/vec.rs | 10 ++++------ src/writer.rs | 11 +++++------ 15 files changed, 62 insertions(+), 81 deletions(-) diff --git a/src/impls/bool.rs b/src/impls/bool.rs index eac90ea5..34fdaba5 100644 --- a/src/impls/bool.rs +++ b/src/impls/bool.rs @@ -76,19 +76,16 @@ mod tests { #[test] fn test_writer() { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); true.to_writer(&mut writer, BitSize(1)).unwrap(); assert_eq!(vec![true], writer.rest()); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); true.to_writer(&mut writer, ()).unwrap(); - assert_eq!(vec![1], out_buf); + assert_eq!(vec![1], writer.inner); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); false.to_writer(&mut writer, ()).unwrap(); - assert_eq!(vec![0], out_buf); + assert_eq!(vec![0], writer.inner); } } diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs index 154a9188..a24a47f4 100644 --- a/src/impls/boxed.rs +++ b/src/impls/boxed.rs @@ -87,10 +87,9 @@ mod tests { let res_read = >::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, ()).unwrap(); - assert_eq!(input.to_vec(), out_buf.to_vec()); + assert_eq!(input.to_vec(), writer.inner); } // Note: Copied tests from vec.rs impl @@ -131,12 +130,11 @@ mod tests { assert_eq!(input[..expected_write.len()].to_vec(), expected_write); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, out_buf.to_vec()); + assert_eq!(expected_write, writer.inner); assert_eq!(input[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/impls/cow.rs b/src/impls/cow.rs index 078bf4da..c2461ab6 100644 --- a/src/impls/cow.rs +++ b/src/impls/cow.rs @@ -51,9 +51,8 @@ mod tests { let res_read = >::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, ()).unwrap(); - assert_eq!(input.to_vec(), out_buf.to_vec()); + assert_eq!(input.to_vec(), writer.inner); } } diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs index 34c46337..cf636b55 100644 --- a/src/impls/cstring.rs +++ b/src/impls/cstring.rs @@ -67,9 +67,8 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest, buf); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, ()).unwrap(); - assert_eq!(vec![b't', b'e', b's', b't', b'\0'], out_buf.to_vec()); + assert_eq!(vec![b't', b'e', b's', b't', b'\0'], writer.inner); } } diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs index 7762a5a4..953fe35a 100644 --- a/src/impls/hashmap.rs +++ b/src/impls/hashmap.rs @@ -274,9 +274,8 @@ mod tests { case::normal(fxhashmap!{0x11u8 => 0xAABBu16, 0x23u8 => 0xCCDDu16}, Endian::Little, vec![0x11, 0xBB, 0xAA, 0x23, 0xDD, 0xCC]), )] fn test_hashmap_write(input: FxHashMap, endian: Endian, expected: Vec) { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); input.to_writer(&mut writer, endian).unwrap(); - assert_eq!(expected, out_buf); + assert_eq!(expected, writer.inner); } } diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs index f391429f..f9a06a17 100644 --- a/src/impls/hashset.rs +++ b/src/impls/hashset.rs @@ -244,10 +244,9 @@ mod tests { case::normal(vec![0xAABB, 0xCCDD].into_iter().collect(), Endian::Little, vec![0xDD, 0xCC, 0xBB, 0xAA]), )] fn test_hashset_write(input: FxHashSet, endian: Endian, expected: Vec) { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); input.to_writer(&mut writer, endian).unwrap(); - assert_eq!(expected, out_buf); + assert_eq!(expected, writer.inner); } // Note: These tests also exist in boxed.rs @@ -288,11 +287,10 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, out_buf); + assert_eq!(expected_write, writer.inner); } } diff --git a/src/impls/ipaddr.rs b/src/impls/ipaddr.rs index f3886fdb..36e30537 100644 --- a/src/impls/ipaddr.rs +++ b/src/impls/ipaddr.rs @@ -83,10 +83,9 @@ mod tests { let res_read = Ipv4Addr::from_reader_with_ctx(&mut reader, endian).unwrap(); assert_eq!(expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, endian).unwrap(); - assert_eq!(input.to_vec(), out_buf.to_vec()); + assert_eq!(input.to_vec(), writer.inner); } #[rstest(input, endian, expected, @@ -99,31 +98,28 @@ mod tests { let res_read = Ipv6Addr::from_reader_with_ctx(&mut reader, endian).unwrap(); assert_eq!(expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, endian).unwrap(); - assert_eq!(input.to_vec(), out_buf.to_vec()); + assert_eq!(input.to_vec(), writer.inner); } #[test] fn test_ip_addr_write() { let ip_addr = IpAddr::V4(Ipv4Addr::new(145, 254, 160, 237)); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); - assert_eq!(vec![237, 160, 254, 145], out_buf.to_vec()); + assert_eq!(vec![237, 160, 254, 145], writer.inner); let ip_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x02ff)); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); assert_eq!( vec![ 0xff, 0x02, 0x0a, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - out_buf.to_vec() + writer.inner ); } } diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index 5ea5b557..e51c1cda 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -83,9 +83,8 @@ mod tests { let res_read = NonZeroU8::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, ()).unwrap(); - assert_eq!(input.to_vec(), out_buf.to_vec()); + assert_eq!(input.to_vec(), writer.inner); } } diff --git a/src/impls/option.rs b/src/impls/option.rs index 59dfd25a..7800e7dc 100644 --- a/src/impls/option.rs +++ b/src/impls/option.rs @@ -27,7 +27,7 @@ mod tests { use crate::reader::Reader; #[test] - fn test_option() { + fn test_option_read() { use crate::ctx::*; let input = &[1u8, 2, 3, 4]; let mut cursor = Cursor::new(input); @@ -35,4 +35,11 @@ mod tests { let v = Option::::from_reader_with_ctx(&mut reader, Endian::Little).unwrap(); assert_eq!(v, Some(0x04030201)) } + + #[test] + fn test_option_write() { + let mut writer = Writer::new(vec![]); + Some(true).to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![1], writer.inner); + } } diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index 09114ac6..5320b356 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -624,10 +624,9 @@ mod tests { let res_read = <$typ>::from_reader_with_ctx(&mut reader, ENDIAN).unwrap(); assert_eq!($expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, ENDIAN).unwrap(); - assert_eq!($input, out_buf); + assert_eq!($input, writer.inner); } }; } @@ -816,8 +815,7 @@ mod tests { expected: Vec, expected_leftover: Vec, ) { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); match bit_size { Some(bit_size) => input .to_writer(&mut writer, (endian, BitSize(bit_size))) @@ -825,7 +823,7 @@ mod tests { None => input.to_writer(&mut writer, endian).unwrap(), }; assert_eq!(expected_leftover, writer.rest()); - assert_eq!(expected, out_buf); + assert_eq!(expected, writer.inner); } #[rstest(input, endian, byte_size, expected, @@ -837,15 +835,14 @@ mod tests { case::byte_size_le_bigger(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]), )] fn test_byte_writer(input: u32, endian: Endian, byte_size: Option, expected: Vec) { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); match byte_size { Some(byte_size) => input .to_writer(&mut writer, (endian, ByteSize(byte_size))) .unwrap(), None => input.to_writer(&mut writer, endian).unwrap(), }; - assert_hex::assert_eq_hex!(expected, out_buf); + assert_hex::assert_eq_hex!(expected, writer.inner); } #[rstest(input, endian, bit_size, expected, expected_write, @@ -869,15 +866,14 @@ mod tests { }; assert_eq!(expected, res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); match bit_size { Some(bit_size) => res_read .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(), None => res_read.to_writer(&mut writer, endian).unwrap(), }; - assert_hex::assert_eq_hex!(expected_write, out_buf); + assert_hex::assert_eq_hex!(expected_write, writer.inner); } macro_rules! TestSignExtending { diff --git a/src/impls/slice.rs b/src/impls/slice.rs index bae3c403..7554b422 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -94,16 +94,14 @@ mod tests { )] fn test_bit_write(input: [u16; 2], endian: Endian, expected: Vec) { // test writer - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); input.to_writer(&mut writer, endian).unwrap(); - assert_eq!(expected, out_buf.to_vec()); + assert_eq!(expected, writer.inner); // test &slice let input = input.as_ref(); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); input.to_writer(&mut writer, endian).unwrap(); - assert_eq!(expected, out_buf.to_vec()); + assert_eq!(expected, writer.inner); } } diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index fe228f81..677942a5 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -97,9 +97,8 @@ mod tests { where T: DekuWriter, { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); input.to_writer(&mut writer, ()).unwrap(); - assert_eq!(expected, out_buf); + assert_eq!(expected, writer.inner); } } diff --git a/src/impls/unit.rs b/src/impls/unit.rs index b6b4ab3f..7d3c93b4 100644 --- a/src/impls/unit.rs +++ b/src/impls/unit.rs @@ -40,9 +40,8 @@ mod tests { let res_read = <()>::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!((), res_read); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read.to_writer(&mut writer, ()).unwrap(); - assert_eq!(0, out_buf.len()); + assert!(writer.inner.is_empty()); } } diff --git a/src/impls/vec.rs b/src/impls/vec.rs index 5d930384..57e24dbf 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -231,10 +231,9 @@ mod tests { case::normal(vec![0xAABB, 0xCCDD], Endian::Little, vec![0xBB, 0xAA, 0xDD, 0xCC]), )] fn test_vec_write(input: Vec, endian: Endian, expected: Vec) { - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); input.to_writer(&mut writer, endian).unwrap(); - assert_eq!(expected, out_buf.to_vec()); + assert_eq!(expected, writer.inner); } // Note: These tests also exist in boxed.rs @@ -273,12 +272,11 @@ mod tests { input.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut out_buf = vec![]; - let mut writer = Writer::new(&mut out_buf); + let mut writer = Writer::new(vec![]); res_read .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, out_buf.to_vec()); + assert_eq!(expected_write, writer.inner); assert_eq!(input_clone[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/writer.rs b/src/writer.rs index 268c62d4..a8ffc04b 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -75,12 +75,11 @@ impl Writer { count += 1; }); - // SAFETY: This does noto have a safety comment in bitvec. But this is safe + // SAFETY: This does not have a safety comment in bitvec. But this is safe // because of `count` here will always still be within the bounds // of `bits` bits = unsafe { bits.get_unchecked(count * bits_of::()..) }; - // TODO: with_capacity? self.leftover = bits.to_bitvec(); if self.inner.write_all(&buf).is_err() { return Err(DekuError::Write); @@ -107,8 +106,8 @@ impl Writer { #[cfg(feature = "logging")] log::trace!("leftover exists"); - // TODO: we could check here and only send the required bits to finish the byte? - // (instead of sending the entire thing) + // TODO(perf): we could check here and only send the required bits to finish the byte, + // instead of sending the entire thing. The rest would be through self.inner.write. self.write_bits(&BitVec::from_slice(buf))?; } else { if self.inner.write_all(buf).is_err() { @@ -133,8 +132,8 @@ impl Writer { .extend_from_bitslice(&bitvec![u8, Msb0; 0; 8 - self.leftover.len()]); let mut buf = alloc::vec![0x00; self.leftover.len() / 8]; - // write as many leftover to the buffer (as we can, can't write bits just bytes) - // TODO: error if bits are leftover? (not bytes aligned) + // write as many leftover to the buffer. Because of the previous extend, + // this will include all the bits in self.leftover self.leftover .chunks_exact(bits_of::()) .zip(buf.iter_mut()) From 16aef3630bbd64fa184af6bcfc1d8090e683d580 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Sun, 7 Jan 2024 17:47:19 -0500 Subject: [PATCH 9/9] Add slice const_generic testing --- src/impls/slice.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/impls/slice.rs b/src/impls/slice.rs index 7554b422..6bd2458c 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -104,4 +104,61 @@ mod tests { input.to_writer(&mut writer, endian).unwrap(); assert_eq!(expected, writer.inner); } + + #[rstest(input,endian,expected,expected_rest, + case::normal_le( + [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(), + Endian::Little, + [[0xCCDD, 0xAABB], [0x8899, 0x6677]], + bits![u8, Msb0;], + ), + case::normal_le( + [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(), + Endian::Big, + [[0xDDCC, 0xBBAA], [0x9988, 0x7766]], + bits![u8, Msb0;], + ), + )] + fn test_nested_array_bit_read( + input: &[u8], + endian: Endian, + expected: [[u16; 2]; 2], + expected_rest: &BitSlice, + ) { + use no_std_io::io::Cursor; + + use crate::reader::Reader; + + let bit_slice = input.view_bits::(); + + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + let res_read = <[[u16; 2]; 2]>::from_reader_with_ctx(&mut reader, endian).unwrap(); + assert_eq!(expected, res_read); + } + + #[rstest(input,endian,expected, + case::normal_le( + [[0xCCDD, 0xAABB], [0x8899, 0x6677]], + Endian::Little, + vec![0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66], + ), + case::normal_be( + [[0xDDCC, 0xBBAA], [0x9988, 0x7766]], + Endian::Big, + vec![0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66], + ), + )] + fn test_nested_array_bit_write(input: [[u16; 2]; 2], endian: Endian, expected: Vec) { + // test writer + let mut writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); + + // test &slice + let input = input.as_ref(); + let mut writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); + } }