Repository: sharksforarms/deku (rust)

Issue:
Enums which have variant `id_pat = "_"` ignore `BitSize` ctx
I have an enum like this:

```rust
#[deku(id_type = "u8", bits = 3)]
pub enum MSCm2RFClass {
    #[deku(id_pat = "0")] Class1,
    #[deku(id_pat = "1")] Class2,
    #[deku(id_pat = "2")] Class3,
    #[deku(id_pat = "3")] Class4,
    #[deku(id_pat = "4")] Class5,
    #[deku(id_pat = "_")] Other(u8),
}
```

Ideally, I'd want values 0-4 stored as their respective class values, and then the other remaining 3-bit values (5, 6, 7) to be stored as `Other(val)`. However, what actually happens is the `BitSize` context is ignored, an entire byte is read, and I end up with values like `Other(255)`.

The generated code looks something like this:

```rust
            let __deku_value = match &__deku_variant_id {
                0 => Self::Class1,
                1 => Self::Class2,
                2 => Self::Class3,
                3 => Self::Class4,
                4 => Self::Class5,
                _ => {
                    {
                        let lvl = ::log::Level::Trace;
                        if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
                            ::log::__private_api::log(
                                format_args!("Reading: {0}.{1}", "MSCm2RFClass", "field_0"),
                                lvl,
                                &(
                                    "pycrate_rs::nas::generated::emm::emmtrackingareaupdaterequest",
                                    "pycrate_rs::nas::generated::emm::emmtrackingareaupdaterequest",
                                    ::log::__private_api::loc(),
                                ),
                                (),
                            );
                        }
                    };
                    let __deku___field_0 = {
                        let __deku_value = {
                            if let Err(e) = __deku_reader.seek_last_read() {
                                return Err(::deku::DekuError::Io(e.kind()));
                            }
                            <u8 as ::deku::DekuReader<
                                '_,
                                _,
                            >>::from_reader_with_ctx(__deku_reader, ())?
                        };
```

which passes `()` for the ctx in the `_` case.

Pull request:
Remove re-reading for ip_pat
* Use the last read value for the id instead of jumping back and re-reading that value.
* Compare the endian, bits,and bytes amt when reading from an id. This is important when writing
  as without this check you end up with different bytes being written. More fields could
  be checked against this?
* Reader: Remove last_bits_read_amt and seek_last_read

Should close #533 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c645ddd..9be46f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,82 @@
 
 ## [Unreleased]
 
-## [0.18.1] - 2024-08-09
+## [0.19.0] - 2025-02-07
+
+## Features
+### Added `bit_order` ([#483](https://github.com/sharksforarms/deku/pull/483/files))
+After much development, `bit_order` is now a supported attribute! `Msb` is still the default bit-order for all operations,
+but if you need `Lsb` ordering, deku now has you covered.
+
+For example, the following is now possible:
+```rs
+# #[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,
+];
+```
+
+If you were using our internal `reader` the following changed. As well as the `leftover` field now containing ordering.
+```diff
+- reader.read_bytes(exact.0, &mut bytes)?;
++ reader.read_bytes(exact.0, &mut bytes, Order::Lsb0)?;
+```
+
+### Added bytes ctx for `CString` ([#497](https://github.com/sharksforarms/deku/pull/497))
+```rs
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+pub struct Data {
+    len: u8,
+    #[deku(bytes = "*len as usize")]
+    s: CString,
+}
+```
 
+### Changed `id_pat` ([#540](https://github.com/sharksforarms/deku/pull/540))
+The `id_pat` attribute has been restored to the behavior of `0.16.0`, removing the seek and re-read.
+Added the requirement of the type used to read the id needs to be the same as the storage unit and force no attributes.
+
+### Allow token streams for `bytes` attribute ([#489](https://github.com/sharksforarms/deku/pull/489))
+```rs
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    field_a_size: u8,
+    #[deku(bytes = "*field_a_size as usize")]
+    field_a: u32,
+}
+```
+
+### Updates
+* `Arc` support by [@vhdirk](https://github.com/vhdirk) ([#522](https://github.com/sharksforarms/deku/pull/522))
+* Update `no-std-io2` to 0.9.0 and Updated `MSRV` to `1.81` ([#529](https://github.com/sharksforarms/deku/pull/529))
+* Add field support for `magic` attribute ([#503](https://github.com/sharksforarms/deku/pull/503))
+* Add `NoSeek` type for unseekable `Read` + `Write` impls ([#487](https://github.com/sharksforarms/deku/pull/487))
+* Add performance specializations for `count` attribute + `Vec<u8>`([#481](https://github.com/sharksforarms/deku/pull/481))
+* `Reader` may now take ownership of `Read + Seek` type. Thanks [@wgreenburg](https://github.com/wgreenburg) ([#521](https://github.com/sharksforarms/deku/pull/521)).
+
+
+## Fixes
 - Fix pad_* attributes in no_std ([#478](https://github.com/sharksforarms/deku/pull/478))
 - Fix bug with id_pat and read_bytes_const ([#479](https://github.com/sharksforarms/deku/pull/479))
+- Several fixes for scope and imports in proc-macros, thanks [@Serial-ATA](https://github.com/Serial-ATA) ([#541](https://github.com/sharksforarms/deku/pull/541), [#538](https://github.com/sharksforarms/deku/pull/538), [#539](https://github.com/sharksforarms/deku/pull/539))
 
 ## [0.18.0] - 2024-08-07
 
@@ -490,7 +562,9 @@ Community:
 - Added `release.toml`
 - Added `CHANGELOG.md` to track changes
 
-[Unreleased]: https://github.com/sharksforarms/deku/compare/deku-v0.18.1...HEAD
+[Unreleased]: https://github.com/sharksforarms/deku/compare/deku-v0.19.0...HEAD
+
+[0.19.0]: https://github.com/sharksforarms/deku/compare/deku-v0.18.1...deku-v0.19.0
 
 [0.18.1]: https://github.com/sharksforarms/deku/compare/deku-v0.18.0...deku-v0.18.1
 
diff --git a/README.md b/README.md
index 6bc2bc3..2bee350 100644
--- a/README.md
+++ b/README.md
@@ -21,13 +21,13 @@ for binary structs or network headers
 
 ```toml
 [dependencies]
-deku = "0.18"
+deku = "0.19"
 ```
 
 no_std:
 ```toml
 [dependencies]
-deku = { version = "0.18", default-features = false, features = ["alloc"] }
+deku = { version = "0.19", default-features = false, features = ["alloc"] }
 ```
 
 ## Example
diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs
index 6857d2a..942583e 100644
--- a/deku-derive/src/lib.rs
+++ b/deku-derive/src/lib.rs
@@ -100,12 +100,26 @@ impl ToTokens for Num {
 impl FromMeta for Num {
     fn from_value(value: &syn::Lit) -> darling::Result<Self> {
         (match *value {
-            syn::Lit::Str(ref s) => Ok(Num::TokenStream(
-                apply_replacements(s)
-                    .map_err(darling::Error::custom)?
-                    .parse::<TokenStream>()
-                    .expect("could not parse token stream"),
-            )),
+            syn::Lit::Str(ref s) => {
+                let str_value = s.value();
+
+                // Attempt to read ("2") as a Num LitInt (2)
+                match str_value.parse::<u64>() {
+                    Ok(int_value) => {
+                        let int_string = int_value.to_string();
+                        let span = s.span();
+                        let lit_int = syn::LitInt::new(&int_string, span);
+                        Ok(Num::new(lit_int))
+                    }
+                    // else, just a tokenstream
+                    Err(_) => Ok(Num::TokenStream(
+                        apply_replacements(s)
+                            .map_err(darling::Error::custom)?
+                            .parse::<TokenStream>()
+                            .expect("could not parse token stream"),
+                    )),
+                }
+            }
             syn::Lit::Int(ref s) => Ok(Num::new(s.clone())),
             _ => Err(darling::Error::unexpected_lit_type(value)),
         })
@@ -516,6 +530,61 @@ struct FieldData {
 }
 
 impl FieldData {
+    pub fn any_field_set(&self) -> bool {
+        // NOTE: Ignore ident
+        let mut any_option_set = self.endian.is_some();
+
+        #[cfg(feature = "bits")]
+        {
+            any_option_set = any_option_set || self.bits.is_some();
+        }
+
+        any_option_set = any_option_set || self.bytes.is_some() || self.count.is_some();
+
+        #[cfg(feature = "bits")]
+        {
+            any_option_set = any_option_set || self.bits_read.is_some();
+        }
+
+        any_option_set = any_option_set
+            || self.bytes_read.is_some()
+            || self.until.is_some()
+            || self.map.is_some()
+            || self.ctx.is_some()
+            || self.update.is_some()
+            || self.reader.is_some()
+            || self.writer.is_some();
+
+        #[cfg(feature = "bits")]
+        {
+            any_option_set = any_option_set || self.pad_bits_before.is_some();
+        }
+
+        any_option_set = any_option_set || self.pad_bytes_before.is_some();
+
+        #[cfg(feature = "bits")]
+        {
+            any_option_set = any_option_set || self.pad_bits_after.is_some();
+        }
+
+        // NOTE: Ignore default
+        any_option_set = any_option_set
+            || self.pad_bytes_after.is_some()
+            || self.temp_value.is_some()
+            || self.cond.is_some()
+            || self.assert.is_some()
+            || self.assert_eq.is_some()
+            || self.seek_from_current.is_some()
+            || self.seek_from_end.is_some()
+            || self.seek_from_start.is_some()
+            || self.bit_order.is_some()
+            || self.magic.is_some();
+
+        let any_bool_set = self.read_all || self.skip || self.temp || self.seek_rewind;
+
+        any_option_set || any_bool_set
+    }
+
     fn from_receiver(receiver: DekuFieldReceiver) -> Result<Self, TokenStream> {
         let ctx = receiver
             .ctx?
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index c9ee907..5ae5b7f 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -236,7 +236,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             .and_then(|v| v.ident.as_ref())
             .is_some();
 
-        let mut restore = false;
+        let mut pad_id = false;
         let variant_id = if let Some(variant_id) = &variant.id {
             match variant_id {
                 Id::TokenStream(v) => quote! {&#v}.into_token_stream(),
@@ -248,8 +248,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             // If user has supplied an id, then we have an id_pat that and the id variant doesn't
             // need read into an id value
             if id.is_none() {
-                // if id_pat and !id, we need to restore after reading
-                restore = true;
+                pad_id = true;
                 variant_id_pat.clone()
             } else {
                 variant_id_pat.clone()
@@ -279,8 +278,17 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let variant_read_func = if variant_reader.is_some() {
             quote! { #variant_reader; }
         } else {
+            // VarDefault { id: u8, value: u8 }, is allowed
+            // VarDefault, is not, if we need to store the id
+            if pad_id && variant.id.is_none() && variant.fields.is_empty() {
+                // TODO: This would be nice to point to the field
+                return Err(syn::Error::new(
+                    input.ident.span(),
+                    "DekuRead: id_pat requires storage field",
+                ));
+            }
             let (field_idents, field_reads) =
-                emit_field_reads(input, &variant.fields.as_ref(), &ident, restore)?;
+                emit_field_reads(input, &variant.fields.as_ref(), &ident, pad_id)?;
 
             // filter out temporary fields
             let field_idents = field_idents
@@ -371,8 +379,6 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
     };
 
     let variant_read = quote! {
-        __deku_reader.last_bits_read_amt = 0;
-        let __deku_last_leftover = __deku_reader.leftover.clone();
         #variant_id_read
 
         #(#pre_match_tokens)*
@@ -673,7 +679,7 @@ fn emit_field_read(
     i: usize,
     f: &FieldData,
     ident: &TokenStream,
-    restore_pad_id: bool,
+    pad_id: bool,
 ) -> Result<(TokenStream, TokenStream), syn::Error> {
     let crate_ = super::get_crate_name();
     let field_type = &f.ty;
@@ -822,19 +828,16 @@ fn emit_field_read(
             quote!(<#field_type as ::#crate_::DekuReader<'_, _>>)
         };
 
-        if restore_pad_id {
+        if pad_id {
+            if f.any_field_set() {
+                // TODO: This would be nice to point to the field
+                return Err(syn::Error::new(
+                    input.ident.span(),
+                    "DekuRead: id_pat id storage cannot have attributes",
+                ));
+            }
             quote! {
-                {
-                    if let Err(e) = __deku_reader.seek_last_read() {
-                        return Err(::#crate_::DekuError::Io(e.kind()));
-                    }
-                    __deku_reader.leftover = __deku_last_leftover;
-                    #type_as_deku_read::from_reader_with_ctx
-                    (
-                        __deku_reader,
-                        (#read_args)
-                    )?
-                }
+                __deku_variant_id;
             }
         } else if let Some(field_count) = &f.count {
             use syn::{GenericArgument, PathArguments, Type};
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index 8444038..d569cf4 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -77,7 +77,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
 
     let magic_write = emit_magic_write(input);
 
-    let field_writes = emit_field_writes(input, &fields, None, &ident)?;
+    let field_writes = emit_field_writes(input, &fields, false, None, &ident)?;
     let field_updates = emit_field_updates(&fields, Some(quote! { self. }));
 
     let named = fields.style.is_struct();
@@ -286,7 +286,13 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let variant_write = if variant_writer.is_some() {
             quote! { #variant_writer ?; }
         } else {
-            let field_writes = emit_field_writes(input, &variant.fields.as_ref(), None, &ident)?;
+            let field_writes = emit_field_writes(
+                input,
+                &variant.fields.as_ref(),
+                variant.id_pat.is_some(),
+                None,
+                &ident,
+            )?;
 
             quote! {
                 {
@@ -421,13 +427,24 @@ fn emit_magic_write(input: &DekuData) -> TokenStream {
 fn emit_field_writes(
     input: &DekuData,
     fields: &Fields<&FieldData>,
+    is_id_pat: bool,
     object_prefix: Option<TokenStream>,
     ident: &TokenStream,
 ) -> Result<Vec<TokenStream>, syn::Error> {
+    // VarDefault { id: u8, value: u8 }, is allowed
+    // VarDefault, is not, if we need to store the id
+    if is_id_pat && input.id.is_none() && fields.is_empty() {
+        // TODO: This would be nice to point to the field
+        return Err(syn::Error::new(
+            input.ident.span(),
+            "DekuRead: id_pat requires storage field",
+        ));
+    }
+    let mut is_id_pat = is_id_pat;
     fields
         .iter()
         .enumerate()
-        .map(|(i, f)| emit_field_write(input, i, f, &object_prefix, ident))
+        .map(|(i, f)| emit_field_write(input, i, f, &object_prefix, ident, &mut is_id_pat))
         .collect()
 }
 
@@ -560,7 +577,36 @@ fn emit_field_write(
     f: &FieldData,
     object_prefix: &Option<TokenStream>,
     ident: &TokenStream,
+    is_id_pat: &mut bool,
 ) -> Result<TokenStream, syn::Error> {
+    // If this is the storage field of the Enum id_pat, we use
+    // the field args that were used for reading, to ensure that
+    // the writing will be the same
+    if *is_id_pat {
+        // TODO: This should error on some attributes that don't make sense aren't used?
+        // Such as magic, seek*
+        let crate_ = super::get_crate_name();
+        let field_endian = input.id_endian.as_ref();
+        #[cfg(feature = "bits")]
+        let field_bits = input.bits.as_ref();
+        #[cfg(not(feature = "bits"))]
+        let field_bits = None;
+        let field_bytes = input.bytes.as_ref();
+        let field_bit_order = input.bit_order.as_ref();
+        let field_ident = f.get_ident(i, object_prefix.is_none());
+
+        let write_args =
+            gen_field_args(field_endian, field_bits, field_bytes, None, field_bit_order)?;
+
+        let ret = quote! {
+            ::#crate_::DekuWriter::to_writer(#object_prefix #field_ident, __deku_writer, (#write_args))?;
+        };
+
+        // only the first field
+        *is_id_pat = false;
+        return Ok(ret);
+    }
+
     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());
diff --git a/src/attributes.rs b/src/attributes.rs
index 5c56c34..e5a23eb 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -1596,7 +1596,8 @@ assert_eq!(
 
 Specify the identifier in the form of a match pattern for the enum variant.
 
-The enum variant must have space to store the identifier for proper writing.
+The first field of the variant is used for storage, and must be the same type as `id_type` and no attributes.
+The writing of the field will use the same options as the reading.
 
 Example:
 ```rust
diff --git a/src/reader.rs b/src/reader.rs
index 9e5bea8..d65abae 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -36,8 +36,6 @@ pub struct Reader<R: Read + Seek> {
     inner: R,
     /// bits stored from previous reads that didn't read to the end of a byte size
     pub leftover: Option<Leftover>,
-    /// Amount of bits read after last read, reseted before reading enum ids
-    pub last_bits_read_amt: usize,
     /// Amount of bits read during the use of [read_bits](Reader::read_bits) and [read_bytes](Reader::read_bytes)
     pub bits_read: usize,
 }
@@ -85,30 +83,10 @@ impl<R: Read + Seek> Reader<R> {
         Self {
             inner,
             leftover: None,
-            last_bits_read_amt: 0,
             bits_read: 0,
         }
     }
 
-    /// Seek to previous previous before the last read, used for `id_pat`
-    #[inline]
-    pub fn seek_last_read(&mut self) -> no_std_io::io::Result<()> {
-        // save the previous bits read
-        let bits_read = self.bits_read;
-
-        let number = self.last_bits_read_amt as i64;
-        let seek_amt = (number / 8).saturating_add((number % 8).signum());
-        #[cfg(feature = "logging")]
-        log::trace!("seek_last_read: {seek_amt:?}");
-        self.seek(SeekFrom::Current(seek_amt.saturating_neg()))?;
-
-        // restore bits read, minus bits we read last time
-        self.bits_read = bits_read - self.last_bits_read_amt;
-        self.leftover = None;
-
-        Ok(())
-    }
-
     /// Consume self, returning inner Reader
     #[inline]
     pub fn into_inner(self) -> R {
@@ -334,13 +312,20 @@ impl<R: Read + Seek> Reader<R> {
                 }
 
                 match order {
+                    // | used | front_bits | leftover |
                     Order::Lsb0 => {
                         let (rest, used) = new_bits.split_at(new_bits.len() - bits_left);
+                        #[cfg(feature = "logging")]
+                        log::trace!("extend(used): {}", used);
                         ret.extend_from_bitslice(used);
                         if let Some(front_bits) = front_bits {
+                            #[cfg(feature = "logging")]
+                            log::trace!("extend(front_bits): {}", front_bits);
                             ret.extend_from_bitslice(front_bits);
                         }
                         if let Some(Leftover::Bits(bits)) = &self.leftover {
+                            #[cfg(feature = "logging")]
+                            log::trace!("extend(leftover): {}", bits);
                             ret.extend_from_bitslice(bits);
                         }
 
@@ -350,14 +335,21 @@ impl<R: Read + Seek> Reader<R> {
                             self.leftover = None;
                         }
                     }
+                    // | leftover | front_bits | rest |
                     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 {
+                            #[cfg(feature = "logging")]
+                            log::trace!("extend(leftover): {}", bits);
                             ret.extend_from_bitslice(bits);
                         }
+                        if let Some(front_bits) = front_bits {
+                            #[cfg(feature = "logging")]
+                            log::trace!("extend(front_bits): {}", front_bits);
+                            ret.extend_from_bitslice(front_bits);
+                        }
+                        #[cfg(feature = "logging")]
+                        log::trace!("extend(rest): {}", rest);
                         ret.extend_from_bitslice(rest);
 
                         if !not_needed.is_empty() {
@@ -394,7 +386,6 @@ impl<R: Read + Seek> Reader<R> {
         }
 
         let bits_read = ret.len();
-        self.last_bits_read_amt += bits_read;
         self.bits_read += bits_read;
 
         #[cfg(feature = "logging")]
@@ -431,7 +422,6 @@ impl<R: Read + Seek> Reader<R> {
             }
 
             let bits_read = amt * 8;
-            self.last_bits_read_amt += bits_read;
             self.bits_read += bits_read;
 
             #[cfg(feature = "logging")]
@@ -522,7 +512,6 @@ impl<R: Read + Seek> Reader<R> {
                 return Err(DekuError::Io(e.kind()));
             }
 
-            self.last_bits_read_amt += N * 8;
             self.bits_read += N * 8;
 
             #[cfg(feature = "logging")]
@@ -652,64 +641,6 @@ mod tests {
         assert_eq!(reader.bits_read, 8);
     }
 
-    #[test]
-    fn test_seek_last_read_bytes() {
-        // bytes
-        let input = hex!("aa");
-        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, Order::Msb0).unwrap();
-        assert_eq!([0xaa], buf);
-        assert_eq!(reader.bits_read, 8);
-
-        reader.seek_last_read().unwrap();
-        let _ = reader.read_bytes(1, &mut buf, Order::Msb0).unwrap();
-        assert_eq!([0xaa], buf);
-        assert_eq!(reader.bits_read, 8);
-
-        // 2 bytes (and const)
-        let input = hex!("aabb");
-        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, Order::Msb0).unwrap();
-        assert_eq!([0xaa, 0xbb], buf);
-        assert_eq!(reader.bits_read, 16);
-
-        reader.seek_last_read().unwrap();
-        let _ = reader.read_bytes_const::<2>(&mut buf, Order::Msb0).unwrap();
-        assert_eq!([0xaa, 0xbb], buf);
-        assert_eq!(reader.bits_read, 16);
-    }
-
-    #[cfg(feature = "bits")]
-    #[test]
-    fn test_seek_last_read_bits() {
-        let input = hex!("ab");
-        let mut cursor = Cursor::new(input);
-        let mut reader = Reader::new(&mut cursor);
-        let bits = reader.read_bits(4, Order::Msb0).unwrap();
-        assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0]));
-        assert_eq!(reader.bits_read, 4);
-        reader.seek_last_read().unwrap();
-        let bits = reader.read_bits(4, Order::Msb0).unwrap();
-        assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0]));
-        assert_eq!(reader.bits_read, 4);
-
-        // 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, Order::Msb0).unwrap();
-        assert_eq!(bits, Some(bitvec![u8, Msb0; 1, 0, 1, 0, 1, 0, 1, 1, 1]));
-        assert_eq!(reader.bits_read, 9);
-        reader.seek_last_read().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]));
-        assert_eq!(reader.bits_read, 9);
-    }
-
     #[cfg(feature = "bits")]
     #[test]
     fn test_bit_order() {
@@ -844,4 +775,36 @@ mod tests {
         // doesn't crash
         let _ = reader.read_bytes(0xfe * 2, &mut out, Order::Lsb0).unwrap();
     }
+
+    #[cfg(feature = "bits")]
+    #[test]
+    fn test_regression_msb0() {
+        // 0110_0100b, 0010_0000b
+        let input = [0x64, 0x20];
+        let mut cursor = Cursor::new(input);
+        let mut reader = Reader::new(&mut cursor);
+        reader.leftover = Some(Leftover::Bits(bitvec![u8, Msb0; 1, 0]));
+        let bits = reader.read_bits(17, Order::Msb0).unwrap();
+        assert_eq!(
+            bits,
+            //                     |left|first                |last                |
+            Some(bitvec![u8, Msb0; 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0])
+        );
+    }
+
+    #[cfg(feature = "bits")]
+    #[test]
+    fn test_regression_lsb0() {
+        // 0110_0100b, 0010_0000b
+        let input = [0x64, 0x20];
+        let mut cursor = Cursor::new(input);
+        let mut reader = Reader::new(&mut cursor);
+        reader.leftover = Some(Leftover::Bits(bitvec![u8, Msb0; 1, 0]));
+        let bits = reader.read_bits(17, Order::Lsb0).unwrap();
+        assert_eq!(
+            bits,
+            //                     |first               |last                   |left|
+            Some(bitvec![u8, Msb0; 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0])
+        );
+    }
 }


Test patch:
diff --git a/tests/test_compile/cases/enum_validation.rs b/tests/test_compile/cases/enum_validation.rs
index 7adafe4..9b27603 100644
--- a/tests/test_compile/cases/enum_validation.rs
+++ b/tests/test_compile/cases/enum_validation.rs
@@ -76,4 +76,60 @@ enum Test11 {
     B(u8),
 }
 
+// Test id_pat id storage must not have attributes
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+#[deku(id_type = "u8", bits = "2")]
+pub enum Test12 {
+    #[deku(id_pat = "_")]
+    B(#[deku(bits = 7)] u8, #[deku(bits = 6)] u8),
+}
+
+// Test id_pat id storage must not have attributes
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+#[deku(id_type = "u32", bytes = "2")]
+pub enum Test13 {
+    #[deku(id_pat = "_")]
+    B(#[deku(bytes = 3)] u32, #[deku(bits = 6)] u8),
+}
+
+// Test id_pat id storage must not have attributes
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+#[deku(id_type = "u32", bytes = "2", id_endian = "little")]
+pub enum Test14 {
+    #[deku(id_pat = "_")]
+    B(#[deku(bytes = 2, endian = "big")] u32, #[deku(bits = 6)] u8),
+}
+
+// Test id_pat id storage must have matching types
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+#[deku(id_type = "u32", bytes = "2")]
+pub enum Test15 {
+    #[deku(id_pat = "_")]
+    B(u8, #[deku(bits = 6)] u8),
+}
+
+// Test id_pat id storage must have matching types
+#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+#[deku(id_type = "[u8; 32]")]
+pub enum Test16 {
+    #[deku(id_pat = "_")]
+    B(u16, #[deku(bits = 6)] u8),
+}
+
+// Test id_pat id storage must exist (read)
+#[derive(PartialEq, Debug, DekuRead)]
+#[deku(id_type = "u8")]
+pub enum Test17 {
+    #[deku(id_pat = "_")]
+    B,
+}
+
+// Test id_pat id storage must exist (write)
+#[derive(PartialEq, Debug, DekuWrite)]
+#[deku(id_type = "u8")]
+pub enum Test18 {
+    #[deku(id_pat = "_")]
+    B,
+}
+
 fn main() {}
diff --git a/tests/test_compile/cases/enum_validation.stderr b/tests/test_compile/cases/enum_validation.stderr
index a68d775..cd3da52 100644
--- a/tests/test_compile/cases/enum_validation.stderr
+++ b/tests/test_compile/cases/enum_validation.stderr
@@ -63,3 +63,51 @@ error: DekuRead: `id` must be specified on non-unit variants
    |
 76 |     B(u8),
    |     ^
+
+error: DekuRead: id_pat id storage cannot have attributes
+  --> tests/test_compile/cases/enum_validation.rs:82:10
+   |
+82 | pub enum Test12 {
+   |          ^^^^^^
+
+error: DekuRead: id_pat id storage cannot have attributes
+  --> tests/test_compile/cases/enum_validation.rs:90:10
+   |
+90 | pub enum Test13 {
+   |          ^^^^^^
+
+error: DekuRead: id_pat id storage cannot have attributes
+  --> tests/test_compile/cases/enum_validation.rs:98:10
+   |
+98 | pub enum Test14 {
+   |          ^^^^^^
+
+error: DekuRead: id_pat requires storage field
+   --> tests/test_compile/cases/enum_validation.rs:122:10
+    |
+122 | pub enum Test17 {
+    |          ^^^^^^
+
+error: DekuRead: id_pat requires storage field
+   --> tests/test_compile/cases/enum_validation.rs:130:10
+    |
+130 | pub enum Test18 {
+    |          ^^^^^^
+
+error[E0308]: `?` operator has incompatible types
+   --> tests/test_compile/cases/enum_validation.rs:104:28
+    |
+104 | #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    |                            ^^^^^^^^ expected `u8`, found `u32`
+    |
+    = note: `?` operator cannot convert from `u32` to `u8`
+    = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0308]: `?` operator has incompatible types
+   --> tests/test_compile/cases/enum_validation.rs:112:28
+    |
+112 | #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    |                            ^^^^^^^^ expected `u16`, found `[u8; 32]`
+    |
+    = note: `?` operator cannot convert from `[u8; 32]` to `u16`
+    = note: this error originates in the derive macro `DekuRead` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/test_enum.rs b/tests/test_enum.rs
index 8c18699..cfb0110 100644
--- a/tests/test_enum.rs
+++ b/tests/test_enum.rs
@@ -102,7 +102,7 @@ fn test_enum_array_type() {
         #[deku(id = "[1,1,1]")]
         VarB,
         #[deku(id_pat = "_")]
-        VarC((u8, u8, u8)),
+        VarC([u8; 3]),
     }
 
     let input = b"123".to_vec();
@@ -116,7 +116,7 @@ fn test_enum_array_type() {
     let input = b"321".to_vec();
 
     let ret_read = TestEnumArray::try_from(input.as_slice()).unwrap();
-    assert_eq!(TestEnumArray::VarC((b"3"[0], b"2"[0], b"1"[0])), ret_read);
+    assert_eq!(TestEnumArray::VarC([b"3"[0], b"2"[0], b"1"[0]]), ret_read);
 
     let ret_write: Vec<u8> = ret_read.try_into().unwrap();
     assert_eq!(input.to_vec(), ret_write);
@@ -176,7 +176,7 @@ fn test_id_pat_with_id() {
         v,
         DekuTest {
             my_id: 0x06,
-            enum_from_id: MyEnum::VariantB
+            enum_from_id: MyEnum::VariantB,
         }
     );
     assert_eq!(input, &*v.to_bytes().unwrap());
@@ -192,45 +192,13 @@ fn id_pat_with_id_bits() {
         A(#[deku(bits = 6)] u8),
 
         #[deku(id_pat = "_")]
-        B(#[deku(bits = 8)] u8),
+        B(u8, #[deku(bits = 6)] u8),
     }
 
     let input = [0b1100_1111];
     let mut cursor = Cursor::new(input);
     let (_, v) = IdPatBits::from_reader((&mut cursor, 0)).unwrap();
-    assert_eq!(v, IdPatBits::B(0b1100_1111));
-    assert_eq!(input, &*v.to_bytes().unwrap());
-
-    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(id_type = "u8", bits = "7")]
-    pub enum IdPatBitsBigger {
-        #[deku(id = 1)]
-        A(#[deku(bits = 9)] u16),
-
-        #[deku(id_pat = "_")]
-        B(u16),
-    }
-
-    let input = [0b0000_0000, 0b0000_0001];
-    let mut cursor = Cursor::new(input);
-    let (_, v) = IdPatBitsBigger::from_reader((&mut cursor, 0)).unwrap();
-    assert_eq!(v, IdPatBitsBigger::B(0b1_0000_0000));
-    assert_eq!(input, &*v.to_bytes().unwrap());
-
-    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-    #[deku(id_type = "u8", bits = "7")]
-    pub enum IdPatBitsTuple {
-        #[deku(id = 1)]
-        A(#[deku(bits = 9)] u16),
-
-        #[deku(id_pat = "_")]
-        B((u8, u8)),
-    }
-
-    let input = [0b0000_0000, 0b0000_0001];
-    let mut cursor = Cursor::new(input);
-    let (_, v) = IdPatBitsTuple::from_reader((&mut cursor, 0)).unwrap();
-    assert_eq!(v, IdPatBitsTuple::B((0, 1)));
+    assert_eq!(v, IdPatBits::B(0b11, 0b00_1111));
     assert_eq!(input, &*v.to_bytes().unwrap());
 }
 
diff --git a/tests/test_regression.rs b/tests/test_regression.rs
index 89bfa8f..ef19e08 100644
--- a/tests/test_regression.rs
+++ b/tests/test_regression.rs
@@ -395,7 +395,7 @@ fn issue_533() {
         #[deku(id = 0)]
         Zero,
         #[deku(id_pat = "_")]
-        Other(#[deku(bits = 1)] u8),
+        Other(u8),
     }
     let input = [0b0100_0000];
     let mut cursor = Cursor::new(input);