diff --git a/Cargo.toml b/Cargo.toml index 00289dbe..e5c28c44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ alloc_counter = "0.0.4" trybuild = "1.0.77" rustc-hash = "1.1.0" env_logger = "0.10.0" +assert_hex = "0.2.2" [[bench]] name = "deku" diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs index 87e28f4d..b6a10e9d 100644 --- a/deku-derive/src/lib.rs +++ b/deku-derive/src/lib.rs @@ -668,7 +668,7 @@ fn apply_replacements(input: &syn::LitStr) -> Result, Repla let input_str = input_value .replace("deku::reader", "__deku_reader") - .replace("deku::output", "__deku_output") // part of the public API `write` + .replace("deku::writer", "__deku_writer") .replace("deku::bit_offset", "__deku_bit_offset") .replace("deku::byte_offset", "__deku_byte_offset"); diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs index 97720071..fdcd40ee 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -603,7 +603,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 87ef27f0..fdb63be4 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -47,26 +47,8 @@ fn emit_struct(input: &DekuData) -> Result { // Implement `DekuContainerWrite` for types that don't need a context if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) { - let to_bits_body = wrap_default_ctx( - quote! { - match *self { - #destructured => { - let mut __deku_acc: ::#crate_::bitvec::BitVec = ::#crate_::bitvec::BitVec::new(); - let __deku_output = &mut __deku_acc; - - #magic_write - #(#field_writes)* - - Ok(__deku_acc) - } - } - }, - &input.ctx, - &input.ctx_default, - ); - tokens.extend(quote! { - impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { + impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec #wher { type Error = ::#crate_::DekuError; fn try_from(input: #ident) -> core::result::Result { @@ -84,13 +66,22 @@ fn emit_struct(input: &DekuData) -> Result { impl #imp DekuContainerWrite for #ident #wher { fn to_bytes(&self) -> core::result::Result, ::#crate_::DekuError> { - let mut acc: ::#crate_::bitvec::BitVec = self.to_bits()?; - Ok(acc.into_vec()) + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + __deku_writer.finalize()?; + Ok(out_buf) } #[allow(unused_variables)] fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - #to_bits_body + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + let mut leftover = __deku_writer.leftover; + let mut bv = ::#crate_::bitvec::BitVec::from_slice(&out_buf); + bv.append(&mut leftover); + Ok(bv) } } }); @@ -122,9 +113,9 @@ fn emit_struct(input: &DekuData) -> Result { } } - impl #imp DekuWrite<#ctx_types> for #ident #wher { + impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher { #[allow(unused_variables)] - fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> { + fn to_writer(&self, __deku_writer: &mut ::#crate_::writer::Writer, #ctx_arg) -> core::result::Result<(), ::#crate_::DekuError> { #write_body } } @@ -134,9 +125,9 @@ fn emit_struct(input: &DekuData) -> Result { let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default); tokens.extend(quote! { - impl #imp DekuWrite for #ident #wher { + impl #imp ::#crate_::DekuWriter for #ident #wher { #[allow(unused_variables)] - fn write(&self, __deku_output: &mut ::#crate_::bitvec::BitVec, _: ()) -> core::result::Result<(), ::#crate_::DekuError> { + fn to_writer(&self, __deku_writer: &mut ::#crate_::writer::Writer, _: ()) -> core::result::Result<(), ::#crate_::DekuError> { #write_body } } @@ -200,13 +191,13 @@ 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::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))?; } } } @@ -215,7 +206,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( @@ -260,25 +251,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 { @@ -296,13 +270,22 @@ fn emit_enum(input: &DekuData) -> Result { impl #imp DekuContainerWrite for #ident #wher { fn to_bytes(&self) -> core::result::Result, ::#crate_::DekuError> { - let mut acc: ::#crate_::bitvec::BitVec = self.to_bits()?; - Ok(acc.into_vec()) + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + __deku_writer.finalize()?; + Ok(out_buf) } #[allow(unused_variables)] fn to_bits(&self) -> core::result::Result<::#crate_::bitvec::BitVec, ::#crate_::DekuError> { - #to_bits_body + let mut out_buf = vec![]; + let mut __deku_writer = ::#crate_::writer::Writer::new(&mut out_buf); + ::#crate_::DekuWriter::to_writer(self, &mut __deku_writer, ())?; + let mut leftover = __deku_writer.leftover; + let mut bv = ::#crate_::bitvec::BitVec::from_slice(&out_buf); + bv.append(&mut leftover); + Ok(bv) } } }) @@ -336,9 +319,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 } } @@ -348,9 +331,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 } } @@ -362,9 +345,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! {} @@ -426,7 +410,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 @@ -438,7 +422,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 @@ -458,8 +442,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())?; } } } @@ -522,6 +505,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 { @@ -537,13 +528,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)) } } }; @@ -592,6 +583,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..7199f610 100644 --- a/examples/custom_reader_and_writer.rs +++ b/examples/custom_reader_and_writer.rs @@ -2,7 +2,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 +26,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 +44,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 +53,7 @@ struct DekuTest { #[deku( reader = "bit_flipper_read(*field_a, deku::reader, BitSize(8))", - writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))" + writer = "bit_flipper_write(*field_a, *field_b, deku::writer, BitSize(8))" )] field_b: u8, } diff --git a/src/attributes.rs b/src/attributes.rs index 16b0028f..048dfc65 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -803,7 +803,7 @@ use deku::prelude::*; struct DekuTest { #[deku( reader = "DekuTest::read(deku::reader)", - writer = "DekuTest::write(deku::output, &self.field_a)" + writer = "DekuTest::write(deku::writer, &self.field_a)" )] field_a: String, } @@ -818,9 +818,9 @@ impl DekuTest { } /// Parse from String to u8 and write - fn write(output: &mut BitVec, field_a: &str) -> Result<(), DekuError> { + fn write(writer: &mut Writer, field_a: &str) -> Result<(), DekuError> { let value = field_a.parse::().unwrap(); - value.write(output, ()) + value.to_writer(writer, ()) } } diff --git a/src/error.rs b/src/error.rs index 061f58dc..f39e1a20 100644 --- a/src/error.rs +++ b/src/error.rs @@ -44,6 +44,8 @@ pub enum DekuError { Assertion(String), /// Could not resolve `id` for variant IdVariantNotFound, + /// IO error while writing + Write, } impl From for DekuError { @@ -78,6 +80,7 @@ impl core::fmt::Display for DekuError { DekuError::Unexpected(ref err) => write!(f, "Unexpected error: {err}"), DekuError::Assertion(ref err) => write!(f, "Assertion error: {err}"), DekuError::IdVariantNotFound => write!(f, "Could not resolve `id` for variant"), + DekuError::Write => write!(f, "write error"), } } } @@ -100,6 +103,7 @@ impl From for std::io::Error { DekuError::Unexpected(_) => io::Error::new(io::ErrorKind::Other, error), DekuError::Assertion(_) => io::Error::new(io::ErrorKind::InvalidData, error), DekuError::IdVariantNotFound => io::Error::new(io::ErrorKind::NotFound, error), + DekuError::Write => io::Error::new(io::ErrorKind::BrokenPipe, error), } } } diff --git a/src/impls/bool.rs b/src/impls/bool.rs index 71e85a16..eac90ea5 100644 --- a/src/impls/bool.rs +++ b/src/impls/bool.rs @@ -1,11 +1,11 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; #[cfg(feature = "alloc")] use alloc::format; -use bitvec::prelude::*; - -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, Ctx> DekuReader<'a, Ctx> for bool where @@ -13,7 +13,7 @@ where u8: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let val = u8::from_reader_with_ctx(reader, inner_ctx)?; @@ -28,15 +28,15 @@ where } } -impl DekuWrite for bool +impl DekuWriter for bool where - u8: DekuWrite, + u8: DekuWriter, { /// wrapper around u8::write with consideration to context, such as bit size - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { match self { - true => (0x01u8).write(output, inner_ctx), - false => (0x00u8).write(output, inner_ctx), + true => (0x01u8).to_writer(writer, inner_ctx), + false => (0x00u8).to_writer(writer, inner_ctx), } } } @@ -47,7 +47,7 @@ mod tests { use no_std_io::io::Cursor; use rstest::rstest; - use crate::reader::Reader; + use crate::{ctx::BitSize, reader::Reader}; use super::*; @@ -72,9 +72,23 @@ mod tests { let mut reader = Reader::new(&mut cursor); let res_read = bool::from_reader_with_ctx(&mut reader, crate::ctx::BitSize(2)).unwrap(); assert!(res_read); + } + + #[test] + fn test_writer() { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + true.to_writer(&mut writer, BitSize(1)).unwrap(); + assert_eq!(vec![true], writer.rest()); + + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + true.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![1], out_buf); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(vec![0b01], res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + false.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![0], out_buf); } } diff --git a/src/impls/boxed.rs b/src/impls/boxed.rs index bd733efa..565dc8a8 100644 --- a/src/impls/boxed.rs +++ b/src/impls/boxed.rs @@ -1,12 +1,12 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use alloc::boxed::Box; use alloc::vec::Vec; -use bitvec::prelude::*; - use crate::ctx::Limit; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, T, Ctx> DekuReader<'a, Ctx> for Box where @@ -14,7 +14,7 @@ where Ctx: Copy, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let val = ::from_reader_with_ctx(reader, inner_ctx)?; @@ -22,17 +22,6 @@ where } } -impl DekuWrite for Box -where - T: DekuWrite, - Ctx: Copy, -{ - /// Write T from box - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { - self.as_ref().write(output, inner_ctx) - } -} - impl<'a, T, Ctx, Predicate> DekuReader<'a, (Limit, Ctx)> for Box<[T]> where T: DekuReader<'a, Ctx>, @@ -40,7 +29,7 @@ where Predicate: FnMut(&T) -> bool, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, (limit, inner_ctx): (Limit, Ctx), ) -> Result { // use Vec's implementation and convert to Box<[T]> @@ -49,15 +38,15 @@ where } } -impl DekuWrite for Box<[T]> +impl DekuWriter for Box<[T]> where - T: DekuWrite, + T: DekuWriter, Ctx: Copy, { /// Write all `T`s to bits - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { for v in self.as_ref() { - v.write(output, ctx)?; + v.to_writer(writer, ctx)?; } Ok(()) } @@ -72,6 +61,7 @@ mod tests { use crate::ctx::*; use crate::native_endian; use crate::reader::Reader; + use bitvec::prelude::*; #[rstest(input, expected, case( @@ -85,9 +75,10 @@ mod tests { let res_read = >::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } // Note: Copied tests from vec.rs impl @@ -105,7 +96,7 @@ mod tests { bit_size: Option, limit: Limit, expected: Box<[u16]>, - expected_rest_bits: &BitSlice, + expected_rest_bits: &bitvec::slice::BitSlice, expected_rest_bytes: &[u8], expected_write: Vec, ) { @@ -126,11 +117,14 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + assert_eq!(input[..expected_write.len()].to_vec(), expected_write); + + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + assert_eq!(expected_write, out_buf.to_vec()); assert_eq!(input[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/impls/cow.rs b/src/impls/cow.rs index d7a4ff56..078bf4da 100644 --- a/src/impls/cow.rs +++ b/src/impls/cow.rs @@ -1,10 +1,10 @@ use std::borrow::{Borrow, Cow}; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use bitvec::prelude::*; - -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, T, Ctx> DekuReader<'a, Ctx> for Cow<'a, T> where @@ -12,7 +12,7 @@ where Ctx: Copy, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let val = ::from_reader_with_ctx(reader, inner_ctx)?; @@ -20,14 +20,14 @@ where } } -impl DekuWrite for Cow<'_, T> +impl DekuWriter for Cow<'_, T> where - T: DekuWrite + Clone, + T: DekuWriter + Clone, Ctx: Copy, { /// Write T from Cow - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { - (self.borrow() as &T).write(output, inner_ctx) + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { + (self.borrow() as &T).to_writer(writer, inner_ctx) } } @@ -51,8 +51,9 @@ mod tests { let res_read = >::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } } diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs index c95e41ff..34c46337 100644 --- a/src/impls/cstring.rs +++ b/src/impls/cstring.rs @@ -1,18 +1,18 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use std::ffi::CString; -use bitvec::prelude::*; - +use crate::reader::Reader; +use crate::writer::Writer; use crate::{ctx::*, DekuReader}; -use crate::{DekuError, DekuWrite}; +use crate::{DekuError, DekuWriter}; -impl DekuWrite for CString +impl DekuWriter for CString where - u8: DekuWrite, + u8: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let bytes = self.as_bytes_with_nul(); - bytes.write(output, ctx) + bytes.to_writer(writer, ctx) } } @@ -21,7 +21,7 @@ where u8: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let bytes = @@ -67,8 +67,9 @@ mod tests { cursor.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest, buf); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(vec![b't', b'e', b's', b't', b'\0'], res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(vec![b't', b'e', b's', b't', b'\0'], out_buf.to_vec()); } } diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs index b51ea801..96d6793c 100644 --- a/src/impls/hashmap.rs +++ b/src/impls/hashmap.rs @@ -1,11 +1,11 @@ use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; -use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; use crate::ctx::*; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; /// Read `K, V`s into a hashmap until a given predicate returns true /// * `capacity` - an optional capacity to pre-allocate the hashmap with @@ -159,7 +159,7 @@ where } } -impl, V: DekuWrite, S, Ctx: Copy> DekuWrite for HashMap { +impl, V: DekuWriter, S, Ctx: Copy> DekuWriter for HashMap { /// Write all `K, V`s in a `HashMap` to bits. /// * **inner_ctx** - The context required by `K, V`. /// Note: depending on the Hasher `S`, the order in which the `K, V` pairs are @@ -167,19 +167,21 @@ impl, V: DekuWrite, S, Ctx: Copy> DekuWrite for Hash /// instead of the default RandomState hasher if you don't want the order written to change. /// # Examples /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; + /// # use deku::{ctx::Endian, DekuWriter}; + /// # use deku::writer::Writer; /// # use deku::bitvec::{Msb0, bitvec}; /// # use std::collections::HashMap; - /// let mut output = bitvec![u8, Msb0;]; + /// let mut out_buf = vec![]; + /// let mut writer = Writer::new(&mut out_buf); /// let mut map = HashMap::::default(); /// map.insert(100, 0x04030201); - /// map.write(&mut output, Endian::Big).unwrap(); + /// map.to_writer(&mut writer, Endian::Big).unwrap(); /// let expected: Vec = vec![100, 4, 3, 2, 1]; - /// assert_eq!(expected, output.into_vec()) + /// assert_eq!(expected, out_buf); /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { for kv in self { - kv.write(output, inner_ctx)?; + kv.to_writer(writer, inner_ctx)?; } Ok(()) } @@ -194,6 +196,7 @@ mod tests { use crate::reader::Reader; use super::*; + use bitvec::prelude::*; // Macro to create a deterministic HashMap for tests // This is needed for tests since the default HashMap Hasher @@ -267,44 +270,9 @@ mod tests { case::normal(fxhashmap!{0x11u8 => 0xAABBu16, 0x23u8 => 0xCCDDu16}, Endian::Little, vec![0x11, 0xBB, 0xAA, 0x23, 0xDD, 0xCC]), )] fn test_hashmap_write(input: FxHashMap, endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); - } - - // Note: These tests also exist in boxed.rs - #[rstest(input, endian, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write, - case::normal_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, 2.into(), fxhashmap!{0xBBAA => 0, 0xDDCC => 0}, bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0, 0xAA, 0xBB, 0]), - case::normal_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, 2.into(), fxhashmap!{0xAABB => 0, 0xCCDD => 0}, bits![u8, Msb0;], &[], vec![0xCC, 0xDD, 0, 0xAA, 0xBB, 0]), - case::predicate_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, (|kv: &(u16, u8)| kv.0 == 0xBBAA && kv.1 == 0).into(), fxhashmap!{0xBBAA => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - case::predicate_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, (|kv: &(u16, u8)| kv.0 == 0xAABB && kv.1 == 0).into(), fxhashmap!{0xAABB => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - case::bytes_le([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Little, BitSize(24).into(), fxhashmap!{0xBBAA => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - case::bytes_be([0xAA, 0xBB, 0, 0xCC, 0xDD, 0].as_ref(), Endian::Big, BitSize(24).into(), fxhashmap!{0xAABB => 0}, bits![u8, Msb0;], &[0xcc, 0xdd, 0], vec![0xAA, 0xBB, 0]), - )] - fn test_hashmap_read_write bool + Copy>( - input: &[u8], - endian: Endian, - limit: Limit<(u16, u8), Predicate>, - expected: FxHashMap, - expected_rest_bits: &BitSlice, - expected_rest_bytes: &[u8], - expected_write: Vec, - ) { - let mut cursor = Cursor::new(input); - let mut reader = Reader::new(&mut cursor); - let res_read = - FxHashMap::::from_reader_with_ctx(&mut reader, (limit, endian)).unwrap(); - assert_eq!(expected, res_read); - assert_eq!( - reader.rest(), - expected_rest_bits.iter().by_vals().collect::>() - ); - let mut buf = vec![]; - cursor.read_to_end(&mut buf).unwrap(); - assert_eq!(expected_rest_bytes, buf); - - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, endian).unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf); } } diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs index 2e1fbd7e..c7da372c 100644 --- a/src/impls/hashset.rs +++ b/src/impls/hashset.rs @@ -1,11 +1,11 @@ use std::collections::HashSet; use std::hash::{BuildHasher, Hash}; -use bitvec::prelude::*; -use no_std_io::io::Read; +use crate::writer::Writer; +use no_std_io::io::{Read, Write}; use crate::ctx::*; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::{DekuError, DekuReader, DekuWriter}; /// Read `T`s into a hashset until a given predicate returns true /// * `capacity` - an optional capacity to pre-allocate the hashset with @@ -150,7 +150,7 @@ impl<'a, T: DekuReader<'a> + Eq + Hash, S: BuildHasher + Default, Predicate: FnM } } -impl, S, Ctx: Copy> DekuWrite for HashSet { +impl, S, Ctx: Copy> DekuWriter for HashSet { /// Write all `T`s in a `HashSet` to bits. /// * **inner_ctx** - The context required by `T`. /// Note: depending on the Hasher `S`, the order in which the `T`'s are @@ -158,17 +158,19 @@ impl, S, Ctx: Copy> DekuWrite for HashSet { /// instead of the default RandomState hasher if you don't want the order written to change. /// # Examples /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; + /// # use deku::{ctx::Endian, DekuWriter}; + /// # use deku::writer::Writer; /// # use deku::bitvec::{Msb0, bitvec}; /// # use std::collections::HashSet; + /// let mut out_buf = vec![]; + /// let mut writer = Writer::new(&mut out_buf); /// let set: HashSet = vec![1].into_iter().collect(); - /// let mut output = bitvec![u8, Msb0;]; - /// set.write(&mut output, Endian::Big).unwrap(); - /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1]) + /// set.to_writer(&mut writer, Endian::Big).unwrap(); + /// assert_eq!(out_buf, vec![1]); /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { for v in self { - v.write(output, inner_ctx)?; + v.to_writer(writer, inner_ctx)?; } Ok(()) } @@ -176,11 +178,13 @@ 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; use crate::reader::Reader; + use bitvec::prelude::*; use super::*; @@ -237,9 +241,10 @@ mod tests { case::normal(vec![0xAABB, 0xCCDD].into_iter().collect(), Endian::Little, vec![0xDD, 0xCC, 0xBB, 0xAA]), )] fn test_hashset_write(input: FxHashSet, endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + //let out_buf = vec![]; + //let mut writer = Writer::new(out_buf); + //input.to_writer(&mut writer, endian).unwrap(); + //assert_eq!(expected, out_buf); } // Note: These tests also exist in boxed.rs @@ -280,10 +285,10 @@ mod tests { 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, BitSize(bit_size))) - .unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + //let mut res_write = bitvec![u8, Msb0;]; + //res_read + // .write(&mut res_write, (endian, BitSize(bit_size))) + // .unwrap(); + //assert_eq!(expected_write, res_write.into_vec()); } } diff --git a/src/impls/ipaddr.rs b/src/impls/ipaddr.rs index 9ad5bf2c..f3886fdb 100644 --- a/src/impls/ipaddr.rs +++ b/src/impls/ipaddr.rs @@ -1,17 +1,17 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use bitvec::prelude::*; - -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; impl<'a, Ctx> DekuReader<'a, Ctx> for Ipv4Addr where u32: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let ip = u32::from_reader_with_ctx(reader, inner_ctx)?; @@ -19,13 +19,13 @@ where } } -impl DekuWrite for Ipv4Addr +impl DekuWriter for Ipv4Addr where - u32: DekuWrite, + u32: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let ip: u32 = (*self).into(); - ip.write(output, ctx) + ip.to_writer(writer, ctx) } } @@ -34,7 +34,7 @@ where u128: DekuReader<'a, Ctx>, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, inner_ctx: Ctx, ) -> Result { let ip = u128::from_reader_with_ctx(reader, inner_ctx)?; @@ -42,25 +42,25 @@ where } } -impl DekuWrite for Ipv6Addr +impl DekuWriter for Ipv6Addr where - u128: DekuWrite, + u128: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let ip: u128 = (*self).into(); - ip.write(output, ctx) + ip.to_writer(writer, ctx) } } -impl DekuWrite for IpAddr +impl DekuWriter for IpAddr where - Ipv6Addr: DekuWrite, - Ipv4Addr: DekuWrite, + Ipv6Addr: DekuWriter, + Ipv4Addr: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { match self { - IpAddr::V4(ipv4) => ipv4.write(output, ctx), - IpAddr::V6(ipv6) => ipv6.write(output, ctx), + IpAddr::V4(ipv4) => ipv4.to_writer(writer, ctx), + IpAddr::V6(ipv6) => ipv6.to_writer(writer, ctx), } } } @@ -83,9 +83,10 @@ mod tests { let res_read = Ipv4Addr::from_reader_with_ctx(&mut reader, endian).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, endian).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, endian).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } #[rstest(input, endian, expected, @@ -98,27 +99,31 @@ mod tests { let res_read = Ipv6Addr::from_reader_with_ctx(&mut reader, endian).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, endian).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, endian).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } #[test] fn test_ip_addr_write() { let ip_addr = IpAddr::V4(Ipv4Addr::new(145, 254, 160, 237)); - let mut ret_write = bitvec![u8, Msb0;]; - ip_addr.write(&mut ret_write, Endian::Little).unwrap(); - assert_eq!(vec![237, 160, 254, 145], ret_write.into_vec()); + + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); + assert_eq!(vec![237, 160, 254, 145], out_buf.to_vec()); let ip_addr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x02ff)); - let mut ret_write = bitvec![u8, Msb0;]; - ip_addr.write(&mut ret_write, Endian::Little).unwrap(); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ip_addr.to_writer(&mut writer, Endian::Little).unwrap(); assert_eq!( vec![ 0xff, 0x02, 0x0a, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ], - ret_write.into_vec() + out_buf.to_vec() ); } } diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index d85b245b..5ea5b557 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -1,18 +1,18 @@ #[cfg(feature = "alloc")] use alloc::format; use core::num::*; -use no_std_io::io::Read; - -use bitvec::prelude::*; +use no_std_io::io::{Read, Write}; use crate::ctx::*; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; macro_rules! ImplDekuTraitsCtx { ($typ:ty, $readtype:ty, $ctx_arg:tt, $ctx_type:tt) => { impl DekuReader<'_, $ctx_type> for $typ { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, $ctx_arg: $ctx_type, ) -> Result { let value = <$readtype>::from_reader_with_ctx(reader, $ctx_arg)?; @@ -25,14 +25,14 @@ macro_rules! ImplDekuTraitsCtx { } } - impl DekuWrite<$ctx_type> for $typ { - fn write( + impl DekuWriter<$ctx_type> for $typ { + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, $ctx_arg: $ctx_type, ) -> Result<(), DekuError> { let value = self.get(); - value.write(output, $ctx_arg) + value.to_writer(writer, $ctx_arg) } } }; @@ -68,6 +68,7 @@ mod tests { use crate::reader::Reader; use super::*; + use bitvec::prelude::*; #[rstest(input, expected, case(&hex!("FF"), NonZeroU8::new(0xFF).unwrap()), @@ -82,8 +83,9 @@ mod tests { let res_read = NonZeroU8::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(input.to_vec(), res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(input.to_vec(), out_buf.to_vec()); } } diff --git a/src/impls/option.rs b/src/impls/option.rs index 3eabefac..59dfd25a 100644 --- a/src/impls/option.rs +++ b/src/impls/option.rs @@ -1,7 +1,6 @@ -use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::{writer::Writer, DekuError, DekuReader, DekuWriter}; impl<'a, T: DekuReader<'a, Ctx>, Ctx: Copy> DekuReader<'a, Ctx> for Option { fn from_reader_with_ctx( @@ -13,20 +12,10 @@ impl<'a, T: DekuReader<'a, Ctx>, Ctx: Copy> DekuReader<'a, Ctx> for Option { } } -impl, Ctx: Copy> DekuWrite for Option { - /// Write T if Some - /// * **inner_ctx** - The context required by `T`. - /// # Examples - /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; - /// # use deku::bitvec::{bitvec, Msb0}; - /// let data = Some(1u8); - /// let mut output = bitvec![u8, Msb0;]; - /// data.write(&mut output, Endian::Big).unwrap(); - /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1]) - /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { - self.as_ref().map_or(Ok(()), |v| v.write(output, inner_ctx)) +impl, Ctx: Copy> DekuWriter for Option { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { + self.as_ref() + .map_or(Ok(()), |v| v.to_writer(writer, inner_ctx)) } } diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index 63aeeb09..6d0fbe69 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,81 +449,65 @@ 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 { + let mut input = match endian { Endian::Little => self.to_le_bytes(), 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(); + for b in &mut input[..size.0 as usize] { + writer.write_bytes(&[*b])?; } } 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_bytes(&input[..size.0 as usize])?; } Ok(()) } } - // Only have `endian`, return all input - impl DekuWrite for $typ { - #[inline] - fn write( + impl DekuWriter for $typ { + #[inline(always)] + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, endian: Endian, ) -> Result<(), DekuError> { let input = match endian { Endian::Little => self.to_le_bytes(), Endian::Big => self.to_be_bytes(), }; - output.extend_from_bitslice(input.view_bits::()); + writer.write_bytes(&input)?; Ok(()) } } @@ -505,34 +516,32 @@ macro_rules! ImplDekuWrite { macro_rules! ForwardDekuWrite { ($typ:ty) => { - // Only have `bit_size`, set `endian` to `Endian::default`. - impl DekuWrite for $typ { + impl DekuWriter for $typ { #[inline] - fn write( + fn to_writer( &self, - output: &mut BitVec, + writer: &mut Writer, bit_size: BitSize, ) -> Result<(), DekuError> { - <$typ>::write(self, output, (Endian::default(), bit_size)) + <$typ>::to_writer(self, writer, (Endian::default(), bit_size)) } } - // Only have `bit_size`, set `endian` to `Endian::default`. - impl DekuWrite for $typ { + impl DekuWriter for $typ { #[inline] - fn write( + fn to_writer( &self, - output: &mut BitVec, - bit_size: ByteSize, + writer: &mut Writer, + byte_size: ByteSize, ) -> Result<(), DekuError> { - <$typ>::write(self, output, (Endian::default(), bit_size)) + <$typ>::to_writer(self, writer, (Endian::default(), byte_size)) } } - impl DekuWrite for $typ { + impl DekuWriter for $typ { #[inline] - fn write(&self, output: &mut BitVec, _: ()) -> Result<(), DekuError> { - <$typ>::write(self, output, Endian::default()) + fn to_writer(&self, writer: &mut Writer, _: ()) -> Result<(), DekuError> { + <$typ>::to_writer(self, writer, Endian::default()) } } }; @@ -615,9 +624,10 @@ mod tests { let res_read = <$typ>::from_reader_with_ctx(&mut reader, ENDIAN).unwrap(); assert_eq!($expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ENDIAN).unwrap(); - assert_eq!($input, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ENDIAN).unwrap(); + assert_eq!($input, out_buf); } }; } @@ -791,23 +801,51 @@ mod tests { assert_eq!(expected_rest_bytes, buf); } - #[rstest(input, endian, bit_size, expected, - case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD]), - case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA]), - case::bit_size_le_smaller(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]), - case::bit_size_be_smaller(0x03AB, Endian::Big, Some(10), vec![0b11_1010_10, 0b11_000000]), + #[rstest(input, endian, bit_size, expected, expected_leftover, + case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD], vec![]), + case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA], vec![]), + case::bit_size_le_smaller(0x03AB, Endian::Little, Some(10), vec![0xAB], vec![true, true]), + case::bit_size_be_smaller(0x03AB, Endian::Big, Some(10), vec![0b11_1010_10], vec![true, true]), #[should_panic(expected = "InvalidParam(\"bit size 100 is larger than input 32\")")] - case::bit_size_le_bigger(0x03AB, Endian::Little, Some(100), vec![0xAB, 0b11_000000]), + case::bit_size_le_bigger(0x03AB, Endian::Little, Some(100), vec![0xAB, 0b11_000000], vec![true, true]), )] - fn test_bit_write(input: u32, endian: Endian, bit_size: Option, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; + fn test_bit_writer( + input: u32, + endian: Endian, + bit_size: Option, + expected: Vec, + expected_leftover: Vec, + ) { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); match bit_size { Some(bit_size) => input - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) + .unwrap(), + None => input.to_writer(&mut writer, endian).unwrap(), + }; + assert_eq!(expected_leftover, writer.rest()); + assert_eq!(expected, out_buf); + } + + #[rstest(input, endian, byte_size, expected, + case::normal_le(0xDDCC_BBAA, Endian::Little, None, vec![0xAA, 0xBB, 0xCC, 0xDD]), + case::normal_be(0xDDCC_BBAA, Endian::Big, None, vec![0xDD, 0xCC, 0xBB, 0xAA]), + case::bit_size_le_smaller(0x00ffABAA, Endian::Little, Some(2), vec![0xaa, 0xab]), + case::bit_size_be_smaller(0x00ffABAA, Endian::Big, Some(2), vec![0x00, 0xff]), + #[should_panic(expected = "InvalidParam(\"byte size 10 is larger then input 4\")")] + case::bit_size_le_bigger(0x03AB, Endian::Little, Some(10), vec![0xAB, 0b11_000000]), + )] + fn test_byte_writer(input: u32, endian: Endian, byte_size: Option, expected: Vec) { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + match byte_size { + Some(byte_size) => input + .to_writer(&mut writer, (endian, ByteSize(byte_size))) .unwrap(), - None => input.write(&mut res_write, endian).unwrap(), + None => input.to_writer(&mut writer, endian).unwrap(), }; - assert_eq!(expected, res_write.into_vec()); + assert_hex::assert_eq_hex!(expected, out_buf); } #[rstest(input, endian, bit_size, expected, expected_write, @@ -831,15 +869,15 @@ mod tests { }; assert_eq!(expected, res_read); - let mut res_write = bitvec![u8, Msb0;]; + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); match bit_size { Some(bit_size) => res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(), - None => res_read.write(&mut res_write, endian).unwrap(), + None => res_read.to_writer(&mut writer, endian).unwrap(), }; - - assert_eq!(expected_write, res_write.into_vec()); + assert_hex::assert_eq_hex!(expected_write, out_buf); } macro_rules! TestSignExtending { diff --git a/src/impls/slice.rs b/src/impls/slice.rs index a6b60408..a2ee8ca7 100644 --- a/src/impls/slice.rs +++ b/src/impls/slice.rs @@ -1,20 +1,18 @@ //! Implementations of DekuRead and DekuWrite for [T; N] where 0 < N <= 32 -use crate::{DekuError, DekuWrite}; -use bitvec::prelude::*; -use core::mem::MaybeUninit; -use no_std_io::io::Read; +pub use deku_derive::*; -use crate::DekuReader; +use crate::reader::Reader; +use crate::writer::Writer; +use crate::{DekuError, DekuReader, DekuWriter}; +use core::mem::MaybeUninit; +use no_std_io::io::{Read, Write}; impl<'a, Ctx: Copy, T, const N: usize> DekuReader<'a, Ctx> for [T; N] where T: DekuReader<'a, Ctx>, { - fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, - ctx: Ctx, - ) -> Result + fn from_reader_with_ctx(reader: &mut Reader, ctx: Ctx) -> Result where Self: Sized, { @@ -46,25 +44,25 @@ where } } -impl DekuWrite for [T; N] +impl DekuWriter for [T; N] where - T: DekuWrite, + T: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { for v in self { - v.write(output, ctx)?; + v.to_writer(writer, ctx)?; } Ok(()) } } -impl DekuWrite for &[T] +impl DekuWriter for &[T] where - T: DekuWrite, + T: DekuWriter, { - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { for v in *self { - v.write(output, ctx)?; + v.to_writer(writer, ctx)?; } Ok(()) } @@ -72,11 +70,11 @@ 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]), @@ -95,48 +93,18 @@ 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 writer + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); // test &slice let input = input.as_ref(); - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); - } - - #[cfg(feature = "const_generics")] - #[rstest(input,endian,expected,expected_rest, - case::normal_le( - [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(), - Endian::Little, - [[0xCCDD, 0xAABB], [0x8899, 0x6677]], - bits![u8, Msb0;], - ), - case::normal_le( - [0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66].as_ref(), - Endian::Big, - [[0xDDCC, 0xBBAA], [0x9988, 0x7766]], - bits![u8, Msb0;], - ), - )] - fn test_nested_array_bit_read( - input: &[u8], - endian: Endian, - expected: [[u16; 2]; 2], - expected_rest: &BitSlice, - ) { - use no_std_io::io::Cursor; - - use crate::reader::Reader; - - let bit_slice = input.view_bits::(); - - let mut cursor = Cursor::new(input); - let mut reader = Reader::new(&mut cursor); - let res_read = <[[u16; 2]; 2]>::from_reader_with_ctx(&mut reader, endian).unwrap(); - assert_eq!(expected, res_read); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); } #[cfg(feature = "const_generics")] @@ -162,5 +130,17 @@ mod tests { 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 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 out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); } } diff --git a/src/impls/tuple.rs b/src/impls/tuple.rs index d527d39f..fe228f81 100644 --- a/src/impls/tuple.rs +++ b/src/impls/tuple.rs @@ -1,9 +1,10 @@ //! Implementations of DekuRead and DekuWrite for tuples of length 1 to 11 -use bitvec::prelude::*; -use no_std_io::io::Read; +use crate::writer::Writer; -use crate::{DekuError, DekuReader, DekuWrite}; +use no_std_io::io::{Read, Write}; + +use crate::{DekuError, DekuReader, DekuWriter}; // Trait to help us build intermediate tuples while DekuRead'ing each element // from the tuple @@ -54,13 +55,13 @@ macro_rules! ImplDekuTupleTraits { } } - impl),+> DekuWrite for ($($T,)+) + impl),+> DekuWriter for ($($T,)+) { #[allow(non_snake_case)] - fn write(&self, output: &mut BitVec, ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, ctx: Ctx) -> Result<(), DekuError> { let ($(ref $T,)+) = *self; $( - $T.write(output, ctx)?; + $T.to_writer(writer, ctx)?; )+ Ok(()) } @@ -94,10 +95,11 @@ mod tests { )] fn test_tuple_write(input: T, expected: Vec) where - T: DekuWrite, + T: DekuWriter, { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, ()).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, ()).unwrap(); + assert_eq!(expected, out_buf); } } diff --git a/src/impls/unit.rs b/src/impls/unit.rs index d5e3d895..b6b4ab3f 100644 --- a/src/impls/unit.rs +++ b/src/impls/unit.rs @@ -1,20 +1,23 @@ -use bitvec::prelude::*; -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; -use crate::{DekuError, DekuReader, DekuWrite}; +use crate::{reader::Reader, writer::Writer, DekuError, DekuReader, DekuWriter}; impl DekuReader<'_, Ctx> for () { fn from_reader_with_ctx( - _reader: &mut crate::reader::Reader, + _reader: &mut Reader, _inner_ctx: Ctx, ) -> Result { Ok(()) } } -impl DekuWrite for () { +impl DekuWriter for () { /// NOP on write - fn write(&self, _output: &mut BitVec, _inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer( + &self, + _writer: &mut Writer, + _inner_ctx: Ctx, + ) -> Result<(), DekuError> { Ok(()) } } @@ -37,8 +40,9 @@ mod tests { let res_read = <()>::from_reader_with_ctx(&mut reader, ()).unwrap(); assert_eq!((), res_read); - let mut res_write = bitvec![u8, Msb0;]; - res_read.write(&mut res_write, ()).unwrap(); - assert_eq!(0, res_write.len()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + res_read.to_writer(&mut writer, ()).unwrap(); + assert_eq!(0, out_buf.len()); } } diff --git a/src/impls/vec.rs b/src/impls/vec.rs index a3c66aa5..42a1983d 100644 --- a/src/impls/vec.rs +++ b/src/impls/vec.rs @@ -1,12 +1,12 @@ -use no_std_io::io::Read; +use no_std_io::io::{Read, Write}; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use bitvec::prelude::*; - +use crate::reader::Reader; +use crate::writer::Writer; use crate::{ctx::*, DekuReader}; -use crate::{DekuError, DekuWrite}; +use crate::{DekuError, DekuWriter}; /// Read `T`s into a vec until a given predicate returns true /// * `capacity` - an optional capacity to pre-allocate the vector with @@ -16,7 +16,7 @@ use crate::{DekuError, DekuWrite}; /// and a borrow of the latest value to have been read. It should return `true` if reading /// should now stop, and `false` otherwise fn reader_vec_with_predicate<'a, T, Ctx, Predicate, R: Read>( - reader: &mut crate::reader::Reader, + reader: &mut Reader, capacity: Option, ctx: Ctx, mut predicate: Predicate, @@ -51,7 +51,7 @@ where Predicate: FnMut(&T) -> bool, { fn from_reader_with_ctx( - reader: &mut crate::reader::Reader, + reader: &mut Reader, (limit, inner_ctx): (Limit, Ctx), ) -> Result where @@ -113,7 +113,7 @@ impl<'a, T: DekuReader<'a>, Predicate: FnMut(&T) -> bool> DekuReader<'a, Limit( - reader: &mut crate::reader::Reader, + reader: &mut Reader, limit: Limit, ) -> Result where @@ -123,21 +123,23 @@ impl<'a, T: DekuReader<'a>, Predicate: FnMut(&T) -> bool> DekuReader<'a, Limit, Ctx: Copy> DekuWrite for Vec { +impl, Ctx: Copy> DekuWriter for Vec { /// Write all `T`s in a `Vec` to bits. /// * **inner_ctx** - The context required by `T`. /// # Examples /// ```rust - /// # use deku::{ctx::Endian, DekuWrite}; + /// # use deku::{ctx::Endian, DekuWriter}; + /// # use deku::writer::Writer; /// # use deku::bitvec::{Msb0, bitvec}; /// let data = vec![1u8]; - /// let mut output = bitvec![u8, Msb0;]; - /// data.write(&mut output, Endian::Big).unwrap(); - /// assert_eq!(output, bitvec![u8, Msb0; 0, 0, 0, 0, 0, 0, 0, 1]) + /// let mut out_buf = vec![]; + /// let mut writer = Writer::new(&mut out_buf); + /// data.to_writer(&mut writer, Endian::Big).unwrap(); + /// assert_eq!(data, out_buf.to_vec()); /// ``` - fn write(&self, output: &mut BitVec, inner_ctx: Ctx) -> Result<(), DekuError> { + fn to_writer(&self, writer: &mut Writer, inner_ctx: Ctx) -> Result<(), DekuError> { for v in self { - v.write(output, inner_ctx)?; + v.to_writer(writer, inner_ctx)?; } Ok(()) } @@ -145,6 +147,7 @@ impl, Ctx: Copy> DekuWrite for Vec { #[cfg(test)] mod tests { + use crate::bitvec::{bits, BitSlice, Msb0}; use rstest::rstest; use crate::reader::Reader; @@ -202,9 +205,10 @@ mod tests { case::normal(vec![0xAABB, 0xCCDD], Endian::Little, vec![0xBB, 0xAA, 0xDD, 0xCC]), )] fn test_vec_write(input: Vec, endian: Endian, expected: Vec) { - let mut res_write = bitvec![u8, Msb0;]; - input.write(&mut res_write, endian).unwrap(); - assert_eq!(expected, res_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + input.to_writer(&mut writer, endian).unwrap(); + assert_eq!(expected, out_buf.to_vec()); } // Note: These tests also exist in boxed.rs @@ -243,11 +247,12 @@ mod tests { input.read_to_end(&mut buf).unwrap(); assert_eq!(expected_rest_bytes, buf); - let mut res_write = bitvec![u8, Msb0;]; + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); res_read - .write(&mut res_write, (endian, BitSize(bit_size))) + .to_writer(&mut writer, (endian, BitSize(bit_size))) .unwrap(); - assert_eq!(expected_write, res_write.into_vec()); + assert_eq!(expected_write, out_buf.to_vec()); assert_eq!(input_clone[..expected_write.len()].to_vec(), expected_write); } diff --git a/src/lib.rs b/src/lib.rs index c3d8c8d3..c9608b13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -331,6 +331,7 @@ pub mod no_std_io { pub use no_std_io::io::Cursor; pub use no_std_io::io::Read; pub use no_std_io::io::Result; + pub use no_std_io::io::Write; } /// re-export of bitvec @@ -347,8 +348,10 @@ pub mod error; mod impls; pub mod prelude; pub mod reader; +pub mod writer; pub use crate::error::DekuError; +use crate::writer::Writer; /// "Reader" trait: read bytes and bits from [`no_std_io::Read`]er pub trait DekuReader<'a, Ctx = ()> { @@ -422,26 +425,58 @@ pub trait DekuContainerRead<'a>: DekuReader<'a, ()> { Self: Sized; } -/// "Writer" trait: write from type to bits -pub trait DekuWrite { - /// Write type to bits - /// * **output** - Sink to store resulting bits - /// * **ctx** - A context required by context-sensitive reading. A unit type `()` means no context - /// needed. - fn write( +/// "Writer" trait: write from type to bytes +pub trait DekuWriter { + /// Write type to bytes + fn to_writer( &self, - output: &mut bitvec::BitVec, + writer: &mut Writer, ctx: Ctx, ) -> Result<(), DekuError>; } /// "Writer" trait: implemented on DekuWrite struct and enum containers. A `container` is a type which /// doesn't need any context information. -pub trait DekuContainerWrite: DekuWrite<()> { +pub trait DekuContainerWrite: DekuWriter<()> { /// Write struct/enum to Vec + /// + /// ```rust + /// # use deku::prelude::*; + /// #[derive(Debug, PartialEq, DekuRead, DekuWrite)] + /// #[deku(endian = "little")] + /// struct S { + /// data_00: u8, + /// data_01: u16, + /// data_02: u32, + /// } + /// + /// let s = S { data_00: 0x01, data_01: 0x02, data_02: 0x03 }; + /// let bytes = s.to_bytes().unwrap(); + /// assert_eq!(bytes, [0x01, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00]); + /// ```` fn to_bytes(&self) -> Result, DekuError>; /// Write struct/enum to BitVec + /// + /// ```rust + /// # use deku::prelude::*; + /// # use deku::bitvec::Lsb0; + /// #[derive(PartialEq, Debug, DekuRead, DekuWrite)] + /// #[deku(endian = "little")] + /// pub struct TestOver { + /// #[deku(bits = "4")] + /// pub a: u8, + /// #[deku(bits = "4")] + /// pub b: u8, + /// #[deku(bits = "1")] + /// pub c: u8, + /// } + /// + /// let test_data: &[u8] = &[0xf1, 0x80]; + /// let test = TestOver::from_bytes((test_data, 0)).unwrap().1; + /// let bits = test.to_bits().unwrap(); + /// assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); + /// ``` fn to_bits(&self) -> Result, DekuError>; } @@ -457,19 +492,17 @@ pub trait DekuEnumExt<'a, T> { fn deku_id(&self) -> Result; } -/// Implements DekuWrite for references of types that implement DekuWrite -impl DekuWrite for &T +impl DekuWriter for &T where - T: DekuWrite, + T: DekuWriter, Ctx: Copy, { - /// Write value of type to bits - fn write( + fn to_writer( &self, - output: &mut bitvec::BitVec, + writer: &mut Writer, ctx: Ctx, ) -> Result<(), DekuError> { - ::write(self, output, ctx)?; + ::to_writer(self, writer, ctx)?; Ok(()) } } diff --git a/src/prelude.rs b/src/prelude.rs index 6a70b6d4..da52e982 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,6 +4,6 @@ */ pub use crate::error::{DekuError, NeedSize}; pub use crate::{ - deku_derive, reader::Reader, DekuContainerRead, DekuContainerWrite, DekuEnumExt, DekuRead, - DekuReader, DekuUpdate, DekuWrite, + deku_derive, reader::Reader, writer::Writer, DekuContainerRead, DekuContainerWrite, + DekuEnumExt, DekuRead, DekuReader, DekuUpdate, DekuWrite, DekuWriter, }; diff --git a/src/writer.rs b/src/writer.rs new file mode 100644 index 00000000..430e065b --- /dev/null +++ b/src/writer.rs @@ -0,0 +1,184 @@ +//! 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 `from_reader` +pub struct Writer<'a, W: Write> { + pub(crate) inner: &'a mut W, + /// Leftover bits + pub leftover: BitVec, + /// Total bits written + pub bits_written: usize, +} + +impl<'a, W: Write> Writer<'a, W> { + /// Create a new `Writer` + #[inline] + pub fn new(inner: &'a mut W) -> Self { + Self { + inner, + leftover: BitVec::new(), + bits_written: 0, + } + } + + /// Return the unused bits + #[inline] + pub fn rest(&mut self) -> alloc::vec::Vec { + self.leftover.iter().by_vals().collect() + } + + /// Write all bits to `Writer` buffer if bits can fit into a byte buffer + #[inline] + pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { + #[cfg(feature = "logging")] + log::trace!("attempting {} bits", bits.len()); + + // quick return if we can't write to the bytes buffer + if (self.leftover.len() + bits.len()) < 8 { + self.leftover.extend_from_bitslice(bits); + return Ok(()); + } + + // pre-pend the previous attempt to write if needed + let mut bits = if self.leftover.is_empty() { + bits + } else { + #[cfg(feature = "logging")] + log::trace!("pre-pending {} bits", self.leftover.len()); + self.leftover.extend_from_bitslice(bits); + &mut self.leftover + }; + + // one shot impl of BitSlice::read(no read_exact), but for no_std + let mut buf = alloc::vec![0x00; bits.len() / 8]; + let mut count = 0; + bits.chunks_exact(bits_of::()) + .zip(buf.iter_mut()) + .for_each(|(byte, slot)| { + *slot = byte.load_be(); + count += 1; + }); + bits = unsafe { bits.get_unchecked(count * bits_of::()..) }; + + // TODO: with_capacity? + self.bits_written = buf.len() * 8; + self.leftover = bits.to_bitvec(); + if self.inner.write_all(&buf).is_err() { + return Err(DekuError::Write); + } + #[cfg(feature = "logging")] + log::trace!("wrote {} bits", buf.len() * 8); + + Ok(()) + } + + /// Write `buf` into `Writer` + // The following inline(always) helps performance significantly + #[inline(always)] + pub fn write_bytes(&mut self, buf: &[u8]) -> Result<(), DekuError> { + #[cfg(feature = "logging")] + log::trace!("writing {} bytes", buf.len()); + if !self.leftover.is_empty() { + #[cfg(feature = "logging")] + log::trace!("leftover exists"); + // TODO: we could check here and only send the required bits to finish the byte? + // (instead of sending the entire thing) + self.write_bits(&BitVec::from_slice(buf))?; + } else { + if self.inner.write_all(buf).is_err() { + return Err(DekuError::Write); + } + self.bits_written = buf.len() * 8; + } + + Ok(()) + } + + /// Write all remaining bits into `Writer`, adding empty bits to the end so that we can write + /// into a byte buffer + #[inline] + pub fn finalize(&mut self) -> Result<(), DekuError> { + if !self.leftover.is_empty() { + #[cfg(feature = "logging")] + log::trace!("finalized: {} bits leftover", self.leftover.len()); + + // add bits to be byte aligned so we can write + self.leftover + .extend_from_bitslice(&bitvec![u8, Msb0; 0; 8 - self.leftover.len()]); + let mut buf = alloc::vec![0x00; self.leftover.len() / 8]; + + // write as many leftover to the buffer (as we can, can't write bits just bytes) + // TODO: error if bits are leftover? (not bytes aligned) + self.leftover + .chunks_exact(bits_of::()) + .zip(buf.iter_mut()) + .for_each(|(byte, slot)| { + *slot = byte.load_be(); + }); + + if self.inner.write_all(&buf).is_err() { + return Err(DekuError::Write); + } + #[cfg(feature = "logging")] + log::trace!("finalized: wrote {} bits", buf.len() * 8); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use hexlit::hex; + + #[test] + fn test_writer() { + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + let mut bv = BitVec::::from_slice(&[0xbb]); + writer.write_bits(&mut bv).unwrap(); + + let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1]; + writer.write_bits(&mut bv).unwrap(); + let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1]; + writer.write_bits(&mut bv).unwrap(); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1]; + writer.write_bits(&mut bv).unwrap(); + let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1]; + writer.write_bits(&mut bv).unwrap(); + + let mut bv = bitvec![u8, Msb0; 0, 0, 0, 1]; + writer.write_bits(&mut bv).unwrap(); + + let mut input = hex!("aa"); + writer.write_bytes(&mut input).unwrap(); + + let mut bv = bitvec![u8, Msb0; 1, 1, 1, 1]; + writer.write_bits(&mut bv).unwrap(); + + assert_eq!( + &mut out_buf, + &mut vec![0xaa, 0xbb, 0xf1, 0xaa, 0x1f, 0x1a, 0xaf] + ); + } +} diff --git a/tests/test_attributes/test_ctx.rs b/tests/test_attributes/test_ctx.rs index 7795ff6f..e40da0fe 100644 --- a/tests/test_attributes/test_ctx.rs +++ b/tests/test_attributes/test_ctx.rs @@ -5,6 +5,7 @@ use bitvec::bitvec; use deku::bitvec::Msb0; use deku::prelude::*; use deku::reader::Reader; +use deku::writer::Writer; /// General smoke tests for ctx /// TODO: These should be divided into smaller units @@ -16,7 +17,7 @@ fn test_ctx_struct() { struct SubTypeNeedCtx { #[deku( reader = "(u8::from_reader_with_ctx(deku::reader,()).map(|c|(a+b+c) as usize))", - writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(self.i as u8)" + writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(self.i as u8)" )] i: usize, } @@ -56,7 +57,7 @@ fn test_top_level_ctx_enum() { VariantA( #[deku( reader = "(u8::from_reader_with_ctx(deku::reader,()).map(|c|(a+b+c)))", - writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(field_0)" + writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(field_0)" )] u8, ), @@ -70,9 +71,10 @@ fn test_top_level_ctx_enum() { .unwrap(); assert_eq!(ret_read, TopLevelCtxEnum::VariantA(0x06)); - let mut ret_write = bitvec![u8, Msb0;]; - ret_read.write(&mut ret_write, (1, 2)).unwrap(); - assert_eq!(ret_write.into_vec(), &test_data[..]); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ret_read.to_writer(&mut writer, (1, 2)).unwrap(); + assert_eq!(out_buf.to_vec(), &test_data[..]); } #[test] @@ -84,7 +86,7 @@ fn test_top_level_ctx_enum_default() { VariantA( #[deku( reader = "(u8::from_reader_with_ctx(deku::reader, ()).map(|c|(a+b+c)))", - writer = "(|c|{u8::write(&(c-a-b), deku::output, ())})(field_0)" + writer = "(|c|{u8::to_writer(&(c-a-b), deku::writer, ())})(field_0)" )] u8, ), @@ -106,9 +108,10 @@ fn test_top_level_ctx_enum_default() { ) .unwrap(); assert_eq!(ret_read, TopLevelCtxEnumDefault::VariantA(0x06)); - let mut ret_write = bitvec![u8, Msb0;]; - ret_read.write(&mut ret_write, (1, 2)).unwrap(); - assert_eq!(test_data.to_vec(), ret_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ret_read.to_writer(&mut writer, (1, 2)).unwrap(); + assert_eq!(test_data.to_vec(), out_buf.to_vec()); } #[test] @@ -228,8 +231,10 @@ fn test_ctx_default_struct() { .unwrap(); assert_eq!(expected, ret_read); let mut ret_write = bitvec![u8, Msb0;]; - ret_read.write(&mut ret_write, (1, 2)).unwrap(); - assert_eq!(test_data.to_vec(), ret_write.into_vec()); + let mut out_buf = vec![]; + let mut writer = Writer::new(&mut out_buf); + ret_read.to_writer(&mut writer, (1, 2)).unwrap(); + assert_eq!(test_data.to_vec(), out_buf.to_vec()); } #[test] diff --git a/tests/test_compile/cases/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_generic.rs b/tests/test_generic.rs index dfb3b8f9..09b3a3c1 100644 --- a/tests/test_generic.rs +++ b/tests/test_generic.rs @@ -7,7 +7,7 @@ fn test_generic_struct() { #[derive(PartialEq, Debug, DekuRead, DekuWrite)] struct TestStruct where - T: deku::DekuWrite + for<'a> deku::DekuReader<'a>, + T: deku::DekuWriter + for<'a> deku::DekuReader<'a>, { field_a: T, } @@ -27,7 +27,7 @@ fn test_generic_enum() { #[deku(type = "u8")] enum TestEnum where - T: deku::DekuWrite + for<'a> deku::DekuReader<'a>, + T: deku::DekuWriter + for<'a> deku::DekuReader<'a>, { #[deku(id = "1")] VariantT(T), diff --git a/tests/test_to_bits.rs b/tests/test_to_bits.rs new file mode 100644 index 00000000..129770ac --- /dev/null +++ b/tests/test_to_bits.rs @@ -0,0 +1,53 @@ +use std::convert::TryFrom; + +use deku::bitvec::Lsb0; +use deku::prelude::*; + +#[derive(PartialEq, Debug, DekuRead, DekuWrite)] +pub struct Test { + #[deku(bits = "4")] + pub a: u8, + #[deku(bits = "4")] + pub b: u8, +} + +#[test] +fn test_to_bits_correct() { + let test_data: &[u8] = &[0xf1]; + let test = Test::try_from(test_data).unwrap(); + let bits = test.to_bits().unwrap(); + assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1], bits); +} + +#[derive(PartialEq, Debug, DekuRead, DekuWrite)] +pub struct TestOver { + #[deku(bits = "4")] + pub a: u8, + #[deku(bits = "4")] + pub b: u8, + #[deku(bits = "1")] + pub c: u8, +} + +#[test] +fn test_to_bits_correct_over() { + let test_data: &[u8] = &[0xf1, 0x80]; + let test = TestOver::from_bytes((test_data, 0)).unwrap().1; + let bits = test.to_bits().unwrap(); + assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits); +} + +#[derive(PartialEq, Debug, DekuRead, DekuWrite)] +#[deku(type = "u8", bits = "4")] +enum TestEnum { + #[deku(id = "0b1010")] + VarA, +} + +#[test] +fn test_to_bits_enum() { + let test_data: &[u8] = &[0b1010_0000]; + let test = TestEnum::from_bytes((test_data, 0)).unwrap().1; + let bits = test.to_bits().unwrap(); + assert_eq!(deku::bitvec::bitvec![1, 0, 1, 0], bits); +}