Repository: sharksforarms/deku
Pull request #521 (2025-01-24 07:13:43)

`Reader` shouldn't require its inner type to be a mutable reference
This makes `Reader` more general, so it can e.g. take ownership of a `Read + Seek` type.

wcampbell0x2a: Seem plausible, I needed to do the following in [backhand](https://github.com/wcampbell0x2a/backhand) for this patch:
```diff
diff --git a/backhand/src/metadata.rs b/backhand/src/metadata.rs
index 89b4994..bf76adf 100644
--- a/backhand/src/metadata.rs
+++ b/backhand/src/metadata.rs
@@ -117,12 +117,13 @@ pub fn read_block<R: Read + Seek>(
     superblock: &SuperBlock,
     kind: &Kind,
 ) -> Result<Vec<u8>, BackhandError> {
-    let mut deku_reader = Reader::new(reader);
+    let mut deku_reader = Reader::new(&mut *reader);
     let metadata_len = u16::from_reader_with_ctx(&mut deku_reader, kind.inner.data_endian)?;

     let byte_len = len(metadata_len);
     tracing::trace!("len: 0x{:02x?}", byte_len);
     let mut buf = vec![0u8; byte_len as usize];
+    // deku_reader.as_mut().read_exact(&mut buf)?; // or this instead of the above
     reader.read_exact(&mut buf)?;

     let bytes = if is_compressed(metadata_len) {
diff --git a/backhand/src/squashfs.rs b/backhand/src/squashfs.rs
index b4420c5..2240d3c 100644
--- a/backhand/src/squashfs.rs
+++ b/backhand/src/squashfs.rs
@@ -225,7 +225,7 @@ impl<'b> Squashfs<'b> {
         kind: &Kind,
     ) -> Result<(SuperBlock, Option<CompressionOptions>), BackhandError> {
         // Parse SuperBlock
-        let mut container = Reader::new(reader);
+        let mut container = Reader::new(&mut *reader);
         let superblock = SuperBlock::from_reader_with_ctx(
             &mut container,
             (
```
wgreenberg: ack, it turns out this is something of a compiler limitation: rust won't automatically reborrow generics, hence why you have to manually reborrow. one way to rewrite backhand's usage would be:

```rust
            let mut my_reader = Reader::new(reader);
            _ = u16::from_reader_with_ctx(&mut my_reader, ()).unwrap();

            let reader = my_reader.into_inner();
            reader.read_exact(&mut vec![0; 10]).unwrap();
```

but i understand if you think this is too obtuse
wcampbell0x2a: > ack, it turns out this is something of a compiler limitation: rust won't automatically reborrow generics, hence why you have to manually reborrow. one way to rewrite backhand's usage would be:
> 
> ```rust
>             let mut my_reader = Reader::new(reader);
>             _ = u16::from_reader_with_ctx(&mut my_reader, ()).unwrap();
> 
>             let reader = my_reader.into_inner();
>             reader.read_exact(&mut vec![0; 10]).unwrap();
> ```
> 
> but i understand if you think this is too obtuse

I'm not against the change, it's technically less restrictive. Do you have a source for the "rust won't automatically reborrow generics"?, I've run into this before and never went any further into research of the problem.

Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 81684c5..a6a3e0e 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -32,7 +32,7 @@ jobs:
       matrix:
         toolchain:
             # msrv
-          - 1.71
+          - 1.81
 
     steps:
       - uses: actions/checkout@v4
diff --git a/Cargo.toml b/Cargo.toml
index 4aa5c9a..88fc280 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,7 @@ keywords = ["deku", "bits", "serialization", "deserialization", "struct"]
 categories = ["encoding", "parsing", "no-std"]
 description = "bit level serialization/deserialization proc-macro for structs"
 readme = "README.md"
-rust-version = "1.71"
+rust-version = "1.81"
 
 [lib]
 bench = false
@@ -32,7 +32,7 @@ bits = ["dep:bitvec", "deku_derive/bits"]
 deku_derive = { version = "^0.18.1", path = "deku-derive", default-features = false}
 bitvec = { version = "1.0.1", default-features = false, optional = true }
 log = { version = "0.4.22", optional = true }
-no_std_io = { version = "0.8.0", default-features = false, features = ["alloc"], package = "no_std_io2" }
+no_std_io = { version = "0.9.0", default-features = false, features = ["alloc"], package = "no_std_io2" }
 rustversion = "1.0.17"
 
 [dev-dependencies]
@@ -41,7 +41,7 @@ hexlit = "0.5.5"
 criterion = "0.5.1"
 alloc_counter = "0.0.4"
 trybuild = "1.0.99"
-rustc-hash = "2.0.0"
+rustc-hash = "=2.1.0"
 env_logger = "0.11.5"
 assert_hex = "0.4.1"
 
diff --git a/README.md b/README.md
index ff8e76b..6bc2bc3 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Avoid the requirement of writing redundant, error-prone parsing and writing code
 for binary structs or network headers
 
 ## Usage
-*Compiler support: requires rustc 1.71+*
+*Compiler support: requires rustc 1.81+*
 
 ```toml
 [dependencies]
diff --git a/deku-derive/Cargo.toml b/deku-derive/Cargo.toml
index a15f99c..8733b41 100644
--- a/deku-derive/Cargo.toml
+++ b/deku-derive/Cargo.toml
@@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
 repository = "https://github.com/sharksforarms/deku"
 description = "bit level serialization/deserialization proc-macro for structs"
 readme = "../README.md"
-rust-version = "1.71"
+rust-version = "1.81"
 
 [lib]
 proc-macro = true
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index 5526e9d..60f1ea5 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -371,6 +371,7 @@ 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)*
@@ -822,6 +823,7 @@ fn emit_field_read(
                     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,
diff --git a/ensure_no_std/src/bin/main.rs b/ensure_no_std/src/bin/main.rs
index 6772955..e93fd0c 100644
--- a/ensure_no_std/src/bin/main.rs
+++ b/ensure_no_std/src/bin/main.rs
@@ -34,7 +34,7 @@ fn main() -> ! {
     {
         use core::mem::MaybeUninit;
         const HEAP_SIZE: usize = 1024;
-        static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
+        static HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
         unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
     }
 
diff --git a/src/impls/arc.rs b/src/impls/arc.rs
new file mode 100644
index 0000000..d00af34
--- /dev/null
+++ b/src/impls/arc.rs
@@ -0,0 +1,151 @@
+use no_std_io::io::{Read, Seek, Write};
+use std::sync::Arc;
+
+use alloc::vec::Vec;
+
+use crate::ctx::Limit;
+use crate::reader::Reader;
+use crate::writer::Writer;
+use crate::{DekuError, DekuReader, DekuWriter};
+
+impl<'a, T, Ctx> DekuReader<'a, Ctx> for Arc<T>
+where
+    T: DekuReader<'a, Ctx>,
+    Ctx: Copy,
+{
+    fn from_reader_with_ctx<R: Read + Seek>(
+        reader: &mut Reader<R>,
+        inner_ctx: Ctx,
+    ) -> Result<Self, DekuError> {
+        let val = <T>::from_reader_with_ctx(reader, inner_ctx)?;
+        Ok(Arc::new(val))
+    }
+}
+
+impl<'a, T, Ctx, Predicate> DekuReader<'a, (Limit<T, Predicate>, Ctx)> for Arc<[T]>
+where
+    T: DekuReader<'a, Ctx>,
+    Ctx: Copy,
+    Predicate: FnMut(&T) -> bool,
+{
+    fn from_reader_with_ctx<R: Read + Seek>(
+        reader: &mut Reader<R>,
+        (limit, inner_ctx): (Limit<T, Predicate>, Ctx),
+    ) -> Result<Self, DekuError> {
+        // use Vec<T>'s implementation and convert to Arc<[T]>
+        let val = <Vec<T>>::from_reader_with_ctx(reader, (limit, inner_ctx))?;
+        Ok(Arc::from(val.into_boxed_slice()))
+    }
+}
+
+impl<T, Ctx> DekuWriter<Ctx> for Arc<[T]>
+where
+    T: DekuWriter<Ctx>,
+    Ctx: Copy,
+{
+    /// Write all `T`s to bits
+    fn to_writer<W: Write + Seek>(
+        &self,
+        writer: &mut Writer<W>,
+        ctx: Ctx,
+    ) -> Result<(), DekuError> {
+        for v in self.as_ref() {
+            v.to_writer(writer, ctx)?;
+        }
+        Ok(())
+    }
+}
+
+impl<T, Ctx> DekuWriter<Ctx> for Arc<T>
+where
+    T: DekuWriter<Ctx>,
+    Ctx: Copy,
+{
+    /// Write all `T`s to bits
+    fn to_writer<W: Write + Seek>(
+        &self,
+        writer: &mut Writer<W>,
+        ctx: Ctx,
+    ) -> Result<(), DekuError> {
+        self.as_ref().to_writer(writer, ctx)?;
+        Ok(())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use no_std_io::io::Cursor;
+    use rstest::rstest;
+
+    use super::*;
+    use crate::ctx::*;
+    use crate::native_endian;
+    use crate::reader::Reader;
+    #[cfg(feature = "bits")]
+    use bitvec::prelude::*;
+
+    #[rstest(input, expected,
+        case(
+            &[0xEF, 0xBE],
+            Arc::new(native_endian!(0xBEEF_u16)),
+        ),
+    )]
+    fn test_boxed(input: &[u8], expected: Arc<u16>) {
+        let mut cursor = Cursor::new(input);
+        let mut reader = Reader::new(&mut cursor);
+        let res_read = <Arc<u16>>::from_reader_with_ctx(&mut reader, ()).unwrap();
+        assert_eq!(expected, res_read);
+
+        let mut writer = Writer::new(Cursor::new(vec![]));
+        res_read.to_writer(&mut writer, ()).unwrap();
+        assert_eq!(input.to_vec(), writer.inner.into_inner());
+    }
+
+    // Note: Copied tests from vec.rs impl
+    #[cfg(feature = "bits")]
+    #[rstest(input, endian, bit_size, limit, expected, expected_rest_bits, expected_rest_bytes, expected_write,
+        case::normal_le([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Little, Some(16), 2.into(), Arc::from(vec![0xBBAA, 0xDDCC].into_boxed_slice()), bits![u8, Msb0;], &[], vec![0xAA, 0xBB, 0xCC, 0xDD]),
+        case::normal_be([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Big, Some(16), 2.into(), Arc::from(vec![0xAABB, 0xCCDD].into_boxed_slice()), bits![u8, Msb0;], &[], vec![0xAA, 0xBB, 0xCC, 0xDD]),
+        case::predicate_le([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Little, Some(16), (|v: &u16| *v == 0xBBAA).into(), Arc::from(vec![0xBBAA].into_boxed_slice()), bits![u8, Msb0;], &[0xcc, 0xdd], vec![0xAA, 0xBB]),
+        case::predicate_be([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Big, Some(16), (|v: &u16| *v == 0xAABB).into(), Arc::from(vec![0xAABB].into_boxed_slice()), bits![u8, Msb0;], &[0xcc, 0xdd], vec![0xAA, 0xBB]),
+        case::bytes_le([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Little, Some(16), BitSize(16).into(), Arc::from(vec![0xBBAA].into_boxed_slice()), bits![u8, Msb0;], &[0xcc, 0xdd], vec![0xAA, 0xBB]),
+        case::bytes_be([0xAA, 0xBB, 0xCC, 0xDD].as_ref(), Endian::Big, Some(16), BitSize(16).into(), Arc::from(vec![0xAABB].into_boxed_slice()), bits![u8, Msb0;], &[0xcc, 0xdd], vec![0xAA, 0xBB]),
+    )]
+    fn test_boxed_slice_from_reader_with_ctx<Predicate: FnMut(&u16) -> bool>(
+        input: &[u8],
+        endian: Endian,
+        bit_size: Option<usize>,
+        limit: Limit<u16, Predicate>,
+        expected: Arc<[u16]>,
+        expected_rest_bits: &bitvec::slice::BitSlice<u8, bitvec::prelude::Msb0>,
+        expected_rest_bytes: &[u8],
+        expected_write: Vec<u8>,
+    ) {
+        // Unwrap here because all test cases are `Some`.
+        let bit_size = bit_size.unwrap();
+
+        let mut cursor = Cursor::new(input);
+        let mut reader = Reader::new(&mut cursor);
+        let res_read =
+            <Arc<[u16]>>::from_reader_with_ctx(&mut reader, (limit, (endian, BitSize(bit_size))))
+                .unwrap();
+        assert_eq!(expected, res_read);
+        assert_eq!(
+            reader.rest(),
+            expected_rest_bits.iter().by_vals().collect::<Vec<bool>>()
+        );
+        let mut buf = vec![];
+        cursor.read_to_end(&mut buf).unwrap();
+        assert_eq!(expected_rest_bytes, buf);
+
+        assert_eq!(input[..expected_write.len()].to_vec(), expected_write);
+
+        let mut writer = Writer::new(Cursor::new(vec![]));
+        res_read
+            .to_writer(&mut writer, (endian, BitSize(bit_size)))
+            .unwrap();
+        assert_eq!(expected_write, writer.inner.into_inner());
+
+        assert_eq!(input[..expected_write.len()].to_vec(), expected_write);
+    }
+}
diff --git a/src/impls/mod.rs b/src/impls/mod.rs
index 2b9b3ed..fd4a04a 100644
--- a/src/impls/mod.rs
+++ b/src/impls/mod.rs
@@ -7,6 +7,9 @@ mod tuple;
 mod unit;
 mod vec;
 
+#[cfg(feature = "std")]
+mod arc;
+
 #[cfg(feature = "std")]
 mod cow;
 
diff --git a/src/noseek.rs b/src/noseek.rs
index fc9264e..7ead7eb 100644
--- a/src/noseek.rs
+++ b/src/noseek.rs
@@ -1,9 +1,10 @@
 //! Wrapper type that provides a fake [`Seek`] implementation.
 
 #[cfg(not(feature = "std"))]
-use alloc::{string::String, vec::Vec};
+use alloc::vec::Vec;
 
 use crate::no_std_io::{Read, Result, Seek, SeekFrom, Write};
+#[cfg(feature = "std")]
 use no_std_io::io::ErrorKind;
 
 /// A wrapper that provides a limited implementation of
diff --git a/src/reader.rs b/src/reader.rs
index 1ff56a7..9e5bea8 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -7,7 +7,7 @@ use bitvec::prelude::*;
 use no_std_io::io::{ErrorKind, Read, Seek, SeekFrom};
 
 use crate::{ctx::Order, prelude::NeedSize, DekuError};
-use alloc::vec::Vec;
+use alloc::{vec, vec::Vec};
 
 #[cfg(feature = "logging")]
 use log;
@@ -21,28 +21,28 @@ 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;
-
-#[derive(Debug)]
-enum Leftover {
+/// Bits or Byte stored from previous read, such as the case of id_pat
+#[derive(Debug, Clone)]
+pub enum Leftover {
+    /// byte value
     Byte(u8),
+    /// bit values
     #[cfg(feature = "bits")]
     Bits(BitVec<u8, Msb0>),
 }
 
 /// Reader to use with `from_reader_with_ctx`
-pub struct Reader<'a, R: Read + Seek> {
-    inner: &'a mut R,
+pub struct Reader<R: Read + Seek> {
+    inner: R,
     /// bits stored from previous reads that didn't read to the end of a byte size
-    leftover: Option<Leftover>,
+    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,
 }
 
-impl<R: Read + Seek> Seek for Reader<'_, R> {
+impl<R: Read + Seek> Seek for Reader<R> {
     #[inline]
     fn seek(&mut self, pos: SeekFrom) -> no_std_io::io::Result<u64> {
         #[cfg(feature = "logging")]
@@ -50,21 +50,38 @@ impl<R: Read + Seek> Seek for Reader<'_, R> {
 
         // clear leftover
         self.leftover = None;
+        // set bits read
+        match pos {
+            // When reading from the start, reset the bits_read so from_bytes
+            // return can still be reasonable
+            SeekFrom::Start(n) => {
+                if n > 0 {
+                    self.bits_read = (n * 8) as usize;
+                }
+            }
+            SeekFrom::End(_) => (),
+            // If seeking from current, act as if we just read those bytes
+            SeekFrom::Current(n) => {
+                if n > 0 {
+                    self.bits_read += (n * 8) as usize;
+                }
+            }
+        }
         self.inner.seek(pos)
     }
 }
 
-impl<R: Read + Seek> AsMut<R> for Reader<'_, R> {
+impl<R: Read + Seek> AsMut<R> for Reader<R> {
     #[inline]
     fn as_mut(&mut self) -> &mut R {
-        self.inner
+        &mut self.inner
     }
 }
 
-impl<'a, R: Read + Seek> Reader<'a, R> {
+impl<R: Read + Seek> Reader<R> {
     /// Create a new `Reader`
     #[inline]
-    pub fn new(inner: &'a mut R) -> Self {
+    pub fn new(inner: R) -> Self {
         Self {
             inner,
             leftover: None,
@@ -76,10 +93,17 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
     /// 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()))?;
-        self.bits_read -= self.last_bits_read_amt;
+
+        // restore bits read, minus bits we read last time
+        self.bits_read = bits_read - self.last_bits_read_amt;
         self.leftover = None;
 
         Ok(())
@@ -87,7 +111,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
 
     /// Consume self, returning inner Reader
     #[inline]
-    pub fn into_inner(self) -> &'a mut R {
+    pub fn into_inner(self) -> R {
         self.inner
     }
 
@@ -186,9 +210,10 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
                     i64::try_from(bytes_amt).expect("could not convert seek usize into i64"),
                 ))
                 .map_err(|e| DekuError::Io(e.kind()))?;
+                self.bits_read = 0;
             }
 
-            // Unlike normal seek not couting as bits_read, this one does
+            // Unlike normal seek not counting as bits_read, this one does
             // to keep from_bytes returns
             self.bits_read += bytes_amt * 8;
 
@@ -214,7 +239,7 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
     ///   `self.bits_read` will increase by `amt`
     ///
     /// # Params
-    /// `amt`    - Amount of bits that will be read. Must be <= [`MAX_BITS_AMT`].
+    /// `amt`    - Amount of bits that will be read
     #[inline(never)]
     #[cfg(feature = "bits")]
     pub fn read_bits(
@@ -282,8 +307,8 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
                 }
 
                 // read in new bytes
-                let mut buf = [0; MAX_BITS_AMT];
-                if let Err(e) = self.inner.read_exact(&mut buf[..bytes_len]) {
+                let mut buf = vec![0; bytes_len];
+                if let Err(e) = self.inner.read_exact(&mut buf) {
                     if e.kind() == ErrorKind::UnexpectedEof {
                         return Err(DekuError::Incomplete(NeedSize::new(amt)));
                     }
@@ -803,4 +828,20 @@ mod tests {
         let bits = reader.read_bits(4, Order::Msb0).unwrap();
         assert_eq!(bits, Some(bitvec![u8, Msb0; 0, 1, 0, 1]));
     }
+
+    #[cfg(feature = "bits")]
+    #[test]
+    fn test_long_unaligned_bytes_read() {
+        let input = vec![0xff; 0xff * 2];
+        let mut cursor = Cursor::new(input);
+        let mut reader = Reader::new(&mut cursor);
+
+        // read 1 bits to make this unaligned
+        let bits = reader.read_bits(1, Order::Lsb0).unwrap();
+        assert_eq!(bits, Some(bitvec![u8, Msb0; 1]));
+        // Now, read the bytes
+        let mut out = vec![0x00; 0xff * 2];
+        // doesn't crash
+        let _ = reader.read_bytes(0xfe * 2, &mut out, Order::Lsb0).unwrap();
+    }
 }


Test patch:
diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs
index eee3319..9050c01 100644
--- a/tests/test_alloc.rs
+++ b/tests/test_alloc.rs
@@ -66,7 +66,7 @@ mod tests {
                 let _ = TestDeku::from_reader((&mut cursor, 0)).unwrap();
             })
             .0,
-            (5, 0, 5)
+            (6, 0, 6)
         );
     }
 
diff --git a/tests/test_regression.rs b/tests/test_regression.rs
index e3a7589..89bfa8f 100644
--- a/tests/test_regression.rs
+++ b/tests/test_regression.rs
@@ -385,3 +385,45 @@ fn issue_397() {
     }
     let _ = Packet::from_bytes((&[0x00, 0x01], 0));
 }
+
+#[cfg(feature = "bits")]
+#[test]
+fn issue_533() {
+    #[derive(PartialEq, Debug, DekuRead)]
+    #[deku(id_type = "u8", bits = "1")]
+    pub enum BitsAndValue {
+        #[deku(id = 0)]
+        Zero,
+        #[deku(id_pat = "_")]
+        Other(#[deku(bits = 1)] u8),
+    }
+    let input = [0b0100_0000];
+    let mut cursor = Cursor::new(input);
+    let mut reader = Reader::new(&mut cursor);
+    let v = BitsAndValue::from_reader_with_ctx(&mut reader, ()).unwrap();
+    assert_eq!(v, BitsAndValue::Zero);
+
+    let v = BitsAndValue::from_reader_with_ctx(&mut reader, ()).unwrap();
+    assert_eq!(v, BitsAndValue::Other(1));
+}
+
+#[test]
+fn issue_525() {
+    env_logger::init();
+    use deku::{DekuContainerRead, DekuRead, DekuWrite};
+
+    #[derive(Debug, DekuRead, DekuWrite, PartialEq)]
+    #[deku(endian = "big")]
+    pub struct TestSeek {
+        pub a: u8,
+        #[deku(seek_from_start = "2")]
+        pub b: u8,
+        pub c: u8,
+    }
+
+    let data: &[u8] = &[0, 1, 2, 3, 4, 5];
+    let test = TestSeek::from_bytes((data, 0)).unwrap();
+    let ret: &[u8] = &[4, 5];
+    assert_eq!(test.0, (ret, 0));
+    assert_eq!(test.1, TestSeek { a: 0, b: 2, c: 3 });
+}