Repository: sharksforarms/deku
Pull request #494 (2024-10-27 11:55:26)

Documentation fix: rename type to id_type

wcampbell0x2a: Thanks!

Patch:
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index 4331ad4..5413629 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -42,7 +42,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_reader.seek(SeekFrom::Current(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_reader.seek(SeekFrom::Current(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -52,7 +53,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_reader.seek(SeekFrom::End(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_reader.seek(SeekFrom::End(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -62,7 +64,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_reader.seek(SeekFrom::Start(u64::try_from(#num).unwrap())) {
+                let seek_amt = u64::try_from(#num).expect("could not convert into u64");
+                if let Err(e) = __deku_reader.seek(SeekFrom::Start(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -163,6 +166,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 +179,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> {
@@ -358,7 +363,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             let __deku_variant_id = <#id_type>::from_reader_with_ctx(__deku_reader,  (#id_args))?;
         }
     } else {
-        // either `id` or `type` needs to be specified
+        // either `id` or `id_type` needs to be specified
         unreachable!();
     };
 
@@ -433,6 +438,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 +452,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 +477,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> {
@@ -973,6 +981,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]
@@ -998,6 +1007,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..ab32eb9 100644
--- a/deku-derive/src/macros/deku_write.rs
+++ b/deku-derive/src/macros/deku_write.rs
@@ -33,7 +33,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_writer.seek(SeekFrom::Current(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_writer.seek(SeekFrom::Current(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -43,7 +44,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_writer.seek(SeekFrom::End(i64::try_from(#num).unwrap())) {
+                let seek_amt = i64::try_from(#num).expect("could not convert into i64");
+                if let Err(e) = __deku_writer.seek(SeekFrom::End(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -53,7 +55,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             {
                 use ::#crate_::no_std_io::Seek;
                 use ::#crate_::no_std_io::SeekFrom;
-                if let Err(e) = __deku_writer.seek(SeekFrom::Start(u64::try_from(#num).unwrap())) {
+                let seek_amt = u64::try_from(#num).expect("could not convert into u64");
+                if let Err(e) = __deku_writer.seek(SeekFrom::Start(seek_amt)) {
                     return Err(::#crate_::DekuError::Io(e.kind()));
                 }
             }
@@ -93,6 +96,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 +109,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 +119,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
 
+            #[automatically_derived]
             impl #imp ::#crate_::DekuContainerWrite for #ident #wher {}
         });
     }
@@ -136,6 +142,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 +153,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 +167,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]
@@ -262,7 +271,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 ));
             }
         } else {
-            // either `id` or `type` needs to be specified
+            // either `id` or `id_type` needs to be specified
             unreachable!();
         };
 
@@ -300,6 +309,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 +322,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 +332,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
                 }
             }
 
+            #[automatically_derived]
             impl #imp ::#crate_::DekuContainerWrite for #ident #wher {}
         });
     }
@@ -341,6 +353,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 +367,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 +381,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/src/attributes.rs b/src/attributes.rs
index e9b6146..a8d88cf 100644
--- a/src/attributes.rs
+++ b/src/attributes.rs
@@ -65,7 +65,7 @@ enum DekuEnum {
 | enum: [id](#id) | top-level, variant | enum or variant id value
 | enum: [id_endian](#id_endian) | top-level | Endianness of *just* the enum `id`
 | enum: [id_pat](#id_pat) | variant | variant id match pattern
-| enum: [type](#type) | top-level | Set the type of the variant `id`
+| enum: [id_type](#id_type) | top-level | Set the type of the variant `id`
 | enum: [bits](#bits-1) | top-level | Set the bit-size of the variant `id`
 | enum: [bytes](#bytes-1) | top-level | Set the byte-size of the variant `id`
 
@@ -1362,7 +1362,7 @@ assert_eq!(&*ret_write, data)
 
 ## id (variant)
 
-Specify the identifier of the enum variant, must be paired with [type](#type)
+Specify the identifier of the enum variant, must be paired with [id_type](#id_type)
 or [id (top-level)](#id-top-level)
 
 **Note**:
@@ -1533,7 +1533,7 @@ let variant_bytes: Vec<u8> = value.try_into().unwrap();
 assert_eq!(vec![0xFF], variant_bytes);
 ```
 
-# type
+# id_type
 
 Specify the type of the enum variant id to consume, see [example](#id-variant)
 
diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs
index d0796b1..606c70c 100644
--- a/src/impls/cstring.rs
+++ b/src/impls/cstring.rs
@@ -31,6 +31,23 @@ where
     ) -> Result<Self, DekuError> {
         let bytes =
             Vec::from_reader_with_ctx(reader, (Limit::from(|b: &u8| *b == 0x00), inner_ctx))?;
+        let value = CString::from_vec_with_nul(bytes).map_err(|e| {
+            DekuError::Parse(Cow::from(format!("Failed to convert Vec to CString: {e}")))
+        })?;
+
+        Ok(value)
+    }
+}
+
+impl<'a, Ctx: Copy> DekuReader<'a, (ByteSize, Ctx)> for CString
+where
+    u8: DekuReader<'a, Ctx>,
+{
+    fn from_reader_with_ctx<R: Read + Seek>(
+        reader: &mut Reader<R>,
+        (byte_size, inner_ctx): (ByteSize, Ctx),
+    ) -> Result<Self, DekuError> {
+        let bytes = Vec::from_reader_with_ctx(reader, (Limit::from(byte_size.0), inner_ctx))?;
 
         let value = CString::from_vec_with_nul(bytes).map_err(|e| {
             DekuError::Parse(Cow::from(format!("Failed to convert Vec to CString: {e}")))
@@ -49,25 +66,59 @@ mod tests {
 
     use super::*;
 
-    #[rstest(input, expected, expected_rest,
+    #[rstest(input, len, expected, expected_rest,
         case(
             &[b't', b'e', b's', b't', b'\0'],
+            Some(5),
             CString::new("test").unwrap(),
             &[],
         ),
+        case(
+            &[b't', b'e', b's', b't', b'\0'],
+            None,
+            CString::new("test").unwrap(),
+            &[],
+        ),
+        case(
+            &[b't', b'e', b's', b't', b'\0', b'a'],
+            Some(5),
+            CString::new("test").unwrap(),
+            &[b'a'],
+        ),
         case(
             &[b't', b'e', b's', b't', b'\0', b'a'],
+            None,
             CString::new("test").unwrap(),
             &[b'a'],
         ),
 
+        #[should_panic(expected = "Parse(\"Failed to convert Vec to CString: data provided is not nul terminated\")")]
+        case(
+            &[b't', b'e', b's', b't'],
+            Some(4),
+            CString::new("test").unwrap(),
+            &[b'a'],
+        ),
+
+        #[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
+        case(&[b't', b'e', b's', b't'], Some(5), CString::new("test").unwrap(), &[]),
+
         #[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
-        case(&[b't', b'e', b's', b't'], CString::new("test").unwrap(), &[]),
+        case(&[b't', b'e', b's', b't'], None, CString::new("test").unwrap(), &[]),
     )]
-    fn test_cstring(input: &[u8], expected: CString, expected_rest: &[u8]) {
+    fn test_cstring_count(
+        input: &[u8],
+        len: Option<usize>,
+        expected: CString,
+        expected_rest: &[u8],
+    ) {
         let mut cursor = Cursor::new(input);
         let mut reader = Reader::new(&mut cursor);
-        let res_read = CString::from_reader_with_ctx(&mut reader, ()).unwrap();
+        let res_read = if let Some(len) = len {
+            CString::from_reader_with_ctx(&mut reader, (ByteSize(len), ())).unwrap()
+        } else {
+            CString::from_reader_with_ctx(&mut reader, ()).unwrap()
+        };
         assert_eq!(expected, res_read);
         let mut buf = vec![];
         cursor.read_to_end(&mut buf).unwrap();
diff --git a/src/lib.rs b/src/lib.rs
index 845cb8e..8fceea5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -152,12 +152,12 @@ assert_eq!(DekuTest {
 As enums can have multiple variants, each variant must have a way to match on
 the incoming data.
 
-First the "type" is read using the `type`, then is matched against the
+First the "type" is read using `id_type`, then is matched against the
 variants given `id`. What happens after is the same as structs!
 
 This is implemented with the [id](attributes#id),
 [id_pat](attributes#id_pat), [default](attributes#default) and
-[type](attributes#type) attributes. See these for more examples.
+[id_type](attributes#id_type) attributes. See these for more examples.
 
 If no `id` is specified, the variant will default to it's discriminant value.
 
@@ -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(())
+    }
+}
diff --git a/src/reader.rs b/src/reader.rs
index 8252823..80d7e82 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -174,8 +174,24 @@ impl<'a, R: Read + Seek> Reader<'a, R> {
         {
             #[cfg(feature = "logging")]
             log::trace!("skip_bits: {amt}");
+
+            let bytes_amt = amt / 8;
+            let bits_amt = amt % 8;
+
+            // first, seek with bytes
+            if bytes_amt != 0 {
+                self.seek(SeekFrom::Current(
+                    i64::try_from(bytes_amt).expect("could not convert seek usize into i64"),
+                ))
+                .map_err(|e| DekuError::Io(e.kind()))?;
+            }
+
+            // Unlike normal seek not couting as bits_read, this one does
+            // to keep from_bytes returns
+            self.bits_read += bytes_amt * 8;
+
             // Save, and keep the leftover bits since the read will most likely be less than a byte
-            self.read_bits(amt)?;
+            self.read_bits(bits_amt)?;
         }
 
         #[cfg(not(feature = "bits"))]


Test patch:
diff --git a/tests/test_cstring.rs b/tests/test_cstring.rs
new file mode 100644
index 0000000..10d7636
--- /dev/null
+++ b/tests/test_cstring.rs
@@ -0,0 +1,29 @@
+use deku::prelude::*;
+
+use std::ffi::CString;
+
+#[test]
+fn test_cstring_no_ctx() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    pub struct Data {
+        s: CString,
+    }
+
+    let bytes = &[b't', b'e', b's', b't', b'\0'];
+    let (_, d) = Data::from_bytes((bytes, 0)).unwrap();
+    assert_eq!(d.s, CString::new("test").unwrap());
+}
+
+#[test]
+fn test_cstring_bytes() {
+    #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+    pub struct Data {
+        len: u8,
+        #[deku(bytes = "*len as usize")]
+        s: CString,
+    }
+
+    let bytes = &[0x05, b't', b'e', b's', b't', b'\0'];
+    let (_, d) = Data::from_bytes((bytes, 0)).unwrap();
+    assert_eq!(d.s, CString::new("test").unwrap());
+}
diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs
index aac1f4d..cb43076 100644
--- a/tests/test_from_bytes.rs
+++ b/tests/test_from_bytes.rs
@@ -54,3 +54,30 @@ fn test_from_bytes_enum() {
     assert_eq!(6, i);
     assert_eq!(0b0101_1010u8, rest[0]);
 }
+
+#[cfg(feature = "bits")]
+#[test]
+fn test_from_bytes_long() {
+    #[derive(Debug, PartialEq, DekuRead, DekuWrite)]
+    #[deku(id_type = "u8", bits = 4)]
+    enum TestDeku {
+        #[deku(id = "0b0110")]
+        VariantA(#[deku(bits = 4)] u8),
+        #[deku(id = "0b0101")]
+        VariantB(#[deku(bits = 2)] u8),
+    }
+
+    let mut test_data = vec![0x00; 200];
+    test_data.extend([0b0110_0110u8, 0b0101_1010u8].to_vec());
+
+    let ((rest, i), ret_read) = TestDeku::from_bytes((&test_data, 200 * 8)).unwrap();
+    assert_eq!(TestDeku::VariantA(0b0110), ret_read);
+    assert_eq!(1, rest.len());
+    assert_eq!(0, i);
+
+    let ((rest, i), ret_read) = TestDeku::from_bytes((rest, i)).unwrap();
+    assert_eq!(TestDeku::VariantB(0b10), ret_read);
+    assert_eq!(1, rest.len());
+    assert_eq!(6, i);
+    assert_eq!(0b0101_1010u8, rest[0]);
+}
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")]