diff --git a/CHANGELOG.md b/CHANGELOG.md index a18b9e83..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,20 +121,20 @@ 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(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id_pat = "_")] VariantC((u8, u8)), } ``` -new: +New: ```rust #[derive(PartialEq, Debug, DekuRead, DekuWrite)] -#[deku(type = "u8")] +#[deku(id_type = "u8")] enum DekuTest { #[deku(id_pat = "_")] VariantC { @@ -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/Cargo.toml b/Cargo.toml index 21c42b18..432138d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,13 +32,14 @@ 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" 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/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 a2a0de3f..13abbe7b 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", )); } @@ -654,11 +656,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` @@ -687,7 +685,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"); @@ -898,7 +896,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) @@ -962,8 +960,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()); @@ -1062,9 +1060,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/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs index 5d76c3f2..1f2afb3d 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -599,7 +599,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 6e0b7154..7ecb47ac 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 { @@ -82,17 +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 acc: ::#crate_::bitvec::BitVec = self.to_bits()?; - Ok(acc.into_vec()) - } - - #[allow(unused_variables)] - fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - #to_bits_body - } - } + impl #imp DekuContainerWrite for #ident #wher {} }); } @@ -122,9 +94,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 +106,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 +172,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 +193,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 +238,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 { @@ -300,17 +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 acc: ::#crate_::bitvec::BitVec = self.to_bits()?; - Ok(acc.into_vec()) - } - - #[allow(unused_variables)] - fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - #to_bits_body - } - } + impl #imp DekuContainerWrite for #ident #wher {} }) } @@ -342,9 +287,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 +299,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 +313,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 +378,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 +390,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 +410,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 +473,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 +496,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)) } } }; @@ -568,11 +521,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 +544,7 @@ fn emit_field_write( (true, None) => { // #[deku(skip)] ==> `skip` quote! { + #skipping_log // skipping, no write } } @@ -598,6 +561,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/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 ee596a00..96966027 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -841,7 +841,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, } @@ -856,9 +856,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, ()) } } @@ -1057,7 +1057,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), @@ -1095,7 +1095,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, @@ -1137,7 +1137,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), @@ -1189,7 +1189,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), @@ -1220,7 +1220,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/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..34fdaba5 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,20 @@ 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 writer = Writer::new(vec![]); + true.to_writer(&mut writer, BitSize(1)).unwrap(); + assert_eq!(vec![true], writer.rest()); + + let mut writer = Writer::new(vec![]); + true.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![1], writer.inner); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(vec![0b01], res_write.into_vec()); + let mut writer = Writer::new(vec![]); + false.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![0], writer.inner); } } diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs index bd733efa..a24a47f4 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,20 +38,32 @@ 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(()) } } +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; @@ -72,6 +73,7 @@ mod tests { use crate::ctx::*; use crate::native_endian; use crate::reader::Reader; + use bitvec::prelude::*; #[rstest(input, expected, case( @@ -85,9 +87,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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), writer.inner); } // Note: Copied tests from vec.rs impl @@ -105,7 +107,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 +128,13 @@ 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 writer = Writer::new(vec![]); 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, 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 d7a4ff56..c2461ab6 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,8 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), writer.inner); } } diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs index c95e41ff..cf636b55 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,8 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, ()).unwrap(); + 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 9d6e4faa..78518103 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 @@ -186,7 +186,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 @@ -194,19 +194,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(()) } @@ -221,6 +223,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 @@ -244,8 +247,12 @@ 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::read_all([0x01, 0xAA].as_ref(), Endian::Little, None, Limit::end(), fxhashmap!{0x01 => 0xAA}, bits![u8, Msb0;], &[]), + 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;], &[]), @@ -295,44 +302,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 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 writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); } } diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs index fc2a69ae..39f61cec 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 @@ -176,7 +176,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 @@ -184,17 +184,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(()) } @@ -202,6 +204,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; @@ -215,8 +218,12 @@ 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::read_all([0xAA, 0xBB].as_ref(), Endian::Little, None, Limit::end(), vec![0xAA, 0xBB].into_iter().collect(), bits![u8, Msb0;], &[]), + 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;], &[]), @@ -264,9 +271,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 res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); } // Note: These tests also exist in boxed.rs @@ -307,10 +314,10 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + let mut writer = Writer::new(vec![]); 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, writer.inner); } } diff --git a/src/impls/ipaddr.rs b/src/impls/ipaddr.rs index 9ad5bf2c..36e30537 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,9 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, endian).unwrap(); + assert_eq!(input.to_vec(), writer.inner); } #[rstest(input, endian, expected, @@ -98,27 +98,28 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, endian).unwrap(); + 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 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 writer = Writer::new(vec![]); + ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); + 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 ret_write = bitvec![u8, Msb0;]; - ip_addr.write(&mut ret_write, Endian::Little).unwrap(); + 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 ], - ret_write.into_vec() + writer.inner ); } } diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index d85b245b..e51c1cda 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,8 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), writer.inner); } } diff --git a/src/impls/option.rs b/src/impls/option.rs index 3eabefac..7800e7dc 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)) } } @@ -38,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); @@ -46,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 63aeeb09..5320b356 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,9 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, ENDIAN).unwrap(); + assert_eq!($input, writer.inner); } }; } @@ -791,23 +800,49 @@ 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 writer = Writer::new(vec![]); 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, writer.inner); + } + + #[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 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, writer.inner); } #[rstest(input, endian, bit_size, expected, expected_write, @@ -831,15 +866,14 @@ mod tests { }; assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; + let mut writer = Writer::new(vec![]); 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, writer.inner); } macro_rules! TestSignExtending { diff --git a/src/impls/slice.rs b/src/impls/slice.rs index adcd290c..b9df3063 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,20 +1,16 @@ //! Implementations of DekuRead and DekuWrite for [T; N] where 0 < N <= 32 -use crate::{DekuError, DekuWrite}; -use bitvec::prelude::*; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; use core::mem::MaybeUninit; -use no_std_io::io::Read; - -use crate::DekuReader; +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,44 +35,44 @@ 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(()) } } -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(()) } @@ -84,15 +80,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::(); @@ -107,47 +105,10 @@ 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()); - } - - #[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); + // test writer + let mut writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); } #[rstest(input,endian,expected, @@ -163,23 +124,15 @@ mod tests { ), )] 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 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 res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); - } - - #[test] - fn test_issue270() { - let num = [1, 1]; - let mut res_write = bitvec![u8, Msb0;]; - num.write(&mut res_write, ()).unwrap(); - <[u8]>::write(num.as_ref(), &mut res_write, ()).unwrap(); - <&[u8]>::write(&num.as_ref(), &mut res_write, ()).unwrap(); + let mut writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); } } diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index d527d39f..677942a5 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,10 @@ 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 writer = Writer::new(vec![]); + input.to_writer(&mut writer, ()).unwrap(); + assert_eq!(expected, writer.inner); } } diff --git a/src/impls/unit.rs b/src/impls/unit.rs index d5e3d895..7d3c93b4 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,8 @@ 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 writer = Writer::new(vec![]); + res_read.to_writer(&mut writer, ()).unwrap(); + assert!(writer.inner.is_empty()); } } diff --git a/src/impls/vec.rs b/src/impls/vec.rs index 3d17976e..664ffff7 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, @@ -72,7 +72,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 @@ -136,7 +136,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 @@ -146,21 +146,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(()) } @@ -168,13 +170,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]), @@ -227,9 +256,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 res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut writer = Writer::new(vec![]); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, writer.inner); } // Note: These tests also exist in boxed.rs @@ -268,11 +297,11 @@ mod tests { input.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + let mut writer = Writer::new(vec![]); 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, writer.inner); assert_eq!(input_clone[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/lib.rs b/src/lib.rs index 64d36de5..30b8dfd1 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, @@ -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 @@ -331,6 +348,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 +365,11 @@ pub mod error; mod impls; pub mod prelude; 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 pub trait DekuReader<'a, Ctx = ()> { @@ -360,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], @@ -373,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 @@ -396,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], @@ -422,27 +443,73 @@ 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 - fn to_bytes(&self) -> Result, DekuError>; + /// + /// ```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> { + 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 - fn to_bits(&self) -> Result, DekuError>; + /// + /// ```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> { + 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 @@ -457,19 +524,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 42a49f11..e8000421 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -225,7 +225,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..a8ffc04b --- /dev/null +++ b/src/writer.rs @@ -0,0 +1,199 @@ +//! 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 { + pub(crate) inner: W, + /// Leftover bits + pub leftover: BitVec, + /// Total bits written + pub bits_written: usize, +} + +impl Writer { + /// Create a new `Writer` + #[inline] + pub fn new(inner: 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. + /// + /// 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")] + 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 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::()..) }; + + 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` + /// + /// 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> { + #[cfg(feature = "logging")] + log::trace!("writing {} bytes: {buf:02x?}", buf.len()); + + if !self.leftover.is_empty() { + #[cfg(feature = "logging")] + log::trace!("leftover exists"); + + // 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() { + 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. Because of the previous extend, + // this will include all the bits in self.leftover + 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_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 3d16fcdf..f945cd34 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, } @@ -50,13 +51,13 @@ 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( #[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,21 +71,22 @@ 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] 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( #[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,14 +231,16 @@ 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] 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_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); +} 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 1ebfc690..a68d7753 100644 --- a/tests/test_compile/cases/enum_validation.stderr +++ b/tests/test_compile/cases/enum_validation.stderr @@ -1,65 +1,65 @@ -error: `type` or `id` must be specified on enum - --> $DIR/enum_validation.rs:5:6 +error: `id_type` or `id` must be specified on enum + --> tests/test_compile/cases/enum_validation.rs:5:6 | 5 | enum Test1 {} | ^^^^^ -error: conflicting: both `type` and `id` specified on enum - --> $DIR/enum_validation.rs:10:6 +error: conflicting: both `id_type` and `id` specified on enum + --> tests/test_compile/cases/enum_validation.rs:10:6 | 10 | enum Test2 {} | ^^^^^ error: conflicting: both `id` and `id_pat` specified on variant - --> $DIR/enum_validation.rs:16:17 + --> tests/test_compile/cases/enum_validation.rs:16:17 | 16 | #[deku(id = "1", id_pat = "2..=3")] | ^^^ -error: `type` only supported on enum - --> $DIR/enum_validation.rs:22:15 +error: `id_type` only supported on enum + --> tests/test_compile/cases/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 + --> tests/test_compile/cases/enum_validation.rs:29:15 | 29 | #[deku(bits = 1)] | ^ error: `bits` only supported on enum - --> $DIR/enum_validation.rs:36:15 + --> tests/test_compile/cases/enum_validation.rs:36:15 | 36 | #[deku(bits = 1)] | ^ -error: `id` only supported on enum - --> $DIR/enum_validation.rs:43:13 +error: `id_type` only supported on enum + --> tests/test_compile/cases/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 + --> tests/test_compile/cases/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 + --> tests/test_compile/cases/enum_validation.rs:59:5 | -58 | enum Test9 { - | ^^^^^ +59 | A, + | ^ error: error: `id_pat` should be used for `_` - --> $DIR/enum_validation.rs:67:5 + --> tests/test_compile/cases/enum_validation.rs:67:5 | 67 | A, | ^ error: DekuRead: `id` must be specified on non-unit variants - --> $DIR/enum_validation.rs:76:5 + --> tests/test_compile/cases/enum_validation.rs:76:5 | 76 | B(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..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` @@ -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_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 dfb3b8f9..a3826dc5 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, } @@ -24,10 +24,10 @@ 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::DekuWrite + for<'a> deku::DekuReader<'a>, + T: deku::DekuWriter + for<'a> deku::DekuReader<'a>, { #[deku(id = "1")] VariantT(T), 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 84ff9904..57e5ac40 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), @@ -370,7 +370,7 @@ fn issue_397() { } #[derive(Debug, Copy, Clone, PartialEq, DekuRead, DekuWrite)] - #[deku(type = "u8", endian = "big")] + #[deku(id_type = "u8", endian = "big")] enum PacketType { #[deku(id = 0)] Zero, 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..1515a403 --- /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(id_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); +} 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)),