Repository: sharksforarms/deku
Pull request #579 (2025-07-20 11:29:29)

Change arc/cow/cstring requirements from std to alloc
 `DekuReader` and `DekuWriter` can be implemented for `Arc`, `Cow` and `CString` with only `alloc` and not `std`, which would be a good thing for `no_std` environments.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 72f7a00..f70899e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,7 +25,6 @@ std = ["deku_derive/std", "bitvec?/std", "alloc", "no_std_io/std"]
 alloc = ["bitvec?/alloc"]
 logging = ["deku_derive/logging", "log"]
 no-assert-string = ["deku_derive/no-assert-string"]
-error_in_core = []
 bits = ["dep:bitvec", "deku_derive/bits"]
 
 [dependencies]
diff --git a/deku-derive/src/macros/deku_read.rs b/deku-derive/src/macros/deku_read.rs
index be98005..deaa5a0 100644
--- a/deku-derive/src/macros/deku_read.rs
+++ b/deku-derive/src/macros/deku_read.rs
@@ -137,7 +137,10 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
             } else {
                 (__deku_reader.bits_read - (__deku_reader.bits_read % 8)) / 8
             };
-            Ok(((&__deku_input.0[idx..], __deku_reader.bits_read % 8), __deku_value))
+            let Some(rest) = __deku_input.0.get(idx..) else {
+                return Err(deku::DekuError::Incomplete(deku::prelude::NeedSize::new(8 * (idx - __deku_input.0.len()))));
+            };
+            Ok(((rest, __deku_reader.bits_read % 8), __deku_value))
         };
 
         tokens.extend(emit_try_from(&imp, &lifetime, &ident, wher));
@@ -436,7 +439,10 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
             } else {
                 (__deku_reader.bits_read - (__deku_reader.bits_read % 8)) / 8
             };
-            Ok(((&__deku_input.0[idx..], __deku_reader.bits_read % 8), __deku_value))
+            let Some(rest) = __deku_input.0.get(idx..) else {
+                return Err(deku::DekuError::Incomplete(deku::prelude::NeedSize::new(8 * (idx - __deku_input.0.len()))));
+            };
+            Ok(((rest, __deku_reader.bits_read % 8), __deku_value))
         };
 
         tokens.extend(emit_try_from(&imp, &lifetime, &ident, wher));
diff --git a/src/error.rs b/src/error.rs
index d41571c..2cc3cfa 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -90,6 +90,25 @@ impl core::fmt::Display for DekuError {
     }
 }
 
+#[cfg(not(feature = "std"))]
+impl core::error::Error for DekuError {}
+
+#[cfg(not(feature = "std"))]
+impl From<DekuError> for no_std_io::io::Error {
+    fn from(error: DekuError) -> Self {
+        use no_std_io::io;
+        match error {
+            DekuError::Incomplete(_) => io::Error::new(io::ErrorKind::UnexpectedEof, error),
+            DekuError::Parse(_) => io::Error::new(io::ErrorKind::InvalidData, error),
+            DekuError::InvalidParam(_) => io::Error::new(io::ErrorKind::InvalidInput, error),
+            DekuError::Assertion(_) => io::Error::new(io::ErrorKind::InvalidData, error),
+            DekuError::AssertionNoStr => io::Error::from(io::ErrorKind::InvalidData),
+            DekuError::IdVariantNotFound => io::Error::new(io::ErrorKind::NotFound, error),
+            DekuError::Io(e) => io::Error::new(e, error),
+        }
+    }
+}
+
 #[cfg(feature = "std")]
 impl std::error::Error for DekuError {
     fn cause(&self) -> Option<&dyn std::error::Error> {
@@ -112,6 +131,3 @@ impl From<DekuError> for std::io::Error {
         }
     }
 }
-
-#[cfg(feature = "error_in_core")]
-impl core::error::Error for DekuError {}
diff --git a/src/impls/arc.rs b/src/impls/arc.rs
index 7ae6c23..96bbeb1 100644
--- a/src/impls/arc.rs
+++ b/src/impls/arc.rs
@@ -1,7 +1,6 @@
-use no_std_io::io::{Read, Seek, Write};
-use std::sync::Arc;
-
+use alloc::sync::Arc;
 use alloc::vec::Vec;
+use no_std_io::io::{Read, Seek, Write};
 
 use crate::ctx::Limit;
 use crate::reader::Reader;
diff --git a/src/impls/cow.rs b/src/impls/cow.rs
index e903f0e..8a22e1e 100644
--- a/src/impls/cow.rs
+++ b/src/impls/cow.rs
@@ -1,4 +1,4 @@
-use std::borrow::{Borrow, Cow};
+use alloc::borrow::{Borrow, Cow};
 
 use no_std_io::io::{Read, Seek, Write};
 
diff --git a/src/impls/cstring.rs b/src/impls/cstring.rs
index 57eae99..a4b9171 100644
--- a/src/impls/cstring.rs
+++ b/src/impls/cstring.rs
@@ -1,6 +1,8 @@
 use alloc::borrow::Cow;
+use alloc::ffi::CString;
+use alloc::format;
+use alloc::vec::Vec;
 use no_std_io::io::{Read, Seek, Write};
-use std::ffi::CString;
 
 use crate::reader::Reader;
 use crate::writer::Writer;
diff --git a/src/impls/mod.rs b/src/impls/mod.rs
index 9d7e6fe..179caec 100644
--- a/src/impls/mod.rs
+++ b/src/impls/mod.rs
@@ -8,13 +8,13 @@ mod tuple;
 mod unit;
 mod vec;
 
-#[cfg(feature = "std")]
+#[cfg(feature = "alloc")]
 mod arc;
 
-#[cfg(feature = "std")]
+#[cfg(feature = "alloc")]
 mod cow;
 
-#[cfg(feature = "std")]
+#[cfg(feature = "alloc")]
 mod cstring;
 
 #[cfg(feature = "std")]


Test patch:
diff --git a/tests/test_from_bytes.rs b/tests/test_from_bytes.rs
index cb43076..d90da25 100644
--- a/tests/test_from_bytes.rs
+++ b/tests/test_from_bytes.rs
@@ -81,3 +81,30 @@ fn test_from_bytes_long() {
     assert_eq!(6, i);
     assert_eq!(0b0101_1010u8, rest[0]);
 }
+
+#[test]
+fn test_from_bytes_short_with_seek() {
+    #[derive(Debug, DekuRead, Eq, PartialEq)]
+    #[deku(ctx = "mid: u8", id = "mid")]
+    enum Sneaky {
+        #[deku(id = 0)]
+        Zero,
+        #[deku(id = 1)]
+        One,
+    }
+
+    #[derive(Debug, DekuRead, Eq, PartialEq)]
+    struct Seeky {
+        id: u8,
+        // Use seek to force an out-of-bounds slice for remaining data
+        #[deku(seek_from_current = "1")]
+        #[deku(ctx = "*id")]
+        body: Sneaky,
+    }
+
+    let bytes: [u8; 1] = [0x01];
+    assert_eq!(
+        Err(DekuError::Incomplete(NeedSize::new(8))),
+        Seeky::from_bytes((&bytes, 0))
+    );
+}