From b3a8778bc415df99576aa82c6696309674197202 Mon Sep 17 00:00:00 2001 From: wcampbell Date: Wed, 21 Aug 2024 00:12:04 -0400 Subject: [PATCH] Add bit_order attribute --- deku-derive/src/lib.rs | 21 +- deku-derive/src/macros/deku_read.rs | 84 +++-- deku-derive/src/macros/deku_write.rs | 53 ++- deku-derive/src/macros/mod.rs | 57 +++- examples/ieee.rs | 73 +++++ src/attributes.rs | 79 +++++ src/ctx.rs | 9 + src/impls/nonzero.rs | 31 ++ src/impls/primitive.rs | 463 +++++++++++++++++++++++--- src/lib.rs | 2 +- src/reader.rs | 101 ++++-- src/writer.rs | 290 ++++++++++++---- tests/bit_order.rs | 474 +++++++++++++++++++++++++++ tests/test_alloc.rs | 4 +- 14 files changed, 1553 insertions(+), 188 deletions(-) create mode 100644 examples/ieee.rs create mode 100644 tests/bit_order.rs diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs index 83470752..4e5d0bf7 100644 --- a/deku-derive/src/lib.rs +++ b/deku-derive/src/lib.rs @@ -140,6 +140,9 @@ struct DekuData { /// enum only: byte size of the enum `id` bytes: Option, + + /// Bit Order for all fields + bit_order: Option, } impl DekuData { @@ -188,6 +191,7 @@ impl DekuData { id_type: receiver.id_type?, bits: receiver.bits, bytes: receiver.bytes, + bit_order: receiver.bit_order, }; DekuData::validate(&data)?; @@ -322,6 +326,7 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataEnum<'a> { deku_data.endian.as_ref(), deku_data.bits.as_ref(), deku_data.bytes.as_ref(), + deku_data.bit_order.as_ref(), )?; Ok(Self { @@ -439,11 +444,14 @@ 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, + + /// Bit Order of field + bit_order: Option, } impl FieldData { @@ -481,6 +489,7 @@ impl FieldData { cond: receiver.cond?, assert: receiver.assert?, assert_eq: receiver.assert_eq?, + bit_order: receiver.bit_order, }; FieldData::validate(&data)?; @@ -668,6 +677,10 @@ struct DekuReceiver { /// enum only: byte size of the enum `id` #[darling(default)] bytes: Option, + + /// Bit Order of field + #[darling(default)] + bit_order: Option, } type ReplacementError = TokenStream; @@ -848,6 +861,10 @@ struct DekuFieldReceiver { // assert value of field #[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")] assert_eq: 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 a7527027..dc8fb017 100644 --- a/deku-derive/src/macros/deku_read.rs +++ b/deku-derive/src/macros/deku_read.rs @@ -4,10 +4,12 @@ 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, pad_bits, token_contains_string, wrap_default_ctx, + gen_bit_order_from_str, gen_ctx_types_and_arg, gen_field_args, gen_internal_field_ident, + gen_internal_field_idents, gen_type_from_ctx_id, pad_bits, token_contains_string, + wrap_default_ctx, }; use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id}; @@ -512,28 +514,60 @@ fn emit_bit_byte_offsets( (bit_offset, byte_offset) } -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 = 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! { + { + println!("{:?}", #order); + 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::Msb0)?; + } else { + // TODO: use skip_bits, or Seek in the future? + let _ = __deku_reader.read_bits(__deku_pad, ::#crate_::ctx::Order::Msb0)?; + } } } } @@ -550,6 +584,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; @@ -619,6 +654,7 @@ fn emit_field_read( f.bits.as_ref(), 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. @@ -710,11 +746,13 @@ fn emit_field_read( let pad_bits_before = pad_bits( f.pad_bits_before.as_ref(), f.pad_bytes_before.as_ref(), + field_bit_order, emit_padding, ); let pad_bits_after = 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 871d1623..6abe4a3a 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, pad_bits, - token_contains_string, wrap_default_ctx, + assertion_failed, gen_bit_order_from_str, gen_ctx_types_and_arg, gen_field_args, + gen_struct_destruction, pad_bits, token_contains_string, wrap_default_ctx, }; use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id}; @@ -410,20 +411,38 @@ fn emit_bit_byte_offsets( (bit_offset, byte_offset) } -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())?; + } } } } @@ -437,6 +456,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()); // fields to check usage of bit/byte offset let field_check_vars = [ @@ -492,6 +512,7 @@ fn emit_field_write( f.bits.as_ref(), f.bytes.as_ref(), f.ctx.as_ref(), + field_bit_order, )?; if f.temp { @@ -512,11 +533,13 @@ fn emit_field_write( let pad_bits_before = pad_bits( f.pad_bits_before.as_ref(), f.pad_bytes_before.as_ref(), + field_bit_order, emit_padding, ); let pad_bits_after = 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 847db153..02441b7b 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; @@ -250,17 +250,24 @@ pub(crate) fn gen_id_args( endian: Option<&syn::LitStr>, bits: Option<&Num>, bytes: Option<&Num>, + 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()?; // 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}), @@ -277,18 +284,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[..] { @@ -297,6 +313,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(); @@ -343,14 +373,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/ieee.rs b/examples/ieee.rs new file mode 100644 index 00000000..30ef4371 --- /dev/null +++ b/examples/ieee.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 ba66f25f..02aceed9 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 | [assert](#assert) | field | Assert a condition | [assert_eq](#assert_eq) | field | Assert equals on the field @@ -143,6 +144,84 @@ 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: u32, + #[deku(bits = "12")] + mode: u32, + #[deku(bits = "8")] + uid: u32, + #[deku(bits = "8")] + guid: u32, + mtime: u32, + inode_number: u32, +} +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, +); +``` +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..9c01f754 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -13,6 +13,15 @@ pub enum Endian { Big, } +/// Bit numbering +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Order { + /// Most significant bit + 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 62e65397..648d90fb 100644 --- a/src/impls/nonzero.rs +++ b/src/impls/nonzero.rs @@ -10,6 +10,25 @@ use crate::reader::Reader; use crate::writer::Writer; use crate::{DekuError, DekuReader, DekuWriter}; +macro_rules! ImplDekuTraitsCtxOrder { + ($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, + $ctx_arg: $ctx_type, + ) -> Result { + let value = <$readtype>::from_reader_with_ctx(reader, $ctx_arg)?; + let value = <$typ>::new(value); + + match value { + None => Err(DekuError::Parse(Cow::from(format!("NonZero assertion")))), + Some(v) => Ok(v), + } + } + } + }; +} + macro_rules! ImplDekuTraitsCtx { ($typ:ty, $readtype:ty, $ctx_arg:tt, $ctx_type:tt) => { impl DekuReader<'_, $ctx_type> for $typ { @@ -45,6 +64,18 @@ macro_rules! ImplDekuTraits { ImplDekuTraitsCtx!($typ, $readtype, (), ()); ImplDekuTraitsCtx!($typ, $readtype, (endian, bitsize), (Endian, BitSize)); ImplDekuTraitsCtx!($typ, $readtype, (endian, bytesize), (Endian, ByteSize)); + ImplDekuTraitsCtxOrder!( + $typ, + $readtype, + (endian, bitsize, order), + (Endian, BitSize, Order) + ); + ImplDekuTraitsCtxOrder!( + $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 17184c87..b15f2204 100644 --- a/src/impls/primitive.rs +++ b/src/impls/primitive.rs @@ -47,16 +47,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), ReaderRet::Bits(bits) => { @@ -77,7 +77,20 @@ 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::Msb0)) + } +} + +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(); @@ -105,7 +118,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(()) } } @@ -126,6 +153,124 @@ impl DekuWriter<(Endian, ByteSize)> for u8 { macro_rules! ImplDekuReadBits { ($typ:ty, $inner:ty) => { + 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 = 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)) + } + } + } + impl DekuRead<'_, (Endian, BitSize)> for $typ { #[inline(never)] fn read( @@ -215,7 +360,7 @@ macro_rules! ImplDekuReadBits { size.0 )))); } - let bits = reader.read_bits(size.0)?; + let bits = reader.read_bits(size.0, Order::Msb0)?; let Some(bits) = bits else { return Err(DekuError::Parse(Cow::from("no bits read from reader"))); }; @@ -223,11 +368,46 @@ macro_rules! ImplDekuReadBits { Ok(a.1) } } + + 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 + 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, ByteSize)>>::read(input, (endian, size)) + } + } + impl DekuRead<'_, (Endian, ByteSize)> for $typ { #[inline(never)] fn read( @@ -251,11 +431,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 { @@ -265,7 +445,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() { @@ -295,53 +475,50 @@ macro_rules! ImplDekuReadBytes { macro_rules! ImplDekuReadSignExtend { ($typ:ty, $inner:ty) => { - impl DekuRead<'_, (Endian, ByteSize)> for $typ { - #[inline(never)] + // Ignore Order, send back + 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, ByteSize)>>::read(input, (endian, size)) + } + } + + 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, + 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()?) - } - } - 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)) } } @@ -363,10 +540,20 @@ macro_rules! ImplDekuReadSignExtend { } impl DekuReader<'_, (Endian, BitSize)> for $typ { - #[inline(always)] + #[inline] fn from_reader_with_ctx( reader: &mut Reader, (endian, size): (Endian, BitSize), + ) -> Result<$typ, DekuError> { + <$typ>::from_reader_with_ctx(reader, (endian, size, Order::Msb0)) + } + } + + 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 { @@ -375,19 +562,77 @@ 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::Msb0))?; Ok(a.1) } } + + // TODO: Remove + impl DekuReader<'_, (Endian, ByteSize, Order)> for $typ { + #[inline] + fn from_reader_with_ctx( + reader: &mut Reader, + (endian, size, order): (Endian, ByteSize, Order), + ) -> Result<$typ, DekuError> { + let mut buf = [0; core::mem::size_of::<$typ>()]; + let ret = reader.read_bytes(size.0, &mut buf, order)?; + let a = match ret { + ReaderRet::Bits(bits) => { + let Some(bits) = bits else { + return Err(DekuError::Parse(Cow::from( + "no bits read from reader".to_string(), + ))); + }; + let a = <$typ>::read(&bits, (endian, size))?; + a.1 + } + ReaderRet::Bytes => { + if endian.is_le() { + <$typ>::from_le_bytes(buf.try_into()?) + } else { + <$typ>::from_be_bytes(buf.try_into()?) + } + } + }; + + 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) + } + } }; } 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::Msb0)) + } + } + // Only have `endian`, specialize and use read_bytes_const impl DekuReader<'_, Endian> for $typ { #[inline(always)] @@ -397,7 +642,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::Msb0)?; let a = match ret { ReaderRet::Bytes => { if endian.is_le() { @@ -449,6 +694,33 @@ macro_rules! ForwardDekuRead { } } + //// Only have `bit_size`, set `endian` to `Endian::default`. + 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( @@ -463,6 +735,63 @@ macro_rules! ForwardDekuRead { macro_rules! ImplDekuWrite { ($typ:ty) => { + 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(()) + } + } + impl DekuWriter<(Endian, BitSize)> for $typ { #[inline(always)] fn to_writer( @@ -564,6 +893,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)) + } + } }; } @@ -589,6 +930,29 @@ macro_rules! ImplDekuWriteOnlyEndian { macro_rules! ForwardDekuWrite { ($typ:ty) => { + 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)) + } + } + impl DekuWriter for $typ { #[inline(always)] fn to_writer( @@ -611,6 +975,17 @@ macro_rules! ForwardDekuWrite { } } + impl DekuWriter for $typ { + #[inline] + 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(&self, writer: &mut Writer, _: ()) -> Result<(), DekuError> { diff --git a/src/lib.rs b/src/lib.rs index b2fec920..564eb98b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -570,7 +570,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 19283e8a..ea06ae9a 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -5,7 +5,7 @@ use core::cmp::Ordering; use bitvec::prelude::*; use no_std_io::io::{ErrorKind, Read}; -use crate::{prelude::NeedSize, DekuError}; +use crate::{ctx::Order, prelude::NeedSize, DekuError}; use alloc::vec::Vec; #[cfg(feature = "logging")] @@ -119,7 +119,7 @@ impl<'a, R: Read> 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::Msb0)?; Ok(()) } @@ -135,7 +135,11 @@ impl<'a, R: Read> Reader<'a, R> { /// # Params /// `amt` - Amount of bits that will be read. Must be <= [`MAX_BITS_AMT`]. #[inline(never)] - 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"); if amt == 0 { @@ -154,10 +158,10 @@ impl<'a, R: Read> Reader<'a, R> { // previous read was not enough to satisfy the amt requirement, return all previously Ordering::Greater => { // read bits - ret.extend_from_bitslice(&self.leftover); + // ret.extend_from_bitslice(&self.leftover); // calculate the amount of bytes we need to read to read enough bits - let bits_left = amt - self.leftover.len(); + let mut bits_left = amt - self.leftover.len(); let mut bytes_len = bits_left / 8; if (bits_left % 8) != 0 { bytes_len += 1; @@ -177,20 +181,61 @@ impl<'a, R: Read> 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); - core::mem::swap(&mut not_needed.to_bitvec(), &mut self.leftover); + let mut rest = BitSlice::try_from_slice(read_buf).unwrap(); + + #[cfg(feature = "logging")] + log::trace!("read_bits: bits: {}", rest); + + // 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) = rest.split_at(bits_left - (bits_left % 8)); + bits_left -= bits_left - (bits_left % 8); + front_bits = Some(used); + rest = more; + } - // create return - ret.extend_from_bitslice(rest); + match order { + Order::Lsb0 => { + let (rest, used) = rest.split_at(rest.len() - bits_left); + ret.extend_from_bitslice(used); + if let Some(front_bits) = front_bits { + ret.extend_from_bitslice(front_bits); + } + ret.extend_from_bitslice(&self.leftover); + + self.leftover = rest.to_bitvec(); + } + Order::Msb0 => { + let (rest, not_needed) = rest.split_at(bits_left); + // TODO: test + if let Some(front_bits) = front_bits { + ret.extend_from_bitslice(front_bits); + } + ret.extend_from_bitslice(&self.leftover); + ret.extend_from_bitslice(rest); + + core::mem::swap(&mut not_needed.to_bitvec(), &mut self.leftover); + } + } } // 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 => { - let used = self.leftover.split_off(amt); - ret.extend_from_bitslice(&self.leftover); - self.leftover = used; - } + Ordering::Less => match order { + Order::Lsb0 => { + let used = self.leftover.split_off(self.leftover.len() - amt); + ret.extend_from_bitslice(&used); + } + Order::Msb0 => { + let used = self.leftover.split_off(amt); + ret.extend_from_bitslice(&self.leftover); + self.leftover = used; + } + }, } self.bits_read += ret.len(); @@ -207,7 +252,12 @@ impl<'a, R: Read> 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"); if self.leftover.is_empty() { @@ -228,7 +278,7 @@ impl<'a, R: Read> Reader<'a, R> { Ok(ReaderRet::Bytes) } else { - Ok(ReaderRet::Bits(self.read_bits(amt * 8)?)) + Ok(ReaderRet::Bits(self.read_bits(amt * 8, order)?)) } } @@ -242,6 +292,7 @@ impl<'a, R: Read> Reader<'a, R> { pub fn read_bytes_const( &mut self, buf: &mut [u8; N], + order: Order, ) -> Result { #[cfg(feature = "logging")] log::trace!("read_bytes: requesting {N} bytes"); @@ -260,7 +311,7 @@ impl<'a, R: Read> Reader<'a, R> { Ok(ReaderRet::Bytes) } else { - Ok(ReaderRet::Bits(self.read_bits(N * 8)?)) + Ok(ReaderRet::Bits(self.read_bits(N * 8, order)?)) } } } @@ -278,16 +329,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()); } @@ -296,9 +347,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] @@ -307,7 +358,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); } } diff --git a/src/writer.rs b/src/writer.rs index bf4ed92e..547a945e 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -7,20 +7,21 @@ use no_std_io::io::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 - pub leftover: BitVec, + pub leftover: (BitVec, Order), /// Total bits written pub bits_written: usize, } @@ -31,7 +32,7 @@ impl Writer { pub fn new(inner: W) -> Self { Self { inner, - leftover: BitVec::new(), + leftover: (BitVec::new(), Order::Msb0), bits_written: 0, } } @@ -39,82 +40,174 @@ impl Writer { /// Return the unused bits #[inline] pub fn rest(&mut self) -> alloc::vec::Vec { - self.leftover.iter().by_vals().collect() + 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)] - pub fn write_bits(&mut self, bits: &BitSlice) -> Result<(), DekuError> { + /// Write all bits to `Writer` buffer if bits can fit into a byte buffer + #[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 + ); + + 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); - self.leftover.extend_from_bitslice(bits); - &mut self.leftover + &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 + #[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()); - 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) { @@ -130,18 +223,20 @@ impl Writer { /// into a byte buffer #[inline] pub fn finalize(&mut self) -> Result<(), DekuError> { - 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)| { @@ -151,10 +246,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(()) } @@ -203,4 +298,73 @@ mod tests { &mut vec![0xaa, 0xbb, 0xf1, 0xaa, 0x1f, 0x1a, 0xaf] ); } + + #[test] + fn test_bit_order() { + let mut out_buf = 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, [0b1010_0101]); + + let mut out_buf = 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, [0b0101_1010]); + + let mut out_buf = 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, [0b1010_0101]); + + let mut out_buf = 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, [0b1010_1001, 0b0101_0000]); + + let mut out_buf = 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, [0b110_1010, 0b0101_0000]); + + let mut out_buf = 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, [0b0101_0110, 0b1010_0000]); + } } diff --git a/tests/bit_order.rs b/tests/bit_order.rs new file mode 100644 index 00000000..4943ff77 --- /dev/null +++ b/tests/bit_order.rs @@ -0,0 +1,474 @@ +use assert_hex::assert_eq_hex; +use deku::ctx::{BitSize, 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, +} + +#[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: u32, + #[deku(bits = "12")] + mode: u32, + #[deku(bits = "8")] + uid: u32, + #[deku(bits = "8")] + guid: u32, + 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(""); + + println!("string_data: {}", string_data); + + 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 86072b47..16d5b673 100644 --- a/tests/test_alloc.rs +++ b/tests/test_alloc.rs @@ -75,7 +75,7 @@ mod tests { t.to_bytes().unwrap(); }) .0, - (2, 1, 2) + (3, 1, 3) ); } @@ -92,7 +92,7 @@ mod tests { t.to_slice(&mut out).unwrap(); }) .0, - (1, 0, 1) + (2, 0, 2) ); } }