Repository: sharksforarms/deku
Pull request #359 (2023-09-01 05:15:48)

Add support for values of zero in bits_read and bytes_read attributes
Currently the count attribute supports counts of zero, but the bits_read and bytes_read attributes do not. This PR adds support for these cases so that they behave the same way as the count attribute.

calebfletcher: For reference the reason why this change is needed is because the call to read_vec_with_predicate reads one element, then checks the predicate, so if the decoder input is an empty slice then it would always fail with an error such as `Incomplete(NeedSize { bits: 8 })`.

Patch:
diff --git a/src/impls/hashmap.rs b/src/impls/hashmap.rs
index 4a63275..9b0d94a 100644
--- a/src/impls/hashmap.rs
+++ b/src/impls/hashmap.rs
@@ -97,6 +97,12 @@ impl<
             // Read until a given quantity of bits have been read
             Limit::BitSize(size) => {
                 let bit_size = size.0;
+
+                // Handle the trivial case of reading an empty hashmap
+                if bit_size == 0 {
+                    return Ok((input, HashMap::<K, V, S>::default()));
+                }
+
                 read_hashmap_with_predicate(input, None, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
@@ -105,6 +111,12 @@ impl<
             // Read until a given quantity of bits have been read
             Limit::ByteSize(size) => {
                 let bit_size = size.0 * 8;
+
+                // Handle the trivial case of reading an empty hashmap
+                if bit_size == 0 {
+                    return Ok((input, HashMap::<K, V, S>::default()));
+                }
+
                 read_hashmap_with_predicate(input, None, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
diff --git a/src/impls/hashset.rs b/src/impls/hashset.rs
index eb21532..7492e02 100644
--- a/src/impls/hashset.rs
+++ b/src/impls/hashset.rs
@@ -96,6 +96,12 @@ impl<
             // Read until a given quantity of bits have been read
             Limit::BitSize(size) => {
                 let bit_size = size.0;
+
+                // Handle the trivial case of reading an empty hashset
+                if bit_size == 0 {
+                    return Ok((input, HashSet::<T, S>::default()));
+                }
+
                 read_hashset_with_predicate(input, None, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
@@ -104,6 +110,12 @@ impl<
             // Read until a given quantity of bits have been read
             Limit::ByteSize(size) => {
                 let bit_size = size.0 * 8;
+
+                // Handle the trivial case of reading an empty hashset
+                if bit_size == 0 {
+                    return Ok((input, HashSet::<T, S>::default()));
+                }
+
                 read_hashset_with_predicate(input, None, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
diff --git a/src/impls/slice.rs b/src/impls/slice.rs
index bd01b09..6f85c21 100644
--- a/src/impls/slice.rs
+++ b/src/impls/slice.rs
@@ -81,6 +81,12 @@ where
             // Read until a given quantity of bits have been read
             Limit::BitSize(size) => {
                 let bit_size = size.0;
+
+                // Handle the trivial case of reading an empty vector
+                if bit_size == 0 {
+                    return Ok((input, &input.domain().region().unwrap().1[..0]));
+                }
+
                 read_slice_with_predicate(input, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
@@ -89,6 +95,12 @@ where
             // Read until a given quantity of bytes have been read
             Limit::ByteSize(size) => {
                 let bit_size = size.0 * 8;
+
+                // Handle the trivial case of reading an empty vector
+                if bit_size == 0 {
+                    return Ok((input, &input.domain().region().unwrap().1[..0]));
+                }
+
                 read_slice_with_predicate(input, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
diff --git a/src/impls/vec.rs b/src/impls/vec.rs
index 7f158f4..d936819 100644
--- a/src/impls/vec.rs
+++ b/src/impls/vec.rs
@@ -90,6 +90,12 @@ impl<'a, T: DekuRead<'a, Ctx>, Ctx: Copy, Predicate: FnMut(&T) -> bool>
             // Read until a given quantity of bits have been read
             Limit::BitSize(size) => {
                 let bit_size = size.0;
+
+                // Handle the trivial case of reading an empty vector
+                if bit_size == 0 {
+                    return Ok((input, Vec::new()));
+                }
+
                 read_vec_with_predicate(input, None, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })
@@ -98,6 +104,12 @@ impl<'a, T: DekuRead<'a, Ctx>, Ctx: Copy, Predicate: FnMut(&T) -> bool>
             // Read until a given quantity of bits have been read
             Limit::ByteSize(size) => {
                 let bit_size = size.0 * 8;
+
+                // Handle the trivial case of reading an empty vector
+                if bit_size == 0 {
+                    return Ok((input, Vec::new()));
+                }
+
                 read_vec_with_predicate(input, None, inner_ctx, move |read_bits, _| {
                     read_bits == bit_size
                 })


Test patch:
diff --git a/tests/test_attributes/test_limits/test_bits_read.rs b/tests/test_attributes/test_limits/test_bits_read.rs
index a23ddeb..a80bbcf 100644
--- a/tests/test_attributes/test_limits/test_bits_read.rs
+++ b/tests/test_attributes/test_limits/test_bits_read.rs
@@ -59,6 +59,28 @@ mod test_slice {
         let ret_write: Vec<u8> = ret_read.try_into().unwrap();
         assert_eq!(test_data, ret_write);
     }
+
+    #[test]
+    fn test_bits_read_zero() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct<'a> {
+            #[deku(bits_read = "0")]
+            data: &'a [u8],
+        }
+
+        let test_data: Vec<u8> = [].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_ref()).unwrap();
+        assert_eq!(
+            TestStruct {
+                data: test_data.as_ref()
+            },
+            ret_read
+        );
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
 }
 
 mod test_vec {
@@ -123,4 +145,21 @@ mod test_vec {
         let ret_write: Vec<u8> = ret_read.try_into().unwrap();
         assert_eq!(test_data, ret_write);
     }
+
+    #[test]
+    fn test_bits_read_zero() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct {
+            #[deku(endian = "little", bits_read = "0")]
+            data: Vec<u16>,
+        }
+
+        let test_data: Vec<u8> = [].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_ref()).unwrap();
+        assert_eq!(TestStruct { data: vec![] }, ret_read);
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
 }
diff --git a/tests/test_attributes/test_limits/test_bytes_read.rs b/tests/test_attributes/test_limits/test_bytes_read.rs
index 38ffa1c..28287d3 100644
--- a/tests/test_attributes/test_limits/test_bytes_read.rs
+++ b/tests/test_attributes/test_limits/test_bytes_read.rs
@@ -56,6 +56,28 @@ mod test_slice {
         let ret_write: Vec<u8> = ret_read.try_into().unwrap();
         assert_eq!(test_data, ret_write);
     }
+
+    #[test]
+    fn test_bytes_read_zero() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct<'a> {
+            #[deku(bytes_read = "0")]
+            data: &'a [u8],
+        }
+
+        let test_data: Vec<u8> = [].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_ref()).unwrap();
+        assert_eq!(
+            TestStruct {
+                data: test_data.as_ref()
+            },
+            ret_read
+        );
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
 }
 
 mod test_vec {
@@ -117,4 +139,21 @@ mod test_vec {
         let ret_write: Vec<u8> = ret_read.try_into().unwrap();
         assert_eq!(test_data, ret_write);
     }
+
+    #[test]
+    fn test_bytes_read_zero() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct {
+            #[deku(endian = "little", bytes_read = "0")]
+            data: Vec<u16>,
+        }
+
+        let test_data: Vec<u8> = [].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_ref()).unwrap();
+        assert_eq!(TestStruct { data: vec![] }, ret_read);
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
 }
diff --git a/tests/test_attributes/test_limits/test_count.rs b/tests/test_attributes/test_limits/test_count.rs
index 0eb8ef5..790ce52 100644
--- a/tests/test_attributes/test_limits/test_count.rs
+++ b/tests/test_attributes/test_limits/test_count.rs
@@ -50,6 +50,28 @@ mod test_slice {
         assert_eq!(test_data, ret_write);
     }
 
+    #[test]
+    fn test_count_zero() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct<'a> {
+            #[deku(count = "0")]
+            data: &'a [u8],
+        }
+
+        let test_data: Vec<u8> = [].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_ref()).unwrap();
+        assert_eq!(
+            TestStruct {
+                data: test_data.as_ref()
+            },
+            ret_read
+        );
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
+
     #[test]
     #[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
     fn test_count_error() {
@@ -115,6 +137,23 @@ mod test_vec {
         assert_eq!(test_data, ret_write);
     }
 
+    #[test]
+    fn test_count_zero() {
+        #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
+        struct TestStruct {
+            #[deku(count = "0")]
+            data: Vec<u8>,
+        }
+
+        let test_data: Vec<u8> = [].to_vec();
+
+        let ret_read = TestStruct::try_from(test_data.as_ref()).unwrap();
+        assert_eq!(TestStruct { data: vec![] }, ret_read);
+
+        let ret_write: Vec<u8> = ret_read.try_into().unwrap();
+        assert_eq!(test_data, ret_write);
+    }
+
     #[test]
     #[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
     fn test_count_error() {