Repository: sharksforarms/deku
Pull request #487 (2024-10-02 23:47:04)

Add NoSeek
* Add NoSeek to implement Seek for types that won't need to seek, but have the trait impl.

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| count | 5.1±0.08µs | **5.0±0.02µs** | **-1.96%** |
| deku_read_bits | **1153.9±19.96ns** | 1183.2±19.07ns | **+2.54%** |
| deku_read_byte | 5.3±0.12ns | 5.3±0.11ns | 0.00% |
| deku_read_enum | 4.5±0.04ns | 4.5±0.03ns | 0.00% |
| deku_read_vec | 42.4±0.37ns | 42.5±0.32ns | +0.24% |
| deku_write_bits | 185.8±3.18ns | 187.0±1.63ns | +0.65% |
| deku_write_byte | 25.1±0.44ns | 25.2±0.47ns | +0.40% |
| deku_write_enum | 22.7±0.20ns | 22.8±0.38ns | +0.44% |
| deku_write_vec | 424.9±6.78ns | 426.0±9.16ns | +0.26% |
| read_all | 5.6±0.05µs | 5.6±0.03µs | 0.00% |
| read_all_bytes | 5.7±0.05µs | **5.6±0.04µs** | **-1.75%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| count | 5.1±0.02µs | **5.0±0.04µs** | **-1.96%** |
| deku_read_bits | **1138.9±13.67ns** | 1172.7±12.42ns | **+2.97%** |
| deku_read_byte | 5.9±0.08ns | 5.9±0.07ns | 0.00% |
| deku_read_enum | **4.7±0.02ns** | 5.2±0.04ns | **+10.64%** |
| deku_read_vec | **42.5±0.40ns** | 42.9±0.51ns | **+0.94%** |
| deku_write_bits | 185.1±1.34ns | 184.8±3.16ns | -0.16% |
| deku_write_byte | 25.4±0.22ns | **25.1±0.27ns** | **-1.18%** |
| deku_write_enum | 22.8±0.48ns | 22.8±0.38ns | 0.00% |
| deku_write_vec | 415.1±6.35ns | 415.5±5.48ns | +0.10% |
| read_all | **3.8±0.05µs** | 5.6±0.04µs | **+47.37%** |
| read_all_bytes | **3.8±0.06µs** | 5.6±0.05µs | **+47.37%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| count | 5.0±0.01µs | 5.0±0.02µs | 0.00% |
| deku_read_bits | 1179.4±18.49ns | 1174.2±11.64ns | -0.44% |
| deku_read_byte | 5.8±0.05ns | **5.3±0.14ns** | **-8.62%** |
| deku_read_enum | 5.3±0.06ns | **4.5±0.02ns** | **-15.09%** |
| deku_read_vec | 43.3±2.39ns | **42.5±0.60ns** | **-1.85%** |
| deku_write_bits | 185.1±2.69ns | 185.6±2.48ns | +0.27% |
| deku_write_byte | 25.1±0.28ns | 25.0±0.20ns | -0.40% |
| deku_write_enum | 22.8±0.33ns | 23.0±1.43ns | +0.88% |
| deku_write_vec | **407.5±7.42ns** | 415.3±3.89ns | **+1.91%** |
| read_all | 5.6±0.20µs | 5.7±0.17µs | +1.79% |
| read_all_bytes | **5.6±0.04µs** | 5.7±0.14µs | **+1.79%** |

  </details>
  

Patch:
diff --git a/benches/deku.rs b/benches/deku.rs
index 7951433..80dd55a 100644
--- a/benches/deku.rs
+++ b/benches/deku.rs
@@ -111,7 +111,7 @@ fn criterion_benchmark(c: &mut Criterion) {
     });
 }
 
-pub fn read_all_vs_count(c: &mut Criterion) {
+pub fn read_all_vs_count_vs_read_exact(c: &mut Criterion) {
     #[derive(DekuRead, DekuWrite)]
     pub struct AllWrapper {
         #[deku(read_all)]
@@ -119,8 +119,20 @@ pub fn read_all_vs_count(c: &mut Criterion) {
     }
 
     #[derive(DekuRead, DekuWrite)]
-    #[deku(ctx = "len: usize")]
     pub struct CountWrapper {
+        #[deku(count = "1500")]
+        pub data: Vec<u8>,
+    }
+
+    #[derive(DekuRead, DekuWrite)]
+    pub struct CountNonSpecialize {
+        #[deku(count = "(1500/2)")]
+        pub data: Vec<u16>,
+    }
+
+    #[derive(DekuRead, DekuWrite)]
+    #[deku(ctx = "len: usize")]
+    pub struct CountFromCtxWrapper {
         #[deku(count = "len")]
         pub data: Vec<u8>,
     }
@@ -137,14 +149,34 @@ pub fn read_all_vs_count(c: &mut Criterion) {
         })
     });
 
-    c.bench_function("count", |b| {
+    c.bench_function("count_specialize", |b| {
+        b.iter(|| {
+            let mut cursor = Cursor::new([1u8; 1500].as_ref());
+            let mut reader = Reader::new(&mut cursor);
+            CountWrapper::from_reader_with_ctx(black_box(&mut reader), ())
+        })
+    });
+
+    c.bench_function("count_from_u8_specialize", |b| {
+        b.iter(|| {
+            let mut cursor = Cursor::new([1u8; 1500].as_ref());
+            let mut reader = Reader::new(&mut cursor);
+            CountWrapper::from_reader_with_ctx(black_box(&mut reader), ())
+        })
+    });
+
+    c.bench_function("count_no_specialize", |b| {
         b.iter(|| {
             let mut cursor = Cursor::new([1u8; 1500].as_ref());
             let mut reader = Reader::new(&mut cursor);
-            CountWrapper::from_reader_with_ctx(black_box(&mut reader), 1500)
+            CountNonSpecialize::from_reader_with_ctx(black_box(&mut reader), ())
         })
     });
 }
 
-criterion_group!(benches, criterion_benchmark, read_all_vs_count);
+criterion_group!(
+    benches,
+    criterion_benchmark,
+    read_all_vs_count_vs_read_exact
+);
 criterion_main!(benches);
diff --git a/deku-derive/src/lib.rs b/deku-derive/src/lib.rs
index 7859ab8..3d1a425 100644
--- a/deku-derive/src/lib.rs
+++ b/deku-derive/src/lib.rs
@@ -8,6 +8,7 @@ extern crate alloc;
 use alloc::borrow::Cow;
 use std::convert::TryFrom;
 use std::fmt::Display;
+use syn::Type;
 
 use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant, ToTokens};
 use proc_macro2::TokenStream;
@@ -70,38 +71,41 @@ impl FromMeta for Id {
 }
 
 #[derive(Debug)]
-struct Num(syn::LitInt);
+enum Num {
+    LitInt(syn::LitInt),
+    TokenStream(TokenStream),
+}
 
 impl Num {
     fn new(n: syn::LitInt) -> Self {
-        Self(n)
+        Self::LitInt(n)
     }
 }
 
 impl Display for Num {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.write_str(&self.0.to_token_stream().to_string())
+        f.write_str(&self.to_token_stream().to_string())
     }
 }
 
 impl ToTokens for Num {
     fn to_tokens(&self, tokens: &mut TokenStream) {
-        self.0.to_tokens(tokens)
+        match self {
+            Num::LitInt(v) => v.to_tokens(tokens),
+            Num::TokenStream(v) => v.to_tokens(tokens),
+        }
     }
 }
 
 impl FromMeta for Num {
     fn from_value(value: &syn::Lit) -> darling::Result<Self> {
         (match *value {
-            syn::Lit::Str(ref s) => Ok(Num::new(syn::LitInt::new(
-                s.value()
-                    .as_str()
-                    .parse::<usize>()
-                    .map_err(|_| darling::Error::unknown_value(&s.value()))?
-                    .to_string()
-                    .as_str(),
-                s.span(),
-            ))),
+            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::Int(ref s) => Ok(Num::new(s.clone())),
             _ => Err(darling::Error::unexpected_lit_type(value)),
         })
@@ -409,7 +413,7 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataStruct<'a> {
 #[derive(Debug)]
 struct FieldData {
     ident: Option<syn::Ident>,
-    ty: syn::Type,
+    ty: Type,
 
     /// endianness for the field
     endian: Option<syn::LitStr>,
@@ -856,7 +860,7 @@ fn default_res_opt<T, E>() -> Result<Option<T>, E> {
 #[darling(attributes(deku))]
 struct DekuFieldReceiver {
     ident: Option<syn::Ident>,
-    ty: syn::Type,
+    ty: Type,
 
     /// Endianness for the field
     #[darling(default)]
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index 5208633..a5d2ee4 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -163,6 +163,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
     };
 
     tokens.extend(quote! {
+        #[automatically_derived]
         impl #imp ::#crate_::DekuReader<#lifetime, #ctx_types> for #ident #wher {
             #[inline]
             fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, #ctx_arg) -> core::result::Result<Self, ::#crate_::DekuError> {
@@ -175,6 +176,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let read_body = wrap_default_ctx(read_body, &input.ctx, &input.ctx_default);
 
         tokens.extend(quote! {
+            #[automatically_derived]
             impl #imp ::#crate_::DekuReader<#lifetime> for #ident #wher {
                 #[inline]
                 fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, _: ()) -> core::result::Result<Self, ::#crate_::DekuError> {
@@ -433,6 +435,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
 
     tokens.extend(quote! {
         #[allow(non_snake_case)]
+        #[automatically_derived]
         impl #imp ::#crate_::DekuReader<#lifetime, #ctx_types> for #ident #wher {
             #[inline]
             fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, #ctx_arg) -> core::result::Result<Self, ::#crate_::DekuError> {
@@ -446,6 +449,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
 
         tokens.extend(quote! {
             #[allow(non_snake_case)]
+            #[automatically_derived]
             impl #imp ::#crate_::DekuReader<#lifetime> for #ident #wher {
                 #[inline]
                 fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, _: ()) -> core::result::Result<Self, ::#crate_::DekuError> {
@@ -470,6 +474,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             // would need to be appended to #imp
         } else {
             tokens.extend(quote! {
+            #[automatically_derived]
             impl<'__deku> #imp ::#crate_::DekuEnumExt<#lifetime, (#deku_id_type)> for #ident #wher {
                 #[inline]
                 fn deku_id(&self) -> core::result::Result<(#deku_id_type), ::#crate_::DekuError> {
@@ -777,14 +782,44 @@ fn emit_field_read(
                 }
             }
         } else if let Some(field_count) = &f.count {
-            quote! {
-                {
-                    use core::borrow::Borrow;
-                    #type_as_deku_read::from_reader_with_ctx
-                    (
-                        __deku_reader,
-                        (::#crate_::ctx::Limit::new_count(usize::try_from(*((#field_count).borrow()))?), (#read_args))
-                    )?
+            use syn::{GenericArgument, PathArguments, Type};
+            let mut is_vec_u8 = false;
+            if let Type::Path(type_path) = &f.ty {
+                if type_path.path.segments.len() == 1 && type_path.path.segments[0].ident == "Vec" {
+                    if let PathArguments::AngleBracketed(ref generic_args) =
+                        type_path.path.segments[0].arguments
+                    {
+                        if generic_args.args.len() == 1 {
+                            if let GenericArgument::Type(Type::Path(ref arg_path)) =
+                                generic_args.args[0]
+                            {
+                                is_vec_u8 = arg_path.path.is_ident("u8");
+                            }
+                        }
+                    }
+                }
+            }
+            if is_vec_u8 {
+                quote! {
+                    {
+                        use core::borrow::Borrow;
+                        #type_as_deku_read::from_reader_with_ctx
+                        (
+                            __deku_reader,
+                            ::#crate_::ctx::ReadExact(usize::try_from(*((#field_count).borrow()))?)
+                        )?
+                    }
+                }
+            } else {
+                quote! {
+                    {
+                        use core::borrow::Borrow;
+                        #type_as_deku_read::from_reader_with_ctx
+                        (
+                            __deku_reader,
+                            (::#crate_::ctx::Limit::new_count(usize::try_from(*((#field_count).borrow()))?), (#read_args))
+                        )?
+                    }
                 }
             }
         } else if let Some(field_bytes) = &f.bytes_read {
@@ -943,6 +978,7 @@ pub fn emit_container_read(
 ) -> TokenStream {
     let crate_ = super::get_crate_name();
     quote! {
+        #[automatically_derived]
         impl #imp ::#crate_::DekuContainerRead<#lifetime> for #ident #wher {
             #[allow(non_snake_case)]
             #[inline]
@@ -968,6 +1004,7 @@ pub fn emit_try_from(
 ) -> TokenStream {
     let crate_ = super::get_crate_name();
     quote! {
+        #[automatically_derived]
         impl #imp core::convert::TryFrom<&#lifetime [u8]> for #ident #wher {
             type Error = ::#crate_::DekuError;
 
diff --git a/deku-derive/src/macros/deku_write.rs b/deku-derive/src/macros/deku_write.rs
index bc7f0bd..2d19c21 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -93,6 +93,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
     if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
         #[cfg(feature = "bits")]
         tokens.extend(quote! {
+            #[automatically_derived]
              impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
                 type Error = ::#crate_::DekuError;
 
@@ -105,6 +106,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
         });
 
         tokens.extend(quote! {
+            #[automatically_derived]
             impl #imp core::convert::TryFrom<#ident> for Vec<u8> #wher {
                 type Error = ::#crate_::DekuError;
 
@@ -114,6 +116,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
 
+            #[automatically_derived]
             impl #imp ::#crate_::DekuContainerWrite for #ident #wher {}
         });
     }
@@ -136,6 +139,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
     let update_use = check_update_use(&field_updates);
 
     tokens.extend(quote! {
+        #[automatically_derived]
         impl #imp ::#crate_::DekuUpdate for #ident #wher {
             #[inline]
             fn update(&mut self) -> core::result::Result<(), ::#crate_::DekuError> {
@@ -146,6 +150,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             }
         }
 
+        #[automatically_derived]
         impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher {
             #[allow(unused_variables)]
             #[inline]
@@ -159,6 +164,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default);
 
         tokens.extend(quote! {
+            #[automatically_derived]
             impl #imp ::#crate_::DekuWriter for #ident #wher {
                 #[allow(unused_variables)]
                 #[inline]
@@ -300,6 +306,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
     if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
         #[cfg(feature = "bits")]
         tokens.extend(quote! {
+            #[automatically_derived]
              impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
                 type Error = ::#crate_::DekuError;
 
@@ -312,6 +319,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
         });
 
         tokens.extend(quote! {
+            #[automatically_derived]
             impl #imp core::convert::TryFrom<#ident> for Vec<u8> #wher {
                 type Error = ::#crate_::DekuError;
 
@@ -321,6 +329,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
 
+            #[automatically_derived]
             impl #imp ::#crate_::DekuContainerWrite for #ident #wher {}
         });
     }
@@ -341,6 +350,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
     let update_use = check_update_use(&variant_updates);
 
     tokens.extend(quote! {
+        #[automatically_derived]
         impl #imp ::#crate_::DekuUpdate for #ident #wher {
             #[inline]
             fn update(&mut self) -> core::result::Result<(), ::#crate_::DekuError> {
@@ -354,6 +364,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             }
         }
 
+        #[automatically_derived]
         impl #imp ::#crate_::DekuWriter<#ctx_types> for #ident #wher {
             #[allow(unused_variables)]
             #[inline]
@@ -367,6 +378,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
         let write_body = wrap_default_ctx(write_body, &input.ctx, &input.ctx_default);
 
         tokens.extend(quote! {
+            #[automatically_derived]
             impl #imp ::#crate_::DekuWriter for #ident #wher {
                 #[allow(unused_variables)]
                 #[inline]
diff --git a/ensure_no_std/src/bin/main.rs b/ensure_no_std/src/bin/main.rs
index 3c8ac7f..6772955 100644
--- a/ensure_no_std/src/bin/main.rs
+++ b/ensure_no_std/src/bin/main.rs
@@ -24,6 +24,8 @@ struct DekuTest {
     count: u8,
     #[deku(count = "count", pad_bytes_after = "8")]
     data: Vec<u8>,
+    #[deku(count = "1")]
+    after: Vec<u8>,
 }
 
 #[entry]
@@ -39,7 +41,7 @@ fn main() -> ! {
     // now the allocator is ready types like Box, Vec can be used.
 
     #[allow(clippy::unusual_byte_groupings)]
-    let test_data: &[u8] = &[0b10101_101, 0x02, 0xBE, 0xEF, 0xff];
+    let test_data: &[u8] = &[0b10101_101, 0x02, 0xBE, 0xEF, 0xff, 0xaa];
     let mut cursor = deku::no_std_io::Cursor::new(test_data);
 
     // Test reading
@@ -49,7 +51,8 @@ fn main() -> ! {
             field_a: 0b10101,
             field_b: 0b101,
             count: 0x02,
-            data: vec![0xBE, 0xEF]
+            data: vec![0xBE, 0xEF],
+            after: vec![0xaa],
         },
         val
     );
diff --git a/src/attributes.rs b/src/attributes.rs
index f73ebde..e9b6146 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -520,6 +520,39 @@ let value: Vec<u8> = value.try_into().unwrap();
 assert_eq!(&*data, value);
 ```
 
+This attribute can also be set from a previous read:
+
+Example:
+```rust
+# use deku::prelude::*;
+# use std::convert::{TryInto, TryFrom};
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    field_a_len: u8,
+    #[deku(bits = "*field_a_len as usize")]
+    field_a: u8,
+    #[deku(bits = 6)]
+    field_b: u8,
+}
+
+let data: &[u8] = &[0x02, 0b11_101010];
+
+let value = DekuTest::try_from(data).unwrap();
+
+assert_eq!(
+    DekuTest {
+       field_a_len: 2,
+       field_a: 0b11,
+       field_b: 0b101010,
+    },
+    value
+);
+
+let value: Vec<u8> = value.try_into().unwrap();
+assert_eq!(&*data, value);
+```
+
+
 # bytes
 
 Set the byte-size of the field
@@ -553,6 +586,35 @@ let value: Vec<u8> = value.try_into().unwrap();
 assert_eq!(data, value);
 ```
 
+This attribute can also be set from a previous read:
+
+Example:
+```rust
+# use deku::prelude::*;
+# use std::convert::{TryInto, TryFrom};
+# #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+struct DekuTest {
+    field_a_size: u8,
+    #[deku(bytes = "*field_a_size as usize")]
+    field_a: u32,
+}
+
+let data: &[u8] = &[0x03, 0x01, 0x02, 0x03];
+
+let value = DekuTest::try_from(data).unwrap();
+
+assert_eq!(
+    DekuTest {
+       field_a_size: 0x03,
+       field_a: 0x030201,
+    },
+    value
+);
+
+let value: Vec<u8> = value.try_into().unwrap();
+assert_eq!(data, value);
+```
+
 # count
 
 Specify the field representing the length of the container, i.e. a Vec
@@ -587,6 +649,8 @@ assert_eq!(data, value);
 
 **Note**: See [update](#update) for more information on the attribute!
 
+## Specializations
+- `Vec<u8>`: `count` used with a byte vector will result in one invocation to `read_bytes`, thus improving performance.
 
 # bytes_read
 
@@ -707,7 +771,6 @@ let value: Vec<u8> = value.try_into().unwrap();
 assert_eq!(&*data, value);
 ```
 
-
 # update
 
 Specify custom code to run on the field when `.update()` is called on the struct/enum
diff --git a/src/ctx.rs b/src/ctx.rs
index 5565be2..393a1a9 100644
--- a/src/ctx.rs
+++ b/src/ctx.rs
@@ -201,3 +201,6 @@ impl BitSize {
         Self::bits_from_reader(core::mem::size_of_val(val))
     }
 }
+
+/// Amount of bytes to read_exact
+pub struct ReadExact(pub usize);
diff --git a/src/impls/vec.rs b/src/impls/vec.rs
index 33c89c9..fa93bcb 100644
--- a/src/impls/vec.rs
+++ b/src/impls/vec.rs
@@ -8,6 +8,20 @@ use crate::writer::Writer;
 use crate::{ctx::*, DekuReader};
 use crate::{DekuError, DekuWriter};
 
+impl<'a> DekuReader<'a, ReadExact> for Vec<u8> {
+    fn from_reader_with_ctx<R: Read + Seek>(
+        reader: &mut Reader<R>,
+        exact: ReadExact,
+    ) -> Result<Self, DekuError>
+    where
+        Self: Sized,
+    {
+        let mut bytes = alloc::vec![0x00; exact.0];
+        let _ = reader.read_bytes(exact.0, &mut bytes)?;
+        Ok(bytes)
+    }
+}
+
 /// Read `T`s into a vec until a given predicate returns true
 /// * `capacity` - an optional capacity to pre-allocate the vector with
 /// * `ctx` - The context required by `T`. It will be passed to every `T` when constructing.
diff --git a/src/lib.rs b/src/lib.rs
index 845cb8e..8a0e187 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -343,6 +343,9 @@ The feature `bits` enables the `bitvec` crate to use when reading and writing, w
 This however slows down the reading and writing process if your code doesn't use `bits` and the `bit_offset`
 in `from_bytes`.
 
+# NoSeek
+Unseekable streams such as [TcpStream](https://doc.rust-lang.org/std/net/struct.TcpStream.html) are supported through the [NoSeek](noseek::NoSeek) wrapper.
+
 */
 #![warn(missing_docs)]
 #![cfg_attr(not(feature = "std"), no_std)]
@@ -377,6 +380,7 @@ pub mod attributes;
 pub mod ctx;
 pub mod error;
 mod impls;
+pub mod noseek;
 pub mod prelude;
 pub mod reader;
 pub mod writer;
diff --git a/src/noseek.rs b/src/noseek.rs
new file mode 100644
index 0000000..fc9264e
--- /dev/null
+++ b/src/noseek.rs
@@ -0,0 +1,124 @@
+//! Wrapper type that provides a fake [`Seek`] implementation.
+
+#[cfg(not(feature = "std"))]
+use alloc::{string::String, vec::Vec};
+
+use crate::no_std_io::{Read, Result, Seek, SeekFrom, Write};
+use no_std_io::io::ErrorKind;
+
+/// A wrapper that provides a limited implementation of
+/// [`Seek`] for unseekable [`Read`] and [`Write`] streams.
+///
+/// This is useful when reading or writing from unseekable streams where deku
+/// does not *actually* need to seek to successfully parse or write the data.
+///
+/// This implementation was taken from [binrw](https://docs.rs/binrw/0.14.0/binrw/io/struct.NoSeek.html)
+pub struct NoSeek<T> {
+    /// The original stream.
+    inner: T,
+    /// The virtual position of the seekable stream.
+    pos: u64,
+}
+
+impl<T> NoSeek<T> {
+    /// Creates a new seekable wrapper for the given value.
+    pub fn new(inner: T) -> Self {
+        NoSeek { inner, pos: 0 }
+    }
+
+    /// Gets a mutable reference to the underlying value.
+    pub fn get_mut(&mut self) -> &mut T {
+        &mut self.inner
+    }
+
+    /// Gets a reference to the underlying value.
+    pub fn get_ref(&self) -> &T {
+        &self.inner
+    }
+
+    /// Consumes this wrapper, returning the underlying value.
+    pub fn into_inner(self) -> T {
+        self.inner
+    }
+}
+
+impl<T> Seek for NoSeek<T> {
+    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
+        match pos {
+            SeekFrom::Start(n) if self.pos == n => Ok(n),
+            SeekFrom::Current(0) => Ok(self.pos),
+            // https://github.com/rust-lang/rust/issues/86442
+            #[cfg(feature = "std")]
+            _ => Err(std::io::Error::new(
+                ErrorKind::Other,
+                "seek on unseekable file",
+            )),
+            #[cfg(not(feature = "std"))]
+            _ => panic!("seek on unseekable file"),
+        }
+    }
+
+    #[cfg(feature = "std")]
+    fn stream_position(&mut self) -> Result<u64> {
+        Ok(self.pos)
+    }
+}
+
+impl<T: Read> Read for NoSeek<T> {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+        let n = self.inner.read(buf)?;
+        self.pos += n as u64;
+        Ok(n)
+    }
+
+    #[cfg(feature = "std")]
+    fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> Result<usize> {
+        let n = self.inner.read_vectored(bufs)?;
+        self.pos += n as u64;
+        Ok(n)
+    }
+
+    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
+        let n = self.inner.read_to_end(buf)?;
+        self.pos += n as u64;
+        Ok(n)
+    }
+
+    #[cfg(feature = "std")]
+    fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
+        let n = self.inner.read_to_string(buf)?;
+        self.pos += n as u64;
+        Ok(n)
+    }
+
+    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
+        self.inner.read_exact(buf)?;
+        self.pos += buf.len() as u64;
+        Ok(())
+    }
+}
+
+impl<T: Write> Write for NoSeek<T> {
+    fn write(&mut self, buf: &[u8]) -> Result<usize> {
+        let n = self.inner.write(buf)?;
+        self.pos += n as u64;
+        Ok(n)
+    }
+
+    fn flush(&mut self) -> Result<()> {
+        self.inner.flush()
+    }
+
+    #[cfg(feature = "std")]
+    fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> Result<usize> {
+        let n = self.inner.write_vectored(bufs)?;
+        self.pos += n as u64;
+        Ok(n)
+    }
+
+    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
+        self.inner.write_all(buf)?;
+        self.pos += buf.len() as u64;
+        Ok(())
+    }
+}


Test patch:
diff --git a/tests/test_attributes/test_limits/test_count.rs b/tests/test_attributes/test_limits/test_count.rs
index c2e7871..544711e 100644
--- a/tests/test_attributes/test_limits/test_count.rs
+++ b/tests/test_attributes/test_limits/test_count.rs
@@ -27,6 +27,28 @@ mod test_slice {
         assert_eq!(test_data, ret_write);
     }
 
+    #[test]
+    fn test_count_static_non_u8() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct {
+            #[deku(count = "1")]
+            data: Vec<(u8, u8)>,
+        }
+
+        let test_data: Vec<u8> = [0xaa, 0xbb].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_slice()).unwrap();
+        assert_eq!(
+            TestStruct {
+                data: vec![(0xaa, 0xbb)],
+            },
+            ret_read
+        );
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
+
     #[test]
     fn test_count_from_field() {
         #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
@@ -74,7 +96,7 @@ mod test_slice {
     }
 
     #[test]
-    #[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
+    #[should_panic(expected = "Incomplete(NeedSize { bits: 24 })")]
     fn test_count_error() {
         #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
         struct TestStruct {
@@ -156,7 +178,7 @@ mod test_vec {
     }
 
     #[test]
-    #[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
+    #[should_panic(expected = "Incomplete(NeedSize { bits: 24 })")]
     fn test_count_error() {
         #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
         struct TestStruct {
diff --git a/tests/test_from_reader.rs b/tests/test_from_reader.rs
index 1e7e87d..a366884 100644
--- a/tests/test_from_reader.rs
+++ b/tests/test_from_reader.rs
@@ -1,3 +1,4 @@
+use deku::noseek::NoSeek;
 use deku::prelude::*;
 use no_std_io::io::Seek;
 
@@ -32,6 +33,12 @@ fn test_from_reader_struct() {
     let (amt_read, ret_read) = TestDeku::from_reader((&mut c, total_read)).unwrap();
     assert_eq!(amt_read, 16);
     assert_eq!(TestDeku(0b1010), ret_read);
+
+    let inner = &c.into_inner();
+    let mut s = NoSeek::new(inner.as_slice());
+    let (amt_read, ret_read) = TestDeku::from_reader((&mut s, 0)).unwrap();
+    assert_eq!(amt_read, 4);
+    assert_eq!(TestDeku(0b0110), ret_read);
 }
 
 #[cfg(feature = "bits")]
@@ -57,4 +64,11 @@ fn test_from_reader_enum() {
     let (amt_read, ret_read) = TestDeku::from_reader((&mut c, first_amt_read)).unwrap();
     assert_eq!(amt_read, 6 + first_amt_read);
     assert_eq!(TestDeku::VariantB(0b10), ret_read);
+
+    c.rewind();
+    let inner = &c.into_inner();
+    let mut s = NoSeek::new(inner.as_slice());
+    let (amt_read, ret_read) = TestDeku::from_reader((&mut s, 0)).unwrap();
+    assert_eq!(first_amt_read, 8);
+    assert_eq!(TestDeku::VariantA(0b0110), ret_read);
 }
diff --git a/tests/test_seek.rs b/tests/test_seek.rs
index 6d3d611..a75354d 100644
--- a/tests/test_seek.rs
+++ b/tests/test_seek.rs
@@ -1,4 +1,4 @@
-use deku::prelude::*;
+use deku::{noseek::NoSeek, prelude::*};
 use hexlit::hex;
 use rstest::*;
 
@@ -25,7 +25,6 @@ pub struct Test {
 )]
 fn test_seek(input: &[u8], expected: Test) {
     let input = input.to_vec();
-
     let mut cursor = std::io::Cursor::new(input.clone());
     let (_, ret_read) = Test::from_reader((&mut cursor, 0)).unwrap();
 
@@ -115,3 +114,36 @@ fn test_seek_ctx_end(input: &[u8], expected: SeekCtxBeforeEnd) {
     let _ = ret_read.to_writer(&mut writer, ()).unwrap();
     assert_eq!(buf, input);
 }
+
+#[derive(DekuRead, DekuWrite, Debug, PartialEq, Eq)]
+#[deku(seek_from_start = "0")]
+pub struct SeekCtxNoSeek {
+    byte: u8,
+}
+
+#[rstest(input, expected,
+    case(&hex!("03"), SeekCtxNoSeek { byte: 0x03 }),
+    case(&hex!("ff"), SeekCtxNoSeek { byte: 0xff }),
+)]
+fn test_seek_ctx_no_seek(input: &[u8], expected: SeekCtxNoSeek) {
+    use std::io::Cursor;
+    let input = input.to_vec();
+
+    let mut cursor = std::io::Cursor::new(input.clone());
+    let mut reader = Reader::new(&mut cursor);
+    let ret_read = SeekCtxNoSeek::from_reader_with_ctx(&mut reader, ()).unwrap();
+
+    assert_eq!(ret_read, expected);
+
+    let mut buf = vec![];
+    let mut cursor = Cursor::new(&mut buf);
+    let mut writer = Writer::new(&mut cursor);
+    let _ = ret_read.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buf, input);
+
+    let mut buf = vec![];
+    let mut cursor = NoSeek::new(&mut buf);
+    let mut writer = Writer::new(&mut cursor);
+    let _ = ret_read.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buf, input);
+}
diff --git a/tests/test_struct.rs b/tests/test_struct.rs
index 92ed213..c084482 100644
--- a/tests/test_struct.rs
+++ b/tests/test_struct.rs
@@ -198,6 +198,11 @@ fn test_big_endian() {
     let new_bytes = a.to_bytes().unwrap();
     assert_eq!(bytes, &*new_bytes);
 
+    let bytes = &[0x11, 0x22, 0x33];
+    let a = A::from_bytes((bytes, 0)).unwrap().1;
+    let new_bytes = a.to_bytes().unwrap();
+    assert_eq!(bytes, &*new_bytes);
+
     #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
     pub struct B {
         #[deku(bytes = "2", endian = "big")]