diff --git a/Cargo.toml b/Cargo.toml index b1eff827..4aa5c9ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,10 @@ required-features = ["bits"] name = "ipv4" required-features = ["bits"] +[[example]] +name = "80211" +required-features = ["bits"] + [[example]] name = "many" diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs index 7859ab82..2eb12acb 100644 --- a/deku-derive/src/lib.rs +++ b/deku-derive/src/lib.rs @@ -159,6 +159,9 @@ struct DekuData { /// struct only: seek from start position seek_from_start: Option, + + /// Bit Order for all fields + bit_order: Option, } impl DekuData { @@ -213,6 +216,7 @@ impl DekuData { seek_from_current: receiver.seek_from_current?, seek_from_end: receiver.seek_from_end?, seek_from_start: receiver.seek_from_start?, + bit_order: receiver.bit_order, }; DekuData::validate(&data)?; @@ -360,6 +364,7 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataEnum<'a> { #[cfg(not(feature = "bits"))] None, deku_data.bytes.as_ref(), + deku_data.bit_order.as_ref(), )?; Ok(Self { @@ -481,10 +486,10 @@ struct FieldData { /// condition to parse field cond: Option, - // assertion on field + /// assertion on field assert: Option, - // assert value of field + /// assert value of field assert_eq: Option, /// seek from current position @@ -498,6 +503,9 @@ struct FieldData { /// seek from start position seek_from_start: Option, + + /// Bit Order of field + bit_order: Option, } impl FieldData { @@ -543,6 +551,7 @@ impl FieldData { seek_from_current: receiver.seek_from_current?, seek_from_end: receiver.seek_from_end?, seek_from_start: receiver.seek_from_start?, + bit_order: receiver.bit_order, }; FieldData::validate(&data)?; @@ -776,6 +785,10 @@ struct DekuReceiver { /// struct only: seek from start position #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] seek_from_start: Result, ReplacementError>, + + /// Bit Order of field + #[darling(default)] + bit_order: Option, } type ReplacementError = TokenStream; @@ -976,6 +989,10 @@ struct DekuFieldReceiver { /// seek from start position #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] seek_from_start: Result, ReplacementError>, + + /// Bit Order of field + #[darling(default)] + bit_order: Option, } /// Receiver for the variant-level attributes inside a enum diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs index 5208633a..7dd08771 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -4,14 +4,15 @@ use darling::ast::{Data, Fields}; use darling::ToTokens; use proc_macro2::TokenStream; use quote::quote; +use syn::LitStr; use crate::macros::{ - gen_ctx_types_and_arg, gen_field_args, gen_internal_field_ident, gen_internal_field_idents, - gen_type_from_ctx_id, token_contains_string, wrap_default_ctx, + assertion_failed, gen_bit_order_from_str, gen_ctx_types_and_arg, gen_field_args, + gen_internal_field_idents, token_contains_string, wrap_default_ctx, }; use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id}; -use super::assertion_failed; +use super::{gen_internal_field_ident, gen_type_from_ctx_id}; pub(crate) fn emit_deku_read(input: &DekuData) -> Result { match &input.data { @@ -567,28 +568,59 @@ fn emit_bit_byte_offsets( } #[cfg(feature = "bits")] -fn emit_padding(bit_size: &TokenStream) -> TokenStream { +fn emit_padding(bit_size: &TokenStream, bit_order: Option<&LitStr>) -> TokenStream { let crate_ = super::get_crate_name(); - quote! { - { - use core::convert::TryFrom; - // TODO: I hope this consts in most cases? - extern crate alloc; - use alloc::borrow::Cow; - let __deku_pad = usize::try_from(#bit_size).map_err(|e| - ::#crate_::DekuError::InvalidParam(Cow::from(format!( - "Invalid padding param \"({})\": cannot convert to usize", - stringify!(#bit_size) - ))) - )?; - - - if (__deku_pad % 8) == 0 { - let bytes_read = __deku_pad / 8; - let mut buf = alloc::vec![0; bytes_read]; - let _ = __deku_reader.read_bytes(bytes_read, &mut buf)?; - } else { - let _ = __deku_reader.read_bits(__deku_pad)?; + if let Some(bit_order) = bit_order { + let order = gen_bit_order_from_str(bit_order).unwrap(); + quote! { + { + use core::convert::TryFrom; + // TODO: I hope this consts in most cases? + extern crate alloc; + use alloc::borrow::Cow; + let __deku_pad = usize::try_from(#bit_size).map_err(|e| + ::#crate_::DekuError::InvalidParam(Cow::from(format!( + "Invalid padding param \"({})\": cannot convert to usize", + stringify!(#bit_size) + ))) + )?; + + + if (__deku_pad % 8) == 0 { + let bytes_read = __deku_pad / 8; + let mut buf = vec![0; bytes_read]; + // TODO: use skip_bytes, or Seek in the future? + let _ = __deku_reader.read_bytes(bytes_read, &mut buf, #order)?; + } else { + // TODO: use skip_bits, or Seek in the future? + let _ = __deku_reader.read_bits(__deku_pad, #order)?; + } + } + } + } else { + quote! { + { + use core::convert::TryFrom; + // TODO: I hope this consts in most cases? + extern crate alloc; + use alloc::borrow::Cow; + let __deku_pad = usize::try_from(#bit_size).map_err(|e| + ::#crate_::DekuError::InvalidParam(Cow::from(format!( + "Invalid padding param \"({})\": cannot convert to usize", + stringify!(#bit_size) + ))) + )?; + + + if (__deku_pad % 8) == 0 { + let bytes_read = __deku_pad / 8; + let mut buf = vec![0; bytes_read]; + // TODO: use skip_bytes, or Seek in the future? + let _ = __deku_reader.read_bytes(bytes_read, &mut buf, ::#crate_::ctx::Order::default())?; + } else { + // TODO: use skip_bits, or Seek in the future? + let _ = __deku_reader.read_bits(__deku_pad, ::#crate_::ctx::Order::default())?; + } } } } @@ -612,7 +644,7 @@ fn emit_padding_bytes(bit_size: &TokenStream) -> TokenStream { let mut buf = alloc::vec![0; __deku_pad]; - let _ = __deku_reader.read_bytes(__deku_pad, &mut buf)?; + let _ = __deku_reader.read_bytes(__deku_pad, &mut buf, ::#crate_::ctx::Order::default())?; } } } @@ -628,6 +660,7 @@ fn emit_field_read( let field_type = &f.ty; let field_endian = f.endian.as_ref().or(input.endian.as_ref()); + let field_bit_order = f.bit_order.as_ref().or(input.bit_order.as_ref()); let field_reader = &f.reader; @@ -744,6 +777,7 @@ fn emit_field_read( None, f.bytes.as_ref(), f.ctx.as_ref(), + field_bit_order, )?; // The __deku_reader limiting options are special, we need to generate `(limit, (other, ..))` for them. @@ -853,12 +887,14 @@ fn emit_field_read( let pad_bits_before = crate::macros::pad_bits( f.pad_bits_before.as_ref(), f.pad_bytes_before.as_ref(), + field_bit_order, emit_padding, ); #[cfg(feature = "bits")] let pad_bits_after = crate::macros::pad_bits( f.pad_bits_after.as_ref(), f.pad_bytes_after.as_ref(), + field_bit_order, emit_padding, ); diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs index bc7f0bd6..d09d46f3 100644 --- a/deku-derive/src/macros/deku_write.rs +++ b/deku-derive/src/macros/deku_write.rs @@ -3,10 +3,11 @@ use std::convert::TryFrom; use darling::ast::{Data, Fields}; use proc_macro2::TokenStream; use quote::quote; +use syn::LitStr; use crate::macros::{ - assertion_failed, gen_ctx_types_and_arg, gen_field_args, gen_struct_destruction, - token_contains_string, wrap_default_ctx, + assertion_failed, gen_bit_order_from_str, gen_ctx_types_and_arg, gen_field_args, + gen_struct_destruction, token_contains_string, wrap_default_ctx, }; use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id}; @@ -469,20 +470,38 @@ fn emit_bit_byte_offsets( } #[cfg(feature = "bits")] -fn emit_padding(bit_size: &TokenStream) -> TokenStream { +fn emit_padding(bit_size: &TokenStream, bit_order: Option<&LitStr>) -> TokenStream { let crate_ = super::get_crate_name(); - quote! { - { - use core::convert::TryFrom; - extern crate alloc; - use alloc::borrow::Cow; - let __deku_pad = usize::try_from(#bit_size).map_err(|e| - ::#crate_::DekuError::InvalidParam(Cow::from(format!( - "Invalid padding param \"({})\": cannot convert to usize", - stringify!(#bit_size) - ))) - )?; - __deku_writer.write_bits(::#crate_::bitvec::bitvec![u8, ::#crate_::bitvec::Msb0; 0; __deku_pad].as_bitslice())?; + if let Some(bit_order) = bit_order { + let order = gen_bit_order_from_str(bit_order).unwrap(); + quote! { + { + use core::convert::TryFrom; + extern crate alloc; + use alloc::borrow::Cow; + let __deku_pad = usize::try_from(#bit_size).map_err(|e| + ::#crate_::DekuError::InvalidParam(Cow::from(format!( + "Invalid padding param \"({})\": cannot convert to usize", + stringify!(#bit_size) + ))) + )?; + __deku_writer.write_bits_order(::#crate_::bitvec::bitvec![u8, ::#crate_::bitvec::Msb0; 0; __deku_pad].as_bitslice(), #order)?; + } + } + } else { + quote! { + { + use core::convert::TryFrom; + extern crate alloc; + use alloc::borrow::Cow; + let __deku_pad = usize::try_from(#bit_size).map_err(|e| + ::#crate_::DekuError::InvalidParam(Cow::from(format!( + "Invalid padding param \"({})\": cannot convert to usize", + stringify!(#bit_size) + ))) + )?; + __deku_writer.write_bits(::#crate_::bitvec::bitvec![u8, ::#crate_::bitvec::Msb0; 0; __deku_pad].as_bitslice())?; + } } } } @@ -516,6 +535,7 @@ fn emit_field_write( ) -> Result { let crate_ = super::get_crate_name(); let field_endian = f.endian.as_ref().or(input.endian.as_ref()); + let field_bit_order = f.bit_order.as_ref().or(input.bit_order.as_ref()); let seek = if let Some(num) = &f.seek_from_current { quote! { @@ -617,6 +637,7 @@ fn emit_field_write( None, f.bytes.as_ref(), f.ctx.as_ref(), + field_bit_order, )?; if f.temp { @@ -638,12 +659,14 @@ fn emit_field_write( let pad_bits_before = crate::macros::pad_bits( f.pad_bits_before.as_ref(), f.pad_bytes_before.as_ref(), + field_bit_order, emit_padding, ); #[cfg(feature = "bits")] let pad_bits_after = crate::macros::pad_bits( f.pad_bits_after.as_ref(), f.pad_bytes_after.as_ref(), + field_bit_order, emit_padding, ); diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs index 06db7b1a..f657946a 100644 --- a/deku-derive/src/macros/mod.rs +++ b/deku-derive/src/macros/mod.rs @@ -4,7 +4,7 @@ use syn::parse::Parser; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::token::Comma; -use syn::Lifetime; +use syn::{Lifetime, LitStr}; use crate::Num; @@ -251,6 +251,7 @@ pub(crate) fn gen_id_args( id_endian: Option<&syn::LitStr>, bits: Option<&Num>, bytes: Option<&Num>, + bit_order: Option<&syn::LitStr>, ) -> syn::Result { let crate_ = get_crate_name(); let endian = id_endian @@ -259,12 +260,18 @@ pub(crate) fn gen_id_args( .transpose()?; let bits = bits.map(|n| quote! {::#crate_::ctx::BitSize(#n)}); let bytes = bytes.map(|n| quote! {::#crate_::ctx::ByteSize(#n)}); + let bit_order = bit_order.map(gen_bit_order_from_str).transpose()?; // FIXME: Should be `into_iter` here, see /~https://github.com/rust-lang/rust/issues/66145. - let id_args = [endian.as_ref(), bits.as_ref(), bytes.as_ref()] - .iter() - .filter_map(|i| *i) - .collect::>(); + let id_args = [ + endian.as_ref(), + bits.as_ref(), + bytes.as_ref(), + bit_order.as_ref(), + ] + .iter() + .filter_map(|i| *i) + .collect::>(); match &id_args[..] { [arg] => Ok(quote! {#arg}), @@ -281,18 +288,27 @@ fn gen_field_args( bits: Option<&Num>, bytes: Option<&Num>, ctx: Option<&Punctuated>, + bit_order: Option<&syn::LitStr>, ) -> syn::Result { let crate_ = get_crate_name(); let endian = endian.map(gen_endian_from_str).transpose()?; let bits = bits.map(|n| quote! {::#crate_::ctx::BitSize(#n)}); let bytes = bytes.map(|n| quote! {::#crate_::ctx::ByteSize(#n)}); + let bit_order = bit_order.map(gen_bit_order_from_str).transpose()?; let ctx = ctx.map(|c| quote! {#c}); // FIXME: Should be `into_iter` here, see /~https://github.com/rust-lang/rust/issues/66145. - let field_args = [endian.as_ref(), bits.as_ref(), bytes.as_ref(), ctx.as_ref()] - .iter() - .filter_map(|i| *i) - .collect::>(); + // TODO: the order here should be documented + let field_args = [ + endian.as_ref(), + bits.as_ref(), + bytes.as_ref(), + bit_order.as_ref(), + ctx.as_ref(), + ] + .iter() + .filter_map(|i| *i) + .collect::>(); // Because `impl DekuRead<'_, (T1, T2)>` but `impl DekuRead<'_, T1>`(not tuple) match &field_args[..] { @@ -301,6 +317,20 @@ fn gen_field_args( } } +/// Generate bit_order tokens from string: `lsb` -> `Order::Lsb0`. +fn gen_bit_order_from_str(s: &syn::LitStr) -> syn::Result { + let crate_ = get_crate_name(); + match s.value().as_str() { + "lsb" => Ok(quote! {::#crate_::ctx::Order::Lsb0}), + "msb" => Ok(quote! {::#crate_::ctx::Order::Msb0}), + _ => { + // treat as variable, possibly from `ctx` + let v: TokenStream = s.value().parse()?; + Ok(quote! {#v}) + } + } +} + /// Generate endian tokens from string: `big` -> `Endian::Big`. fn gen_endian_from_str(s: &syn::LitStr) -> syn::Result { let crate_ = get_crate_name(); @@ -348,14 +378,15 @@ fn token_contains_string(tok: &Option, s: &str) -> bool { fn pad_bits( bits: Option<&TokenStream>, bytes: Option<&TokenStream>, - emit_padding: fn(&TokenStream) -> TokenStream, + bit_order: Option<&LitStr>, + emit_padding: fn(&TokenStream, bit_order: Option<&LitStr>) -> TokenStream, ) -> TokenStream { match (bits, bytes) { (Some(pad_bits), Some(pad_bytes)) => { - emit_padding("e! { (#pad_bits) + ((#pad_bytes) * 8) }) + emit_padding("e! { (#pad_bits) + ((#pad_bytes) * 8) }, bit_order) } - (Some(pad_bits), None) => emit_padding(pad_bits), - (None, Some(pad_bytes)) => emit_padding("e! {((#pad_bytes) * 8)}), + (Some(pad_bits), None) => emit_padding(pad_bits, bit_order), + (None, Some(pad_bytes)) => emit_padding("e! {((#pad_bytes) * 8)}, bit_order), (None, None) => quote!(), } } diff --git a/examples/80211.rs b/examples/80211.rs new file mode 100644 index 00000000..30ef4371 --- /dev/null +++ b/examples/80211.rs @@ -0,0 +1,73 @@ +use deku::ctx::Order; +use deku::prelude::*; + +use std::convert::TryFrom; + +#[derive(Debug, DekuRead, DekuWrite, PartialEq)] +#[deku(id_type = "u8", bits = "2")] +#[deku(bit_order = "ctx_lsb", ctx = "ctx_lsb: Order")] +pub enum FrameType { + #[deku(id = "0")] + Management, + #[deku(id = "1")] + Control, + #[deku(id = "2")] + Data, +} + +#[derive(Debug, DekuRead, DekuWrite, PartialEq)] +#[deku(bit_order = "ctx_lsb", ctx = "ctx_lsb: Order")] +pub struct Flags { + #[deku(bits = 1)] + pub to_ds: u8, + #[deku(bits = 1)] + pub from_ds: u8, + #[deku(bits = 1)] + pub more_fragments: u8, + #[deku(bits = 1)] + pub retry: u8, + #[deku(bits = 1)] + pub power_management: u8, + #[deku(bits = 1)] + pub more_data: u8, + #[deku(bits = 1)] + pub protected_frame: u8, + #[deku(bits = 1)] + pub order: u8, +} + +#[derive(Debug, DekuRead, DekuWrite, PartialEq)] +#[deku(bit_order = "lsb")] +pub struct FrameControl { + #[deku(bits = 4)] + pub sub_type: u8, + #[deku(bits = 2)] + pub protocol_version: u8, + pub frame_type: FrameType, + + pub flags: Flags, +} + +fn main() { + let data = vec![0x88u8, 0x41]; + let control_frame = FrameControl::try_from(data.as_ref()).unwrap(); + assert_eq!( + control_frame, + FrameControl { + protocol_version: 0, + frame_type: FrameType::Data, + sub_type: 8, + + flags: Flags { + to_ds: 1, + from_ds: 0, + more_fragments: 0, + retry: 0, + power_management: 0, + more_data: 0, + protected_frame: 1, + order: 0, + } + } + ); +} diff --git a/src/attributes.rs b/src/attributes.rs index f73ebdea..f4504087 100644 --- a/src/attributes.rs +++ b/src/attributes.rs @@ -33,6 +33,7 @@ enum DekuEnum { | Attribute | Scope | Description |-----------|------------------|------------ | [endian](#endian) | top-level, field | Set the endianness +| [bit_order](#bit_order) | top-level, field | Set the bit-order when reading bits | [magic](#magic) | top-level | A magic value that must be present at the start of this struct/enum | [seek_from_current](#seek_from_current) | top-level, field | Sets the offset of reader and writer to the current position plus the specified number of bytes | [seek_from_end](#seek_from_end) | top-level, field | Sets the offset to the size of reader and writer plus the specified number of bytes @@ -148,6 +149,89 @@ let value: Vec = value.try_into().unwrap(); assert_eq!(&*data, value); ``` +# bit_order +Specify the field or containers bit order. By default all bits are read in `Msb0` (Most significant bit) order. +### Top-Level Example +```rust +# use deku::prelude::*; +# use std::convert::{TryInto, TryFrom}; +# #[derive(Debug, DekuRead, DekuWrite, PartialEq)] +#[deku(bit_order = "lsb")] +pub struct SquashfsV3 { + #[deku(bits = "4")] + inode_type: u8, + #[deku(bits = "12")] + mode: u16, + uid: u8, + guid: u8, + mtime: u32, + inode_number: u32, +} +let data: &[u8] = &[ +// inode_type +// ╭----------- +// | +// | mode +// ╭+-------- ...and so on +// || || +// vv vv + 0x31, 0x12, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, +]; +let header = SquashfsV3::try_from(data).unwrap(); +assert_eq!( + SquashfsV3 { + inode_type: 0x01, + mode: 0x123, + uid: 0x4, + guid: 0x5, + mtime: 0x6, + inode_number: 0x7 + }, + header, +); +``` +With endian-ness: +```rust +# use deku::prelude::*; +# use std::convert::{TryInto, TryFrom}; +# #[derive(Debug, DekuRead, DekuWrite, PartialEq)] +#[deku(endian = "big", bit_order = "lsb")] +pub struct BigEndian { + #[deku(bits = "13")] + offset: u16, + #[deku(bits = "3")] + t: u8, +} +let data = vec![0x40, 0x40]; +let big_endian = BigEndian::try_from(data.as_ref()).unwrap(); +assert_eq!( + big_endian, + BigEndian { + offset: 0x4000, + t: 2 + } +); +let bytes = big_endian.to_bytes().unwrap(); +assert_eq!(bytes, data); +```` +### Field Example +```rust +# use deku::prelude::*; +# use std::convert::{TryInto, TryFrom}; +# #[derive(Debug, DekuRead, DekuWrite, PartialEq)] +pub struct LsbField { + #[deku(bit_order = "lsb", bits = "13")] + offset: u16, + #[deku(bit_order = "lsb", bits = "3")] + t: u8, +} +let data = vec![0x40, 0x40]; +let more_first = LsbField::try_from(data.as_ref()).unwrap(); +assert_eq!(more_first, LsbField { offset: 0x40, t: 2 }); +let bytes = more_first.to_bytes().unwrap(); +assert_eq!(bytes, data); +``` + # magic Sets a "magic" value that must be present in the data at the start of diff --git a/src/ctx.rs b/src/ctx.rs index 5565be21..57440cd8 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -13,6 +13,16 @@ pub enum Endian { Big, } +/// Bit numbering +#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] +pub enum Order { + /// Most significant bit + #[default] + Msb0, + /// least significant bit + Lsb0, +} + /// Error returned when parsing a `Endian` using [`from_str`] /// /// [`from_str`]: Endian::from_str() diff --git a/src/impls/nonzero.rs b/src/impls/nonzero.rs index 6000742b..76b6b75c 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -46,6 +46,19 @@ macro_rules! ImplDekuTraits { #[cfg(feature = "bits")] ImplDekuTraitsCtx!($typ, $readtype, (endian, bitsize), (Endian, BitSize)); ImplDekuTraitsCtx!($typ, $readtype, (endian, bytesize), (Endian, ByteSize)); + #[cfg(feature = "bits")] + ImplDekuTraitsCtx!( + $typ, + $readtype, + (endian, bitsize, order), + (Endian, BitSize, Order) + ); + ImplDekuTraitsCtx!( + $typ, + $readtype, + (endian, bytesize, order), + (Endian, ByteSize, Order) + ); ImplDekuTraitsCtx!($typ, $readtype, endian, Endian); }; } diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs index a3aff12b..00d32ab5 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -49,16 +49,16 @@ impl DekuRead<'_, (Endian, ByteSize)> for u8 { } } -impl DekuReader<'_, (Endian, ByteSize)> for u8 { +impl DekuReader<'_, (Endian, ByteSize, Order)> for u8 { /// Ignore endian, as this is a `u8` #[inline(always)] fn from_reader_with_ctx( reader: &mut Reader, - (endian, size): (Endian, ByteSize), + (endian, size, order): (Endian, ByteSize, Order), ) -> Result { const MAX_TYPE_BYTES: usize = core::mem::size_of::(); let mut buf = [0; MAX_TYPE_BYTES]; - let ret = reader.read_bytes_const::(&mut buf)?; + let ret = reader.read_bytes_const::(&mut buf, order)?; let a = match ret { ReaderRet::Bytes => ::from_be_bytes(buf), #[cfg(feature = "bits")] @@ -81,7 +81,21 @@ impl DekuWriter<(Endian, BitSize)> for u8 { fn to_writer( &self, writer: &mut Writer, - (_, size): (Endian, BitSize), + (_, bit_size): (Endian, BitSize), + ) -> Result<(), DekuError> { + // endian doens't matter + ::to_writer(self, writer, (Endian::Big, bit_size, Order::default())) + } +} + +#[cfg(feature = "bits")] +impl DekuWriter<(Endian, BitSize, Order)> for u8 { + /// Ignore endian, as this is a `u8` + #[inline(always)] + fn to_writer( + &self, + writer: &mut Writer, + (_, size, order): (Endian, BitSize, Order), ) -> Result<(), DekuError> { let input = self.to_le_bytes(); @@ -109,7 +123,21 @@ impl DekuWriter<(Endian, BitSize)> for u8 { )))); } } - writer.write_bits(&input_bits[input_bits.len() - bit_size..])?; + writer.write_bits_order(&input_bits[input_bits.len() - bit_size..], order)?; + Ok(()) + } +} + +impl DekuWriter<(Endian, ByteSize, Order)> for u8 { + /// Ignore endian and byte_size, as this is a `u8` + #[inline(always)] + fn to_writer( + &self, + writer: &mut Writer, + (_, _, _): (Endian, ByteSize, Order), + ) -> Result<(), DekuError> { + let input = self.to_le_bytes(); + writer.write_bytes(&input)?; Ok(()) } } @@ -130,6 +158,125 @@ impl DekuWriter<(Endian, ByteSize)> for u8 { macro_rules! ImplDekuReadBits { ($typ:ty, $inner:ty) => { + #[cfg(feature = "bits")] + impl DekuRead<'_, (Endian, BitSize, Order)> for $typ { + #[inline] + fn read( + input: &BitSlice, + (endian, size, order): (Endian, BitSize, Order), + ) -> Result<(usize, Self), DekuError> { + const MAX_TYPE_BITS: usize = BitSize::of::<$typ>().0; + let bit_size: usize = size.0; + + let input_is_le = endian.is_le(); + + // PANIC: We already check that input.len() < bit_size above, so no panic will happen + let bit_slice = &input; + + let pad = 8 * ((bit_slice.len() + 7) / 8) - bit_slice.len(); + + // if everything is aligned, just read the value + if pad == 0 && bit_slice.len() == MAX_TYPE_BITS { + let bytes = bit_slice.domain().region().unwrap().1; + + if bytes.len() * 8 == MAX_TYPE_BITS { + // Read value + let value = if input_is_le { + <$typ>::from_le_bytes(bytes.try_into()?) + } else { + <$typ>::from_be_bytes(bytes.try_into()?) + }; + return Ok((bit_size, value)); + } + } + + // if read from Lsb order and it's escpecially cursed since its not just within one byte... + // read_bits returned: [0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1] + // | second | first | + // we want to read from right to left when lsb (without using BitVec BitFields) + // + // Turning this into [0x23, 0x01] (then appending till type size) + if order == Order::Lsb0 && bit_slice.len() > 8 { + let mut bits = BitVec::::with_capacity(bit_slice.len() + pad); + + bits.extend_from_bitslice(&bit_slice); + + for _ in 0..pad { + bits.insert(0, false); + } + + let mut buf = alloc::vec![]; + let mut n = bits.len() - 8; + while let Some(slice) = bits.get(n..n + 8) { + let a: u8 = slice.load_be(); + buf.push(a); + if n < 8 { + break; + } + n -= 8; + } + + // Pad up-to size of type + for _ in 0..core::mem::size_of::<$typ>() - buf.len() { + buf.push(0x00); + } + + // Read value + let value = if input_is_le { + <$typ>::from_le_bytes(buf.try_into().unwrap()) + } else { + <$typ>::from_be_bytes(buf.try_into().unwrap()) + }; + + Ok((bit_size, value)) + } else { + // Create a new BitVec from the slice and pad un-aligned chunks + // i.e. [10010110, 1110] -> [10010110, 00001110] + let bits: BitVec = { + let mut bits = BitVec::with_capacity(bit_slice.len() + pad); + + // Copy bits to new BitVec + bits.extend_from_bitslice(&bit_slice); + + // Force align + //i.e. [1110, 10010110] -> [11101001, 0110] + bits.force_align(); + + // Some padding to next byte + let index = if input_is_le { + bits.len() - (8 - pad) + } else { + 0 + }; + for _ in 0..pad { + bits.insert(index, false); + } + + // Pad up-to size of type + for _ in 0..(MAX_TYPE_BITS - bits.len()) { + if input_is_le { + bits.push(false); + } else { + bits.insert(0, false); + } + } + + bits + }; + let bytes: &[u8] = bits.domain().region().unwrap().1; + + // Read value + let value = if input_is_le { + <$typ>::from_le_bytes(bytes.try_into()?) + } else { + <$typ>::from_be_bytes(bytes.try_into()?) + }; + + Ok((bit_size, value)) + } + } + } + #[cfg(feature = "bits")] impl DekuRead<'_, (Endian, BitSize)> for $typ { #[inline(never)] @@ -221,7 +368,7 @@ macro_rules! ImplDekuReadBits { size.0 )))); } - let bits = reader.read_bits(size.0)?; + let bits = reader.read_bits(size.0, Order::default())?; let Some(bits) = bits else { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); }; @@ -229,11 +376,51 @@ macro_rules! ImplDekuReadBits { Ok(a.1) } } + + #[cfg(feature = "bits")] + impl DekuReader<'_, (Endian, BitSize, Order)> for $typ { + #[inline] + fn from_reader_with_ctx( + reader: &mut Reader, + (endian, size, order): (Endian, BitSize, Order), + ) -> Result<$typ, DekuError> { + const MAX_TYPE_BITS: usize = BitSize::of::<$typ>().0; + if size.0 > MAX_TYPE_BITS { + return Err(DekuError::Parse(Cow::from(format!( + "too much data: container of {MAX_TYPE_BITS} bits cannot hold {} bits", + size.0 + )))); + } + let bits = reader.read_bits(size.0, order)?; + let Some(bits) = bits else { + return Err(DekuError::Parse(Cow::from(format!( + "no bits read from reader", + )))); + }; + let a = <$typ>::read(&bits, (endian, size, order))?; + Ok(a.1) + } + } }; } macro_rules! ImplDekuReadBytes { ($typ:ty, $inner:ty) => { + /// Ignore order + #[cfg(feature = "bits")] + impl DekuRead<'_, (Endian, ByteSize, Order)> for $typ { + #[inline] + fn read( + input: &BitSlice, + (endian, size, order): (Endian, ByteSize, Order), + ) -> Result<(usize, Self), DekuError> { + <$typ as DekuRead<'_, (Endian, BitSize, Order)>>::read( + input, + (endian, BitSize(size.0 * 8), order), + ) + } + } + #[cfg(feature = "bits")] impl DekuRead<'_, (Endian, ByteSize)> for $typ { #[inline(never)] @@ -247,11 +434,11 @@ macro_rules! ImplDekuReadBytes { } } - impl DekuReader<'_, (Endian, ByteSize)> for $typ { + impl DekuReader<'_, (Endian, ByteSize, Order)> for $typ { #[inline(always)] fn from_reader_with_ctx( reader: &mut Reader, - (endian, size): (Endian, ByteSize), + (endian, size, order): (Endian, ByteSize, Order), ) -> Result<$typ, DekuError> { const MAX_TYPE_BYTES: usize = core::mem::size_of::<$typ>(); if size.0 > MAX_TYPE_BYTES { @@ -261,7 +448,7 @@ macro_rules! ImplDekuReadBytes { )))); } let mut buf = [0; MAX_TYPE_BYTES]; - let ret = reader.read_bytes(size.0, &mut buf)?; + let ret = reader.read_bytes(size.0, &mut buf, order)?; let a = match ret { ReaderRet::Bytes => { if endian.is_le() { @@ -277,7 +464,7 @@ macro_rules! ImplDekuReadBytes { } #[cfg(feature = "bits")] ReaderRet::Bits(Some(bits)) => { - let a = <$typ>::read(&bits, (endian, size))?; + let a = <$typ>::read(&bits, (endian, size, order))?; a.1 } #[cfg(feature = "bits")] @@ -293,55 +480,56 @@ macro_rules! ImplDekuReadBytes { macro_rules! ImplDekuReadSignExtend { ($typ:ty, $inner:ty) => { + // Ignore Order, send back #[cfg(feature = "bits")] - impl DekuRead<'_, (Endian, ByteSize)> for $typ { - #[inline(never)] + impl DekuRead<'_, (Endian, ByteSize, Order)> for $typ { + #[inline] fn read( input: &BitSlice, - (endian, size): (Endian, ByteSize), + (endian, size, order): (Endian, ByteSize, Order), ) -> Result<(usize, Self), DekuError> { - let (amt_read, value) = - <$inner as DekuRead<'_, (Endian, ByteSize)>>::read(input, (endian, size))?; + <$typ as DekuRead<'_, (Endian, BitSize, Order)>>::read( + input, + (endian, BitSize(size.0 * 8), order), + ) + } + } + + #[cfg(feature = "bits")] + impl DekuRead<'_, (Endian, BitSize, Order)> for $typ { + #[inline] + fn read( + input: &BitSlice, + (endian, size, order): (Endian, BitSize, Order), + ) -> Result<(usize, Self), DekuError> { + let (amt_read, value) = <$inner as DekuRead<'_, (Endian, BitSize, Order)>>::read( + input, + (endian, size, order), + )?; const MAX_TYPE_BITS: usize = BitSize::of::<$typ>().0; - let bit_size = size.0 * 8; + let bit_size = size.0; let shift = MAX_TYPE_BITS - bit_size; let value = (value as $typ) << shift >> shift; Ok((amt_read, value)) } } - impl DekuReader<'_, (Endian, ByteSize)> for $typ { - #[inline(always)] - fn from_reader_with_ctx( - reader: &mut Reader, + #[cfg(feature = "bits")] + impl DekuRead<'_, (Endian, ByteSize)> for $typ { + #[inline(never)] + fn read( + input: &BitSlice, (endian, size): (Endian, ByteSize), - ) -> Result<$typ, DekuError> { - let mut buf = [0; core::mem::size_of::<$typ>()]; - let ret = reader.read_bytes(size.0, &mut buf)?; - let a = match ret { - ReaderRet::Bytes => { - if endian.is_le() { - <$typ>::from_le_bytes(buf.try_into()?) - } else { - <$typ>::from_be_bytes(buf.try_into()?) - } - } - #[cfg(feature = "bits")] - ReaderRet::Bits(bits) => { - let Some(bits) = bits else { - return Err(DekuError::Parse(Cow::from("no bits read from reader"))); - }; - let a = <$typ>::read(&bits, (endian, size))?; - a.1 - } - }; + ) -> Result<(usize, Self), DekuError> { + let (amt_read, value) = + <$inner as DekuRead<'_, (Endian, ByteSize)>>::read(input, (endian, size))?; const MAX_TYPE_BITS: usize = BitSize::of::<$typ>().0; let bit_size = size.0 * 8; let shift = MAX_TYPE_BITS - bit_size; - let value = (a as $typ) << shift >> shift; - Ok(value) + let value = (value as $typ) << shift >> shift; + Ok((amt_read, value)) } } @@ -369,6 +557,17 @@ macro_rules! ImplDekuReadSignExtend { fn from_reader_with_ctx( reader: &mut Reader, (endian, size): (Endian, BitSize), + ) -> Result<$typ, DekuError> { + <$typ>::from_reader_with_ctx(reader, (endian, size, Order::default())) + } + } + + #[cfg(feature = "bits")] + impl DekuReader<'_, (Endian, BitSize, Order)> for $typ { + #[inline(always)] + fn from_reader_with_ctx( + reader: &mut Reader, + (endian, size, order): (Endian, BitSize, Order), ) -> Result<$typ, DekuError> { const MAX_TYPE_BITS: usize = BitSize::of::<$typ>().0; if size.0 > MAX_TYPE_BITS { @@ -377,19 +576,83 @@ macro_rules! ImplDekuReadSignExtend { size.0 )))); } - let bits = reader.read_bits(size.0)?; + let bits = reader.read_bits(size.0, order)?; let Some(bits) = bits else { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); }; - let a = <$typ>::read(&bits, (endian, size))?; + let a = <$typ>::read(&bits, (endian, size, order))?; Ok(a.1) } } + + impl DekuReader<'_, (Endian, ByteSize, Order)> for $typ { + #[inline(always)] + fn from_reader_with_ctx( + reader: &mut Reader, + (endian, size, order): (Endian, ByteSize, Order), + ) -> Result<$typ, DekuError> { + const MAX_TYPE_BYTES: usize = core::mem::size_of::<$typ>(); + if size.0 > MAX_TYPE_BYTES { + return Err(DekuError::Parse(Cow::from(format!( + "too much data: container of {MAX_TYPE_BYTES} bytes cannot hold {} bytes", + size.0 + )))); + } + let mut buf = [0; MAX_TYPE_BYTES]; + let ret = reader.read_bytes(size.0, &mut buf, order)?; + let a = match ret { + ReaderRet::Bytes => { + if endian.is_le() { + <$typ>::from_le_bytes(buf.try_into().unwrap()) + } else { + if size.0 != core::mem::size_of::<$typ>() { + let padding = core::mem::size_of::<$typ>() - size.0; + buf.copy_within(0..size.0, padding); + buf[..padding].fill(0x00); + } + <$typ>::from_be_bytes(buf.try_into().unwrap()) + } + } + #[cfg(feature = "bits")] + ReaderRet::Bits(Some(bits)) => { + let a = <$typ>::read(&bits, (endian, size, order))?; + a.1 + } + #[cfg(feature = "bits")] + ReaderRet::Bits(None) => { + return Err(DekuError::Parse(Cow::from("no bits read from reader"))); + } + }; + Ok(a) + } + } }; } macro_rules! ForwardDekuRead { ($typ:ty) => { + impl DekuReader<'_, (Endian, Order)> for $typ { + #[inline] + fn from_reader_with_ctx( + reader: &mut Reader, + (endian, order): (Endian, Order), + ) -> Result<$typ, DekuError> { + let byte_size = core::mem::size_of::<$typ>(); + + <$typ>::from_reader_with_ctx(reader, (endian, ByteSize(byte_size), order)) + } + } + + impl DekuReader<'_, (Endian, ByteSize)> for $typ { + #[inline] + fn from_reader_with_ctx( + reader: &mut Reader, + (endian, byte_size): (Endian, ByteSize), + ) -> Result<$typ, DekuError> { + <$typ>::from_reader_with_ctx(reader, (endian, byte_size, Order::default())) + } + } + // Only have `endian`, specialize and use read_bytes_const impl DekuReader<'_, Endian> for $typ { #[inline(always)] @@ -399,7 +662,7 @@ macro_rules! ForwardDekuRead { ) -> Result<$typ, DekuError> { const MAX_TYPE_BYTES: usize = core::mem::size_of::<$typ>(); let mut buf = [0; MAX_TYPE_BYTES]; - let ret = reader.read_bytes_const::(&mut buf)?; + let ret = reader.read_bytes_const::(&mut buf, Order::default())?; let a = match ret { ReaderRet::Bytes => { if endian.is_le() { @@ -454,6 +717,34 @@ macro_rules! ForwardDekuRead { } } + //// Only have `bit_size`, set `endian` to `Endian::default`. + #[cfg(feature = "bits")] + impl DekuReader<'_, (BitSize, Order)> for $typ { + #[inline] + fn from_reader_with_ctx( + reader: &mut Reader, + (bit_size, order): (BitSize, Order), + ) -> Result<$typ, DekuError> { + let endian = Endian::default(); + + if (bit_size.0 % 8) == 0 { + <$typ>::from_reader_with_ctx(reader, (endian, ByteSize(bit_size.0 / 8), order)) + } else { + <$typ>::from_reader_with_ctx(reader, (endian, bit_size, order)) + } + } + } + + impl DekuReader<'_, Order> for $typ { + #[inline] + fn from_reader_with_ctx( + reader: &mut Reader, + order: Order, + ) -> Result<$typ, DekuError> { + <$typ>::from_reader_with_ctx(reader, (Endian::default(), order)) + } + } + impl DekuReader<'_> for $typ { #[inline(always)] fn from_reader_with_ctx( @@ -468,6 +759,64 @@ macro_rules! ForwardDekuRead { macro_rules! ImplDekuWrite { ($typ:ty) => { + #[cfg(feature = "bits")] + impl DekuWriter<(Endian, BitSize, Order)> for $typ { + #[inline] + fn to_writer( + &self, + writer: &mut Writer, + (endian, size, order): (Endian, BitSize, Order), + ) -> Result<(), DekuError> { + let input = match endian { + Endian::Little => self.to_le_bytes(), + Endian::Big => self.to_be_bytes(), + }; + + let bit_size: usize = size.0; + + let input_bits = input.view_bits::(); + + if bit_size > input_bits.len() { + return Err(DekuError::InvalidParam(Cow::from(format!( + "bit size {} is larger then input {}", + bit_size, + input_bits.len() + )))); + } + + match (endian, order) { + (Endian::Little, Order::Lsb0) + | (Endian::Little, Order::Msb0) + | (Endian::Big, Order::Lsb0) => { + let mut remaining_bits = bit_size; + for chunk in input_bits.chunks(8) { + if chunk.len() > remaining_bits { + writer.write_bits_order( + &chunk[chunk.len() - remaining_bits..], + order, + )?; + break; + } else { + writer.write_bits_order(&chunk, order)?; + } + remaining_bits -= chunk.len(); + } + } + (Endian::Big, Order::Msb0) => { + // big endian + // Example read 10 bits u32 [0xAB, 0b11_000000] + // => [00000000, 00000000, 00000010, 10101111] + writer.write_bits_order( + &input_bits[input_bits.len() - bit_size..], + Order::Msb0, + )?; + } + } + + Ok(()) + } + } + #[cfg(feature = "bits")] impl DekuWriter<(Endian, BitSize)> for $typ { #[inline(always)] @@ -570,6 +919,18 @@ macro_rules! ImplDekuWrite { Ok(()) } } + + /// When using Endian and ByteSize, Order is not used + impl DekuWriter<(Endian, ByteSize, Order)> for $typ { + #[inline] + fn to_writer( + &self, + writer: &mut Writer, + (endian, size, _order): (Endian, ByteSize, Order), + ) -> Result<(), DekuError> { + <$typ>::to_writer(self, writer, (endian, size)) + } + } }; } @@ -595,6 +956,30 @@ macro_rules! ImplDekuWriteOnlyEndian { macro_rules! ForwardDekuWrite { ($typ:ty) => { + #[cfg(feature = "bits")] + impl DekuWriter<(BitSize, Order)> for $typ { + #[inline(always)] + fn to_writer( + &self, + writer: &mut Writer, + (bit_size, order): (BitSize, Order), + ) -> Result<(), DekuError> { + <$typ>::to_writer(self, writer, (Endian::default(), bit_size, order)) + } + } + + impl DekuWriter<(Endian, Order)> for $typ { + #[inline(always)] + fn to_writer( + &self, + writer: &mut Writer, + (endian, order): (Endian, Order), + ) -> Result<(), DekuError> { + let byte_size = core::mem::size_of::<$typ>(); + <$typ>::to_writer(self, writer, (endian, ByteSize(byte_size), order)) + } + } + #[cfg(feature = "bits")] impl DekuWriter for $typ { #[inline(always)] @@ -618,6 +1003,17 @@ macro_rules! ForwardDekuWrite { } } + impl DekuWriter for $typ { + #[inline(always)] + fn to_writer( + &self, + writer: &mut Writer, + order: Order, + ) -> Result<(), DekuError> { + <$typ>::to_writer(self, writer, (Endian::default(), order)) + } + } + impl DekuWriter for $typ { #[inline(always)] fn to_writer( diff --git a/src/lib.rs b/src/lib.rs index 845cb8e0..f5313ab8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -582,7 +582,7 @@ pub trait DekuContainerWrite: DekuWriter<()> { 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); + bv.append(&mut leftover.0); Ok(bv) } } diff --git a/src/reader.rs b/src/reader.rs index 82528230..7552ed3e 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -6,7 +6,7 @@ use core::cmp::Ordering; use bitvec::prelude::*; use no_std_io::io::{ErrorKind, Read, Seek, SeekFrom}; -use crate::{prelude::NeedSize, DekuError}; +use crate::{ctx::Order, prelude::NeedSize, DekuError}; use alloc::vec::Vec; #[cfg(feature = "logging")] @@ -24,6 +24,7 @@ pub enum ReaderRet { /// Max bits requested from [`Reader::read_bits`] during one call pub const MAX_BITS_AMT: usize = 128; +#[derive(Debug)] enum Leftover { Byte(u8), #[cfg(feature = "bits")] @@ -175,7 +176,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> { #[cfg(feature = "logging")] log::trace!("skip_bits: {amt}"); // Save, and keep the leftover bits since the read will most likely be less than a byte - self.read_bits(amt)?; + self.read_bits(amt, Order::default())?; } #[cfg(not(feature = "bits"))] @@ -184,7 +185,6 @@ impl<'a, R: Read + Seek> Reader<'a, R> { panic!("deku features no-bits was used"); } } - Ok(()) } @@ -200,12 +200,18 @@ impl<'a, R: Read + Seek> Reader<'a, R> { /// `amt` - Amount of bits that will be read. Must be <= [`MAX_BITS_AMT`]. #[inline(never)] #[cfg(feature = "bits")] - pub fn read_bits(&mut self, amt: usize) -> Result>, DekuError> { + pub fn read_bits( + &mut self, + amt: usize, + order: Order, + ) -> Result>, DekuError> { #[cfg(feature = "logging")] - log::trace!("read_bits: requesting {amt} bits"); + log::trace!("read_bits: requesting {amt} bits in {order:?} order"); + if amt == 0 { #[cfg(feature = "logging")] log::trace!("read_bits: returned None"); + return Ok(None); } let mut ret = BitVec::new(); @@ -218,7 +224,12 @@ impl<'a, R: Read + Seek> Reader<'a, R> { } let previous_len = match &self.leftover { - Some(Leftover::Bits(bits)) => bits.len(), + Some(Leftover::Bits(bits)) => { + #[cfg(feature = "logging")] + log::trace!("read_bits: using stored {} bits", bits.len()); + + bits.len() + } None => 0, Some(Leftover::Byte(_)) => unreachable!(), }; @@ -226,6 +237,9 @@ impl<'a, R: Read + Seek> Reader<'a, R> { match amt.cmp(&previous_len) { // exact match, just use leftover Ordering::Equal => { + #[cfg(feature = "logging")] + log::trace!("read_bits: exact bits already read"); + if let Some(Leftover::Bits(bits)) = &mut self.leftover { core::mem::swap(&mut ret, bits); self.leftover = None; @@ -235,19 +249,16 @@ impl<'a, R: Read + Seek> Reader<'a, R> { } // previous read was not enough to satisfy the amt requirement, return all previously Ordering::Greater => { - // read bits - match self.leftover { - Some(Leftover::Bits(ref bits)) => { - ret.extend_from_bitslice(bits); - } - Some(Leftover::Byte(_)) => { - unreachable!(); - } - None => {} - } + #[cfg(feature = "logging")] + log::trace!("read_bits: reading more bits"); // calculate the amount of bytes we need to read to read enough bits - let bits_left = amt - ret.len(); + let bits_len = if let Some(Leftover::Bits(ref bits)) = self.leftover { + bits.len() + } else { + 0 + }; + let mut bits_left = amt - bits_len; let mut bytes_len = bits_left / 8; if (bits_left % 8) != 0 { bytes_len += 1; @@ -267,21 +278,73 @@ impl<'a, R: Read + Seek> Reader<'a, R> { log::trace!("read_bits: read() {:02x?}", read_buf); // create bitslice and remove unused bits - let rest = BitSlice::try_from_slice(read_buf).unwrap(); - let (rest, not_needed) = rest.split_at(bits_left); - self.leftover = Some(Leftover::Bits(not_needed.to_bitvec())); + let mut new_bits = BitSlice::try_from_slice(read_buf).unwrap(); + // remove bytes until we get to the last byte, of which + // we need to care abount bit-order + let mut front_bits = None; + // Allow bits_left -= bits_left - (bits_left % 8), as this is correct + #[allow(clippy::misrefactored_assign_op)] + if bits_left > 8 { + let (used, more) = new_bits.split_at(bits_left - (bits_left % 8)); + bits_left -= bits_left - (bits_left % 8); + front_bits = Some(used); + new_bits = more; + } - // create return - ret.extend_from_bitslice(rest); + match order { + Order::Lsb0 => { + let (rest, used) = new_bits.split_at(new_bits.len() - bits_left); + ret.extend_from_bitslice(used); + if let Some(front_bits) = front_bits { + ret.extend_from_bitslice(front_bits); + } + if let Some(Leftover::Bits(bits)) = &self.leftover { + ret.extend_from_bitslice(bits); + } + + if !rest.is_empty() { + self.leftover = Some(Leftover::Bits(rest.to_bitvec())); + } else { + self.leftover = None; + } + } + Order::Msb0 => { + let (rest, not_needed) = new_bits.split_at(bits_left); + if let Some(front_bits) = front_bits { + ret.extend_from_bitslice(front_bits); + } + if let Some(Leftover::Bits(bits)) = &self.leftover { + ret.extend_from_bitslice(bits); + } + ret.extend_from_bitslice(rest); + + if !not_needed.is_empty() { + self.leftover = Some(Leftover::Bits(not_needed.to_bitvec())); + } else { + self.leftover = None; + } + } + } } // The entire bits we need to return have been already read previously from bytes but // not all were read, return required leftover bits Ordering::Less => { + #[cfg(feature = "logging")] + log::trace!("read_bits: bits already read"); + // read bits if let Some(Leftover::Bits(bits)) = &mut self.leftover { - let used = bits.split_off(amt); - ret.extend_from_bitslice(bits); - self.leftover = Some(Leftover::Bits(used)); + match order { + Order::Lsb0 => { + let used = bits.split_off(bits.len() - amt); + ret.extend_from_bitslice(&used); + } + Order::Msb0 => { + let used = bits.split_off(amt); + ret.extend_from_bitslice(bits); + *bits = used; + } + } } else { unreachable!(); } @@ -295,6 +358,8 @@ impl<'a, R: Read + Seek> Reader<'a, R> { #[cfg(feature = "logging")] log::trace!("read_bits: returning {ret}"); + debug_assert!(ret.len() == amt); + Ok(Some(ret)) } @@ -306,7 +371,12 @@ impl<'a, R: Read + Seek> Reader<'a, R> { /// `amt` - Amount of bytes that will be read /// `buf` - result bytes #[inline(always)] - pub fn read_bytes(&mut self, amt: usize, buf: &mut [u8]) -> Result { + pub fn read_bytes( + &mut self, + amt: usize, + buf: &mut [u8], + order: Order, + ) -> Result { #[cfg(feature = "logging")] log::trace!("read_bytes: requesting {amt} bytes"); @@ -329,14 +399,19 @@ impl<'a, R: Read + Seek> Reader<'a, R> { } // Trying to keep this not in the hot path - self.read_bytes_other(amt, buf) + self.read_bytes_other(amt, buf, order) } - fn read_bytes_other(&mut self, amt: usize, buf: &mut [u8]) -> Result { + fn read_bytes_other( + &mut self, + amt: usize, + buf: &mut [u8], + order: Order, + ) -> Result { match self.leftover { Some(Leftover::Byte(byte)) => self.read_bytes_leftover(buf, byte, amt), #[cfg(feature = "bits")] - Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(amt * 8)?)), + Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(amt * 8, order)?)), _ => unreachable!(), } } @@ -391,6 +466,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> { pub fn read_bytes_const( &mut self, buf: &mut [u8; N], + order: Order, ) -> Result { #[cfg(feature = "logging")] log::trace!("read_bytes_const: requesting {N} bytes"); @@ -413,17 +489,18 @@ impl<'a, R: Read + Seek> Reader<'a, R> { } // Trying to keep this not in the hot path - self.read_bytes_const_other::(buf) + self.read_bytes_const_other::(buf, order) } fn read_bytes_const_other( &mut self, buf: &mut [u8; N], + order: Order, ) -> Result { match self.leftover { Some(Leftover::Byte(byte)) => self.read_bytes_const_leftover(buf, byte), #[cfg(feature = "bits")] - Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(N * 8)?)), + Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(N * 8, order)?)), _ => unreachable!(), } } @@ -485,16 +562,16 @@ mod tests { let mut reader = Reader::new(&mut cursor); assert!(!reader.end()); let mut buf = [0; 2]; - let _ = reader.read_bytes_const::<2>(&mut buf).unwrap(); + let _ = reader.read_bytes_const::<2>(&mut buf, Order::Lsb0).unwrap(); assert!(reader.end()); let input = hex!("aa"); let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); assert!(!reader.end()); - let _ = reader.read_bits(4); + let _ = reader.read_bits(4, Order::Lsb0); assert!(!reader.end()); - let _ = reader.read_bits(4); + let _ = reader.read_bits(4, Order::Lsb0); assert!(reader.end()); } @@ -504,9 +581,9 @@ mod tests { let input = hex!("aa"); let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); - let _ = reader.read_bits(1); - let _ = reader.read_bits(4); - let _ = reader.read_bits(3); + let _ = reader.read_bits(1, Order::Lsb0); + let _ = reader.read_bits(4, Order::Lsb0); + let _ = reader.read_bits(3, Order::Lsb0); } #[test] @@ -515,7 +592,7 @@ mod tests { let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); let mut buf = [0; 1]; - let _ = reader.read_bytes(1, &mut buf); + let _ = reader.read_bytes(1, &mut buf, Order::Lsb0); assert_eq!([0xaa], buf); } @@ -526,10 +603,10 @@ mod tests { let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); let mut buf = [0; 1]; - let _ = reader.read_bytes(1, &mut buf); + let _ = reader.read_bytes(1, &mut buf, Order::Msb0); assert_eq!([0xaa], buf); reader.seek_last_read().unwrap(); - let _ = reader.read_bytes(1, &mut buf); + let _ = reader.read_bytes(1, &mut buf, Order::Msb0); assert_eq!([0xaa], buf); // 2 bytes (and const) @@ -537,10 +614,10 @@ mod tests { let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); let mut buf = [0; 2]; - let _ = reader.read_bytes_const::<2>(&mut buf); + let _ = reader.read_bytes_const::<2>(&mut buf, Order::Msb0); assert_eq!([0xaa, 0xbb], buf); reader.seek_last_read().unwrap(); - let _ = reader.read_bytes_const::<2>(&mut buf); + let _ = reader.read_bytes_const::<2>(&mut buf, Order::Msb0); assert_eq!([0xaa, 0xbb], buf); } @@ -550,20 +627,139 @@ mod tests { let input = hex!("ab"); let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); - let bits = reader.read_bits(4).unwrap(); + let bits = reader.read_bits(4, Order::Msb0).unwrap(); assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); reader.seek_last_read().unwrap(); - let bits = reader.read_bits(4).unwrap(); + let bits = reader.read_bits(4, Order::Msb0).unwrap(); assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); // more than byte let input = hex!("abd0"); let mut cursor = Cursor::new(input); let mut reader = Reader::new(&mut cursor); - let bits = reader.read_bits(9).unwrap(); + let bits = reader.read_bits(9, Order::Msb0).unwrap(); assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0, 1, 0, 1, 1, 1])); reader.seek_last_read().unwrap(); - let bits = reader.read_bits(9).unwrap(); + let bits = reader.read_bits(9, Order::Msb0).unwrap(); assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0, 1, 0, 1, 1, 1])); } + + #[cfg(feature = "bits")] + #[test] + fn test_bit_order() { + // Lsb0 one byte + let input = hex!("ab"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + // b + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + // a + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); + + // Msb0 one byte + let input = hex!("ab"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + // a + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); + // b + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + + // Lsb0 two bytes + let input = hex!("abcd"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + // b + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + // a + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); + // d + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 1])); + // c + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 0])); + + // Msb0 two byte + let input = hex!("abcd"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + // a + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); + // b + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + // c + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 0])); + // d + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 1])); + + // split order two bytes, lsb first + let input = hex!("abcd"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + // b + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + // a + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); + // c + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 0])); + // d + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 1])); + + // split order two bytes, msb first + let input = hex!("abcd"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + // a + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0])); + // b + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + // d + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 1])); + // c + let bits = reader.read_bits(4, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 1, 0, 0])); + + let input = hex!("ab"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + let bits = reader.read_bits(1, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1])); + let bits = reader.read_bits(3, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 0, 1, 0])); + // b + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 1])); + + // 10101011 + // | + // ||| + // |||| + let input = hex!("ab"); + let mut cursor = Cursor::new(input); + let mut reader = Reader::new(&mut cursor); + let bits = reader.read_bits(1, Order::Lsb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1])); + let bits = reader.read_bits(3, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1])); + let bits = reader.read_bits(4, Order::Msb0).unwrap(); + assert_eq!(bits, Some(bitvec![u8, Msb0; 0, 1, 0, 1])); + } } diff --git a/src/writer.rs b/src/writer.rs index bfe25769..0fa7ac87 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -9,21 +9,22 @@ use no_std_io::io::{Seek, SeekFrom, Write}; #[cfg(feature = "logging")] use log; +use crate::ctx::Order; use crate::DekuError; +#[cfg(feature = "alloc")] +use alloc::borrow::ToOwned; + const fn bits_of() -> usize { core::mem::size_of::().saturating_mul(::BITS as usize) } -/// Max bits written to [`Reader::write_bits`] during one call -pub const MAX_BITS_AMT: usize = 128; - -/// Container to use with `to_writer` +/// Container to use with `from_reader` pub struct Writer { pub(crate) inner: W, /// Leftover bits #[cfg(feature = "bits")] - pub leftover: BitVec, + pub leftover: (BitVec, Order), /// Total bits written pub bits_written: usize, } @@ -36,7 +37,7 @@ impl Seek for Writer { // clear leftover #[cfg(feature = "bits")] { - self.leftover = BitVec::new(); + self.leftover = (BitVec::new(), Order::Msb0); } self.inner.seek(pos) @@ -50,100 +51,186 @@ impl Writer { Self { inner, #[cfg(feature = "bits")] - leftover: BitVec::new(), + leftover: (BitVec::new(), Order::Msb0), bits_written: 0, } } /// Return the unused bits #[inline] + #[cfg(feature = "bits")] pub fn rest(&mut self) -> alloc::vec::Vec { - #[cfg(feature = "bits")] - { - self.leftover.iter().by_vals().collect() - } - - #[cfg(not(feature = "bits"))] - { - alloc::vec![] - } + self.leftover.0.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`. - /// - /// # Params - /// `bits` - Amount of bits that will be written. length must be <= [`MAX_BITS_AMT`]. - #[inline(never)] + /// Write all bits to `Writer` buffer if bits can fit into a byte buffer #[cfg(feature = "bits")] - pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { + #[inline] + pub fn write_bits_order( + &mut self, + bits: &BitSlice, + order: Order, + ) -> 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); + log::trace!("attempting {} bits : {}", bits.len(), bits); + + // quick return if we don't have enough bits to write to the byte buffer + if (self.leftover.0.len() + bits.len()) < 8 { + if self.leftover.1 == Order::Msb0 { + self.leftover.0.extend_from_bitslice(bits); + self.leftover.1 = order; + + #[cfg(feature = "logging")] + log::trace!( + "no write: msb pre-pending {} bits : {} => {}", + bits.len(), + bits, + self.leftover.0 + ); + } else { + let tmp = self.leftover.0.clone(); + self.leftover.0 = bits.to_owned(); + self.leftover.0.extend_from_bitslice(&tmp); + self.leftover.1 = order; + + #[cfg(feature = "logging")] + log::trace!( + "no write: lsb post-pending {} bits : {} => {}", + bits.len(), + bits, + self.leftover.0 + ); + } return Ok(()); } - // pre-pend the previous attempt to write if needed - let mut bits = if self.leftover.is_empty() { + let mut bits = if self.leftover.0.is_empty() { bits + } else if self.leftover.1 == Order::Msb0 { + #[cfg(feature = "logging")] + log::trace!( + "msb pre-pending {} bits : {}", + self.leftover.0.len(), + self.leftover.0 + ); + + self.leftover.0.extend_from_bitslice(bits); + + #[cfg(feature = "logging")] + log::trace!("now {} bits : {}", self.leftover.0.len(), self.leftover.0); + + &mut self.leftover.0 } else { #[cfg(feature = "logging")] - log::trace!("pre-pending {} bits", self.leftover.len()); + log::trace!( + "lsb post-pending {} bits : {}", + self.leftover.0.len(), + self.leftover.0 + ); - self.leftover.extend_from_bitslice(bits); - &mut self.leftover + let tmp = self.leftover.0.clone(); + self.leftover.0 = bits.to_owned(); + self.leftover.0.extend_from_bitslice(&tmp); + + #[cfg(feature = "logging")] + log::trace!("now {} bits : {}", self.leftover.0.len(), self.leftover.0); + + &mut self.leftover.0 }; - // one shot impl of BitSlice::read(no read_exact), but for no_std - let mut buf = [0; MAX_BITS_AMT]; - let buf = &mut buf[..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 let Err(e) = self.inner.write_all(buf) { - return Err(DekuError::Io(e.kind())); + if order == Order::Msb0 { + // This is taken from bitvec's std::io::Read function for BitSlice, but + // supports 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: there is no safety comment in bitvec, but assume this is safe b/c of bits + // always still pointing to it's own instance of bits (size-wise) + bits = unsafe { bits.get_unchecked(count * bits_of::()..) }; + + // TODO: with_capacity? + self.bits_written = buf.len() * 8; + self.leftover = (bits.to_bitvec(), order); + if let Err(e) = self.inner.write_all(&buf) { + return Err(DekuError::Io(e.kind())); + } + + #[cfg(feature = "logging")] + log::trace!("msb: wrote {} bits : 0x{:02x?}", buf.len() * 8, &buf); + } else { + // This is more complicated, as we need to skip the first bytes until we are "byte aligned" + // TODO: then reverse the buf before writing in the case that bits.len() > one byte buf ? + let skip_amount = bits.len() % 8; + + // This is taken from bitvec's std::io::Read function for BitSlice, but + // supports no-std + let mut buf = alloc::vec![0x00; bits.len() / 8]; + let mut count = 0; + + // SAFETY: there is no safety comment in bitvec, but assume this is safe b/c of bits + // always still pointing to it's own instance of bits (size-wise) + let inner_bits = unsafe { bits.get_unchecked(skip_amount..) }; + inner_bits + .chunks_exact(bits_of::()) + .zip(buf.iter_mut()) + .for_each(|(byte, slot)| { + *slot = byte.load_be(); + count += 1; + }); + // SAFETY: there is no safety comment in bitvec, but assume this is safe b/c of bits + // always still pointing to it's own instance of bits (size-wise) + bits = unsafe { bits.get_unchecked(..skip_amount) }; + + buf.reverse(); + + // TODO: with_capacity? + if let Err(e) = self.inner.write_all(&buf) { + return Err(DekuError::Io(e.kind())); + } + + self.bits_written = buf.len() * 8; + self.leftover = (bits.to_bitvec(), order); + + #[cfg(feature = "logging")] + log::trace!("lsb: wrote {} bits : 0x{:02x?}", buf.len() * 8, &buf); } - self.bits_written += buf.len() * 8; #[cfg(feature = "logging")] - log::trace!("wrote {} bits: {buf:02x?}", buf.len() * 8); + log::trace!( + "leftover {} bits : {}", + self.leftover.0.len(), + self.leftover.0 + ); Ok(()) } + /// Write all bits to `Writer` buffer if bits can fit into a byte buffer + #[cfg(feature = "bits")] + #[inline] + pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { + self.write_bits_order(bits, Order::Msb0) + } + /// 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()); + log::trace!("writing {} bytes", buf.len()); #[cfg(feature = "bits")] - if !self.leftover.is_empty() { + if !self.leftover.0.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. + // 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 let Err(e) = self.inner.write_all(buf) { @@ -168,18 +255,20 @@ impl Writer { #[inline] pub fn finalize(&mut self) -> Result<(), DekuError> { #[cfg(feature = "bits")] - if !self.leftover.is_empty() { + if !self.leftover.0.is_empty() { #[cfg(feature = "logging")] - log::trace!("finalized: {} bits leftover", self.leftover.len()); + log::trace!("finalized: {} bits leftover", self.leftover.0.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]; + .0 + .extend_from_bitslice(&bitvec![u8, Msb0; 0; 8 - self.leftover.0.len()]); + let mut buf = alloc::vec![0x00; self.leftover.0.len() / 8]; - // write as many leftover to the buffer. Because of the previous extend, - // this will include all the bits in self.leftover + // 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 + .0 .chunks_exact(bits_of::()) .zip(buf.iter_mut()) .for_each(|(byte, slot)| { @@ -189,10 +278,10 @@ impl Writer { if let Err(e) = self.inner.write_all(&buf) { return Err(DekuError::Io(e.kind())); } - self.bits_written += buf.len() * 8; - #[cfg(feature = "logging")] - log::trace!("finalized: wrote {} bits: {buf:02x?}", buf.len() * 8); + log::trace!("finalized: wrote {} bits", buf.len() * 8); + + self.bits_written = buf.len() * 8; } Ok(()) } @@ -256,4 +345,74 @@ mod tests { assert_eq!(&mut out_buf.into_inner(), &mut vec![0xaa]); } + + #[test] + #[cfg(feature = "bits")] + fn test_bit_order() { + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + writer + .write_bits_order(&bitvec![u8, Msb0; 1, 0, 1, 0], Order::Msb0) + .unwrap(); + writer + .write_bits_order(&bitvec![u8, Msb0; 0, 1, 0, 1], Order::Msb0) + .unwrap(); + writer.finalize(); + assert_eq!(out_buf.into_inner(), [0b1010_0101]); + + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + writer + .write_bits_order(&bitvec![u8, Msb0; 1, 0, 1, 0], Order::Lsb0) + .unwrap(); + writer + .write_bits_order(&bitvec![u8, Msb0; 0, 1, 0, 1], Order::Lsb0) + .unwrap(); + writer.finalize(); + assert_eq!(out_buf.into_inner(), [0b0101_1010]); + + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + writer + .write_bits_order(&bitvec![u8, Msb0; 1, 0, 1, 0], Order::Msb0) + .unwrap(); + writer + .write_bits_order(&bitvec![u8, Msb0; 0, 1, 0, 1], Order::Lsb0) + .unwrap(); + writer.finalize(); + assert_eq!(out_buf.into_inner(), [0b1010_0101]); + + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + writer + .write_bits_order(&bitvec![u8, Msb0; 1, 0, 1, 0, 1, 0], Order::Msb0) + .unwrap(); + writer + .write_bits_order(&bitvec![u8, Msb0; 0, 1, 0, 1, 0, 1], Order::Msb0) + .unwrap(); + writer.finalize(); + assert_eq!(out_buf.into_inner(), [0b1010_1001, 0b0101_0000]); + + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + writer + .write_bits_order(&bitvec![u8, Msb0; 1, 0, 1, 0, 1, 0], Order::Lsb0) + .unwrap(); + writer + .write_bits_order(&bitvec![u8, Msb0; 0, 1, 0, 1, 0, 1], Order::Lsb0) + .unwrap(); + writer.finalize(); + assert_eq!(out_buf.into_inner(), [0b110_1010, 0b0101_0000]); + + let mut out_buf = Cursor::new(vec![]); + let mut writer = Writer::new(&mut out_buf); + writer + .write_bits_order(&bitvec![u8, Msb0; 1, 0, 1, 0, 1, 0], Order::Lsb0) + .unwrap(); + writer + .write_bits_order(&bitvec![u8, Msb0; 0, 1, 0, 1, 0, 1], Order::Msb0) + .unwrap(); + writer.finalize(); + assert_eq!(out_buf.into_inner(), [0b0101_0110, 0b1010_0000]); + } } diff --git a/tests/bit_order.rs b/tests/bit_order.rs new file mode 100644 index 00000000..e7901d97 --- /dev/null +++ b/tests/bit_order.rs @@ -0,0 +1,474 @@ +#[cfg(feature = "bits")] +mod tests { + use assert_hex::assert_eq_hex; + use deku::ctx::{BitSize, Order}; + use deku::prelude::*; + + use std::convert::TryFrom; + use std::io::{Read, Seek, Write}; + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(id_type = "u8", bits = "2")] + #[deku(bit_order = "ctx_lsb", ctx = "ctx_lsb: Order")] + pub enum FrameType { + #[deku(id = "0")] + Management, + #[deku(id = "1")] + Control, + #[deku(id = "2")] + Data, + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(bit_order = "ctx_lsb", ctx = "ctx_lsb: Order")] + pub struct Flags { + #[deku(bits = 1)] + pub to_ds: u8, + #[deku(bits = 1)] + pub from_ds: u8, + #[deku(bits = 1)] + pub more_fragments: u8, + #[deku(bits = 1)] + pub retry: u8, + #[deku(bits = 1)] + pub power_management: u8, + #[deku(bits = 1)] + pub more_data: u8, + #[deku(bits = 1)] + pub protected_frame: u8, + #[deku(bits = 1)] + pub order: u8, + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(bit_order = "lsb")] + pub struct FrameControl { + #[deku(bits = 4)] + pub sub_type: u8, + #[deku(bits = 2)] + pub protocol_version: u8, + pub frame_type: FrameType, + + pub flags: Flags, + } + + #[test] + fn test_bit_order_frame() { + let data = vec![0x88u8, 0x41]; + let control_frame = FrameControl::try_from(data.as_ref()).unwrap(); + assert_eq!( + control_frame, + FrameControl { + protocol_version: 0, + frame_type: FrameType::Data, + sub_type: 8, + + flags: Flags { + to_ds: 1, + from_ds: 0, + more_fragments: 0, + retry: 0, + power_management: 0, + more_data: 0, + protected_frame: 1, + order: 0, + } + } + ); + + let bytes = control_frame.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(bit_order = "lsb")] + pub struct ReadGreater { + #[deku(bits = "1")] + one: u8, + #[deku(bits = "2")] + two: u8, + #[deku(bits = "4")] + three: u8, + #[deku(bits = "3")] + four: u8, + #[deku(bits = "6")] + five: u8, + } + + #[test] + fn test_bit_order_read_greater() { + let data: &[u8] = &[0b0111_1001, 0b111_11100]; + let g = ReadGreater::try_from(data).unwrap(); + + let bytes = g.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(bit_order = "lsb")] + pub struct SquashfsV3 { + #[deku(bits = "4")] + inode_type: u8, + #[deku(bits = "12")] + mode: u16, + uid: u8, + guid: u8, + mtime: u32, + inode_number: u32, + } + + #[test] + fn test_bit_order_squashfs() { + let data: &[u8] = &[ + 0x31, 0x12, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + ]; + let header = SquashfsV3::try_from(data).unwrap(); + assert_eq!( + SquashfsV3 { + inode_type: 0x01, + mode: 0x123, + uid: 0x4, + guid: 0x5, + mtime: 0x6, + inode_number: 0x7 + }, + header, + ); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + pub struct Surrounded { + one: u8, + header: SquashfsV3, + two: u8, + #[deku(bit_order = "lsb", bits = "4")] + three: u8, + #[deku(bits = "4")] + four: u8, + #[deku(bits = "4")] + five: u8, + #[deku(bit_order = "lsb", bits = "4")] + six: u8, + } + + #[test] + fn test_bit_order_surrounded() { + let data: &[u8] = &[ + 0xff, 0x31, 0x12, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xff, + 0x0f, 0x0f, + ]; + let header = Surrounded::try_from(data).unwrap(); + assert_eq!( + Surrounded { + one: 0xff, + header: SquashfsV3 { + inode_type: 0x01, + mode: 0x123, + uid: 0x4, + guid: 0x5, + mtime: 0x6, + inode_number: 0x7 + }, + two: 0xff, + three: 0xf, + four: 0x0, + five: 0x0, + six: 0xf, + }, + header + ); + + let bytes = header.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(bit_order = "lsb")] + pub struct Enums { + right: Choice, + left: Choice, + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku( + bits = "4", + id_type = "u8", + bit_order = "bit_order", + ctx = "bit_order: deku::ctx::Order" + )] + pub enum Choice { + Empty = 0x0, + Full = 0xf, + } + + #[test] + fn test_bit_order_enums() { + let data = vec![0xf0]; + let control_frame = Enums::try_from(data.as_ref()).unwrap(); + assert_eq!( + control_frame, + Enums { + right: Choice::Empty, + left: Choice::Full + } + ); + + let bytes = control_frame.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(bit_order = "lsb")] + pub struct MoreFirst { + #[deku(bits = "13")] + offset: u16, + #[deku(bits = "3")] + t: u8, + } + + #[test] + fn test_bit_order_more_first() { + let data = vec![0x40, 0x40]; + let more_first = MoreFirst::try_from(data.as_ref()).unwrap(); + assert_eq!(more_first, MoreFirst { offset: 0x40, t: 2 }); + + let bytes = more_first.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + pub struct LsbField { + #[deku(bit_order = "lsb", bits = "13")] + offset: u16, + #[deku(bit_order = "lsb", bits = "3")] + t: u8, + } + + #[test] + fn test_bit_order_lsb_field() { + let data = vec![0x40, 0x40]; + let more_first = LsbField::try_from(data.as_ref()).unwrap(); + assert_eq!(more_first, LsbField { offset: 0x40, t: 2 }); + + let bytes = more_first.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[test] + fn test_bit_order_custom_reader_writer() { + fn reader_lsb(reader: &mut Reader) -> Result<(u16, u8), DekuError> { + let first = u16::from_reader_with_ctx(reader, (BitSize(13), Order::Lsb0))?; + let second = u8::from_reader_with_ctx(reader, BitSize(3))?; + + Ok((first, second)) + } + + fn reader_msb(reader: &mut Reader) -> Result<(u16, u8), DekuError> { + let first = u16::from_reader_with_ctx(reader, (BitSize(13), Order::Msb0))?; + let second = u8::from_reader_with_ctx(reader, BitSize(3))?; + + Ok((first, second)) + } + + fn writer_lsb( + val_msb: (u16, u8), + writer: &mut Writer, + ) -> Result<(), DekuError> { + val_msb.0.to_writer(writer, (BitSize(13), Order::Lsb0))?; + val_msb.1.to_writer(writer, (BitSize(3), Order::Msb0))?; + + Ok(()) + } + + fn writer_msb( + val_msb: (u16, u8), + writer: &mut Writer, + ) -> Result<(), DekuError> { + val_msb.0.to_writer(writer, (BitSize(13), Order::Msb0))?; + val_msb.1.to_writer(writer, (BitSize(3), Order::Msb0))?; + + Ok(()) + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + pub struct Custom { + #[deku(reader = "reader_lsb(deku::reader)")] + #[deku(writer = "writer_lsb(*val_lsb, deku::writer)")] + val_lsb: (u16, u8), + #[deku(reader = "reader_msb(deku::reader)")] + #[deku(writer = "writer_msb(*val_msb, deku::writer)")] + val_msb: (u16, u8), + } + + // |lsb |msb + // | f |sss|rest f| f |sss| + let data = vec![0b0000_0000, 0b0011_1111, 0b0100_0000, 0b0011_0000]; + let more_first = Custom::try_from(data.as_ref()).unwrap(); + assert_eq!( + more_first, + Custom { + val_lsb: (0b1_1111_0000_0000, 1), + val_msb: (0b0_0110_0100_0000, 0) + } + ); + + let bytes = more_first.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(endian = "big", bit_order = "lsb")] + pub struct MoreFirstBe { + #[deku(bits = "13")] + offset: u16, + #[deku(bits = "3")] + t: u8, + } + + #[test] + fn test_bit_order_more_first_be() { + let data = vec![0x40, 0x40]; + let more_first = MoreFirstBe::try_from(data.as_ref()).unwrap(); + assert_eq!( + more_first, + MoreFirstBe { + offset: 0x4000, + t: 2 + } + ); + + let bytes = more_first.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[derive(Debug, DekuRead, DekuWrite, PartialEq)] + #[deku(endian = "little", bit_order = "lsb")] + pub struct BitOrderLittle { + #[deku(bits = 4)] + value_a: u16, + + #[deku(bits = 11)] + value_b: u16, + + #[deku(bits = 13)] + value_c: u16, + + #[deku(bits = 10)] + value_d: u16, + + #[deku(bits = 8)] + value_e: u16, + + #[deku(bits = 9)] + value_f: u16, + + #[deku(bits = 9)] + value_g: u16, + + #[deku(bits = 8)] + value_h: u16, + + #[deku(bits = 7)] + value_i: u16, + + #[deku(bits = 9)] + value_j: u16, + } + + #[test] + fn test_bit_order_little() { + let data = vec![ + 0x8B, 0xF3, 0xDC, 0x7B, 0x94, 0x38, 0x98, 0x42, 0x78, 0xB8, 0x5E, + ]; + let bit_order_little = BitOrderLittle::try_from(data.as_ref()).unwrap(); + assert_eq!( + bit_order_little, + BitOrderLittle { + value_a: 11, + value_b: 1848, + value_c: 6073, + value_d: 327, + value_e: 226, + value_f: 96, + value_g: 133, + value_h: 120, + value_i: 56, + value_j: 189, + } + ); + + let bytes = bit_order_little.to_bytes().unwrap(); + assert_eq_hex!(bytes, data); + } + + #[test] + fn test_bit_order_13() { + #[derive(DekuRead, PartialEq, Debug)] + #[deku(bit_order = "lsb")] + pub struct BitTest { + #[deku(bits = "13")] + raw_value1: u16, + #[deku(bits = "13")] + raw_value2: u16, + #[deku(bits = "6")] + raw_value3: u16, + } + + let data = vec![0b00000000, 0b00000010, 0b01000000, 0b00000000]; + + let string_data = data + .iter() + .map(|f| (format!("{:08b}", f).chars().rev().collect())) + .collect::>() + .join(""); + + assert_eq!(string_data[0..13], string_data[13..26]); + assert_eq!(string_data.chars().nth(9).unwrap(), '1'); + + assert_eq!( + BitTest { + raw_value1: 2_u16.pow(9), + raw_value2: 2_u16.pow(9), + raw_value3: 0 + }, + BitTest::try_from(data.as_slice()).unwrap() + ); + } + + #[test] + fn test_pad_bits_after() { + env_logger::init(); + #[derive(DekuRead, DekuWrite, Debug)] + #[deku(bit_order = "lsb")] + struct DekuTest { + pad: u8, + #[deku(bits = 6, pad_bits_after = "10")] + flag: u16, + sent: u8, + } + + let data = vec![0x13, 0b0011_0101, 0x0, 0xFF]; + let (_, dt) = DekuTest::from_bytes((&data, 0)).unwrap(); + let to_bytes = dt.to_bytes().unwrap(); + assert_eq!(dt.flag, 0b110101); + assert_eq!(to_bytes, data); + } + + #[test] + fn test_pad_bits_before() { + #[derive(DekuRead, DekuWrite, Debug)] + #[deku(bit_order = "lsb")] + struct DekuTest { + pad: u8, + #[deku(bits = 6, pad_bits_before = "10")] + flag: u16, + sent: u8, + } + + let data = vec![0x13, 0x0, 0b0111_0100, 0xFF]; + let (_, dt) = DekuTest::from_bytes((&data, 0)).unwrap(); + let to_bytes = dt.to_bytes().unwrap(); + assert_eq!(dt.flag, 0b111_01); + assert_eq!(to_bytes, data); + } +} diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs index a5348d6e..eee33196 100644 --- a/tests/test_alloc.rs +++ b/tests/test_alloc.rs @@ -82,7 +82,7 @@ mod tests { t.to_bytes().unwrap(); }) .0, - (2, 1, 2) + (3, 1, 3) ); } @@ -100,7 +100,7 @@ mod tests { t.to_slice(&mut out).unwrap(); }) .0, - (1, 0, 1) + (2, 0, 2) ); } } diff --git a/tests/test_compile/cases/internal_variables.rs b/tests/test_compile/cases/internal_variables.rs index 74422cac..d5e8544d 100644 --- a/tests/test_compile/cases/internal_variables.rs +++ b/tests/test_compile/cases/internal_variables.rs @@ -50,7 +50,7 @@ struct TestMap { } fn dummy_reader( - offset: usize, + _offset: usize, _reader: &mut Reader, ) -> Result { Ok(0) @@ -72,7 +72,10 @@ struct TestCtx { field_b: ChildCtx, } -fn dummy_writer(_offset: usize, _writer: &mut deku::writer::Writer) -> Result<(), DekuError> { +fn dummy_writer( + _offset: usize, + _writer: &mut deku::writer::Writer, +) -> Result<(), DekuError> { Ok(()) } #[derive(DekuRead, DekuWrite)] diff --git a/tests/test_compile/cases/internal_variables.stderr b/tests/test_compile/cases/internal_variables.stderr index a1b76aac..ff97e28a 100644 --- a/tests/test_compile/cases/internal_variables.stderr +++ b/tests/test_compile/cases/internal_variables.stderr @@ -1,29 +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:88:19 + --> tests/test_compile/cases/internal_variables.rs:91:19 | -88 | #[deku(cond = "__deku_bit_offset == *field_a as usize")] +91 | #[deku(cond = "__deku_bit_offset == *field_a as usize")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `W: Seek` is not satisfied - --> tests/test_compile/cases/internal_variables.rs:75:66 - | -75 | fn dummy_writer(_offset: usize, _writer: &mut deku::writer::Writer) -> Result<(), DekuError> { - | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Seek` is not implemented for `W` - | -note: required by a bound in `Writer` - --> src/writer.rs - | - | pub struct Writer { - | ^^^^ required by this bound in `Writer` -help: consider further restricting this bound - | -75 | fn dummy_writer(_offset: usize, _writer: &mut deku::writer::Writer) -> Result<(), DekuError> { - | +++++++++++++++ - -warning: unused variable: `offset` - --> tests/test_compile/cases/internal_variables.rs:53:5 - | -53 | offset: usize, - | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_offset` - | - = note: `#[warn(unused_variables)]` on by default