Repository: sharksforarms/deku
Pull request #411 (2024-01-21 04:19:52)

Fix error for invalid deku_id generation on generic enum 
- Add/Fixup lifetime for generated code for enum ext id finding. Lifetime __deku needs to be added to the deku_id_type, and the impl.

NOTE: I'm not sure if `__deku` the the best answer, and _now_ ignoring generics is more of a problem of me fixing the problem and the time I have to fix it in a day.

See #408, this also deals with #400

github-actions[bot]: ## Benchmark for 620f4ef
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1115.7±17.37ns** | 1147.6±16.91ns | **+2.86%** |
| deku_read_byte | 20.2±0.27ns | 20.2±0.40ns | 0.00% |
| deku_read_enum | **9.3±0.07ns** | 9.4±0.12ns | **+1.08%** |
| deku_read_vec | 53.2±0.87ns | 53.7±0.80ns | +0.94% |
| deku_write_bits | 114.3±1.25ns | 114.3±1.62ns | 0.00% |
| deku_write_byte | 134.5±11.63ns | 129.3±5.69ns | -3.87% |
| deku_write_enum | 93.6±3.21ns | **91.8±1.66ns** | **-1.92%** |
| deku_write_vec | 3.1±0.06µs | 3.1±0.04µs | 0.00% |

  </details>
  
github-actions[bot]: ## Benchmark for 2df9a4d
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1208.2±12.66ns | **1172.8±50.33ns** | **-2.93%** |
| deku_read_byte | 5.5±0.12ns | 5.5±0.06ns | 0.00% |
| deku_read_enum | 2.9±0.07ns | 2.9±0.06ns | 0.00% |
| deku_read_vec | 35.4±1.63ns | **35.0±0.34ns** | **-1.13%** |
| deku_write_bits | 202.4±3.80ns | 199.3±5.89ns | -1.53% |
| deku_write_byte | 22.0±0.30ns | 22.0±0.10ns | 0.00% |
| deku_write_enum | **23.5±0.27ns** | 23.8±1.89ns | **+1.28%** |
| deku_write_vec | 290.0±2.63ns | 288.9±2.39ns | -0.38% |

  </details>
  
wcampbell0x2a: > lgtm, a test case would be good for this too

Added!

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2068fe0..7f38406 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,9 @@
 ## [Unreleased]
 
 ## Changes
+- Add DekuWrite impl for `[T]` ([#416](https://github.com/sharksforarms/deku/pull/416))
+- Add `no-assert-string` feature to remove panic string on failed assertion ([#405](https://github.com/sharksforarms/deku/pull/405))
+- Add `read_all` attribute to read until `reader.end()` ([#387](https://github.com/sharksforarms/deku/pull/387))
 - Added MSRV for `1.67.1` ([#390](https://github.com/sharksforarms/deku/pull/390))
 - Changed edition to 2021 ([#389](https://github.com/sharksforarms/deku/pull/389))
 - Refactored `logging` feature with massive usability increases ([#352](https://github.com/sharksforarms/deku/pull/352)), ([#355](https://github.com/sharksforarms/deku/pull/355))
@@ -221,6 +224,9 @@ struct DekuTest {
 ```
 - Added `DekuError::Write` to denote `io::Write` errors
 
+## Bug fix
+- Fix error for invalid deku_id generation on generic enum ([#411](https://github.com/sharksforarms/deku/pull/411))
+
 ## [0.16.0] - 2023-02-28
 
 ### Changes
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index 600ff6c..37199b8 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -409,8 +409,12 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
 
     // Implement `DekuEnumExt`
     if let Some(deku_id_type) = deku_id_type {
-        tokens.extend(quote! {
-            impl #imp DekuEnumExt<#lifetime, (#deku_id_type)> for #ident #wher {
+        if !imp.to_token_stream().is_empty() {
+            // Generics (#imp) are not supported, as our __deku
+            // would need to be appended to #imp
+        } else {
+            tokens.extend(quote! {
+            impl<'__deku> #imp ::#crate_::DekuEnumExt<#lifetime, (#deku_id_type)> for #ident #wher {
                 #[inline]
                 fn deku_id(&self) -> core::result::Result<(#deku_id_type), DekuError> {
                     match self {
@@ -420,6 +424,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
         });
+        }
     }
 
     // println!("{}", tokens.to_string());
diff --git a/deku-derive/src/macros/mod.rs b/deku-derive/src/macros/mod.rs
index 256137c..f5fbf40 100644
--- a/deku-derive/src/macros/mod.rs
+++ b/deku-derive/src/macros/mod.rs
@@ -4,6 +4,7 @@ use syn::parse::Parser;
 use syn::punctuated::Punctuated;
 use syn::spanned::Spanned;
 use syn::token::Comma;
+use syn::Lifetime;
 
 use crate::Num;
 
@@ -211,6 +212,10 @@ fn gen_type_from_ctx_id(
             if let syn::FnArg::Typed(pat_type) = arg {
                 if let syn::Pat::Ident(ident) = &*pat_type.pat {
                     if id == ident.ident {
+                        let mut pat_type = pat_type.clone();
+                        if let syn::Type::Reference(r) = pat_type.ty.as_mut() {
+                            r.lifetime = Some(Lifetime::new("'__deku", Span::call_site()));
+                        }
                         let ty = &pat_type.ty;
                         t = Some(quote! {#ty});
                     }
diff --git a/src/lib.rs b/src/lib.rs
index a9acfdc..7e14233 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -521,7 +521,7 @@ pub trait DekuUpdate {
 }
 
 /// "Extended Enum" trait: obtain additional enum information
-pub trait DekuEnumExt<'a, T> {
+pub trait DekuEnumExt<'__deku, T> {
     /// Obtain `id` of a given enum variant
     fn deku_id(&self) -> Result<T, DekuError>;
 }
diff --git a/src/reader.rs b/src/reader.rs
index c70e0f3..0846802 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -19,6 +19,9 @@ pub enum ReaderRet {
     Bits(Option<BitVec<u8, Msb0>>),
 }
 
+/// Max bits requested from [`Reader::read_bits`] during one call
+pub const MAX_BITS_AMT: usize = 128;
+
 /// Reader to use with `from_reader_with_ctx`
 pub struct Reader<'a, R: Read> {
     inner: &'a mut R,
@@ -28,9 +31,6 @@ pub struct Reader<'a, R: Read> {
     pub bits_read: usize,
 }
 
-/// Max bits requested from [`Reader::read_bits`] during one call
-pub const MAX_BITS_AMT: usize = 128;
-
 impl<'a, R: Read> Reader<'a, R> {
     /// Create a new `Reader`
     #[inline]
diff --git a/src/writer.rs b/src/writer.rs
index 362a29e..7e02d69 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -13,6 +13,9 @@ const fn bits_of<T>() -> usize {
     core::mem::size_of::<T>().saturating_mul(<u8>::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`
 pub struct Writer<W: Write> {
     pub(crate) inner: W,
@@ -43,6 +46,9 @@ impl<W: Write> Writer<W> {
     ///
     /// 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<u8, Msb0>) -> Result<(), DekuError> {
         #[cfg(feature = "logging")]
@@ -66,7 +72,8 @@ impl<W: Write> Writer<W> {
         };
 
         // one shot impl of BitSlice::read(no read_exact), but for no_std
-        let mut buf = alloc::vec![0x00; bits.len() / 8];
+        let mut buf = [0; MAX_BITS_AMT];
+        let buf = &mut buf[..bits.len() / 8];
         let mut count = 0;
         bits.chunks_exact(bits_of::<u8>())
             .zip(buf.iter_mut())
@@ -81,7 +88,7 @@ impl<W: Write> Writer<W> {
         bits = unsafe { bits.get_unchecked(count * bits_of::<u8>()..) };
 
         self.leftover = bits.to_bitvec();
-        if self.inner.write_all(&buf).is_err() {
+        if self.inner.write_all(buf).is_err() {
             return Err(DekuError::Write);
         }
 


Test patch:
diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs
index 0ac56ab..8da0463 100644
--- a/tests/test_alloc.rs
+++ b/tests/test_alloc.rs
@@ -63,4 +63,19 @@ mod tests {
             (5, 0, 5)
         );
     }
+
+    #[test]
+    #[cfg_attr(miri, ignore)]
+    fn test_simple_write() {
+        let input = hex!("aa_bbbb_cc_0102_dd_ffffff_aa_0100ff");
+        let t = TestDeku::from_reader((&mut input.as_slice(), 0)).unwrap().1;
+
+        assert_eq!(
+            count_alloc(|| {
+                t.to_bytes().unwrap();
+            })
+            .0,
+            (2, 1, 2)
+        );
+    }
 }
diff --git a/tests/test_compile/cases/no_deku_id_generic_enum.rs b/tests/test_compile/cases/no_deku_id_generic_enum.rs
new file mode 100644
index 0000000..4bcde37
--- /dev/null
+++ b/tests/test_compile/cases/no_deku_id_generic_enum.rs
@@ -0,0 +1,13 @@
+use deku::prelude::*;
+
+#[derive(DekuRead)]
+#[deku(id_type = "u8")]
+pub enum Body<T: for<'a> DekuReader<'a>> {
+    #[deku(id = "0x0001")]
+    First(T),
+}
+
+fn main() {
+    let n = Body::<u8>::First(1);
+    n.deku_id();
+}
diff --git a/tests/test_compile/cases/no_deku_id_generic_enum.stderr b/tests/test_compile/cases/no_deku_id_generic_enum.stderr
new file mode 100644
index 0000000..778e7b9
--- /dev/null
+++ b/tests/test_compile/cases/no_deku_id_generic_enum.stderr
@@ -0,0 +1,12 @@
+error[E0599]: no method named `deku_id` found for enum `Body` in the current scope
+  --> tests/test_compile/cases/no_deku_id_generic_enum.rs:12:7
+   |
+5  | pub enum Body<T: for<'a> DekuReader<'a>> {
+   | ---------------------------------------- method `deku_id` not found for this enum
+...
+12 |     n.deku_id();
+   |       ^^^^^^^ method not found in `Body<u8>`
+   |
+   = help: items from traits can only be used if the trait is implemented and in scope
+   = note: the following trait defines an item `deku_id`, perhaps you need to implement it:
+           candidate #1: `deku::DekuEnumExt`