Repository: sharksforarms/deku (rust)

Issue:
Problems serializing signed values (negative values) in bitfields
When using signed types in bitfields I cannot serialize negative values (deserializing works).
With the example below I get `encode error: InvalidParam("bit size 16 of input is larger than bit requested size 10")`.

Any idea where to start? I suspect this can be solved maybe in library level (not in the macro?). Any hint is appreciated, then I could prepare a PR... **The changed code would check that for signed values the "unused" bits are all 0 or all 1 - unlike for unsigned values, where the "unsed" bits must all be 0.**
The error message ("input is larger than bit requested size") can clearly be allocated to a code location...

```rust
#[test]
fn check_signed_i10_decode_encode() {
    #[derive(Debug,PartialEq,Default,Clone,DekuRead,DekuWrite)]
    pub struct TestStruct {
        #[deku(bits="1")]
        pub a: bool,
        #[deku(pad_bits_before = "5", bits="10", endian = "big")]
        pub b: i16,
    }

    let buffer = vec![0b10000011,0b11111110];
    //                        ^^   ^^^^^^^^10 bits

    let ((remaining_bytes, offset), test_struct) = TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
    // println!("{decoded_asterix:#?}");
    
    // everything consumed?
    assert_eq!(offset, 0);
    assert_eq!(remaining_bytes.len(), 0);

    // check content
    assert_eq!(test_struct.a, true);
    assert_eq!(test_struct.b, -2);

    // write back and check
    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"))
}
```

Pull request description:
Bugfix/signed values in bitfields
This PR fixes #564 

**Background**: Deku checks that the stored value fits into the specified bitfield.
**Problem**: There was a problem with checking the validity of signed integral values in bitfields: negative values have "1" set all along the unused area (for negative values; e.g. `-2u4 stored in a u8 = 0x1111 1110`). This caused a problem (deku checked for "1" in the unused region; see #564).

**Solution**:
* The logic was changed for signed types - separately for positive and negative values.
* I enhanced the macro `ImplDekuWrite` to accept an additional flag "Signed" or "Unsigned". All uses were adapted (importantly, for signd types we call the signed variant).
* I renamed some other macros accordingly (Signed/Unsigned) to make clear what is called/generated.
* I added a test for the different cases, as well, as for some trivial cases to see that it works as expected (min/max values and values one below minimum and one above maximum).

I am happy to get feedback (there is a coverage problem, see below).


Patch:
diff --git a/src/impls/primitive.rs b/src/impls/primitive.rs
index 439ada1..74578fc 100644
--- a/src/impls/primitive.rs
+++ b/src/impls/primitive.rs
@@ -758,7 +758,7 @@ macro_rules! ForwardDekuRead {
 }
 
 macro_rules! ImplDekuWrite {
-    ($typ:ty) => {
+    ($typ:ty, $signed_type:ident) => {
         #[cfg(feature = "bits")]
         impl DekuWriter<(Endian, BitSize, Order)> for $typ {
             #[inline]
@@ -817,6 +817,55 @@ macro_rules! ImplDekuWrite {
             }
         }
 
+        ImplDekuWriteDetails!($typ, $signed_type);
+
+        impl DekuWriter<(Endian, ByteSize)> for $typ {
+            #[inline(always)]
+            fn to_writer<W: Write + Seek>(
+                &self,
+                writer: &mut Writer<W>,
+                (endian, size): (Endian, ByteSize),
+            ) -> Result<(), DekuError> {
+                let input = match endian {
+                    Endian::Little => self.to_le_bytes(),
+                    Endian::Big => self.to_be_bytes(),
+                };
+
+                const TYPE_SIZE: usize = core::mem::size_of::<$typ>();
+                if size.0 > TYPE_SIZE {
+                    return Err(DekuError::InvalidParam(Cow::from(format!(
+                        "byte size {} is larger then input {}",
+                        size.0, TYPE_SIZE
+                    ))));
+                }
+
+                let input = if matches!(endian, Endian::Big) {
+                    &input[TYPE_SIZE - size.0 as usize..]
+                } else {
+                    &input[..size.0 as usize]
+                };
+
+                writer.write_bytes(&input)?;
+                Ok(())
+            }
+        }
+
+        /// When using Endian and ByteSize, Order is not used
+        impl DekuWriter<(Endian, ByteSize, Order)> for $typ {
+            #[inline]
+            fn to_writer<W: Write + Seek>(
+                &self,
+                writer: &mut Writer<W>,
+                (endian, size, _order): (Endian, ByteSize, Order),
+            ) -> Result<(), DekuError> {
+                <$typ>::to_writer(self, writer, (endian, size))
+            }
+        }
+    };
+}
+
+macro_rules! ImplDekuWriteDetails {
+    ($typ:ty, Unsigned) => {
         #[cfg(feature = "bits")]
         impl DekuWriter<(Endian, BitSize)> for $typ {
             #[inline(always)]
@@ -888,49 +937,107 @@ macro_rules! ImplDekuWrite {
                 Ok(())
             }
         }
-
-        impl DekuWriter<(Endian, ByteSize)> for $typ {
+    };
+    ($typ:ty, Signed) => {
+        #[cfg(feature = "bits")]
+        impl DekuWriter<(Endian, BitSize)> for $typ {
             #[inline(always)]
             fn to_writer<W: Write + Seek>(
                 &self,
                 writer: &mut Writer<W>,
-                (endian, size): (Endian, ByteSize),
+                (endian, size): (Endian, BitSize),
             ) -> Result<(), DekuError> {
                 let input = match endian {
                     Endian::Little => self.to_le_bytes(),
                     Endian::Big => self.to_be_bytes(),
                 };
 
-                const TYPE_SIZE: usize = core::mem::size_of::<$typ>();
-                if size.0 > TYPE_SIZE {
+                let bit_size: usize = size.0;
+
+                let input_bits = input.view_bits::<Msb0>();
+
+                if bit_size > input_bits.len() {
                     return Err(DekuError::InvalidParam(Cow::from(format!(
-                        "byte size {} is larger then input {}",
-                        size.0, TYPE_SIZE
+                        "bit size {} is larger than input {}",
+                        bit_size,
+                        input_bits.len()
                     ))));
                 }
 
-                let input = if matches!(endian, Endian::Big) {
-                    &input[TYPE_SIZE - size.0 as usize..]
-                } else {
-                    &input[..size.0 as usize]
-                };
+                if matches!(endian, Endian::Little) {
+                    // Check if this is a value that will fit inside the required bits, if
+                    // not, throw an error
+                    if *self>=0 {
+                        let input_bits_lsb = input.view_bits::<Lsb0>();
+                        if let Some(last) = input_bits_lsb.last_one() {
+                            let last = last + 2;
+                            let max = bit_size;
+                            if last > max {
+                                return Err(DekuError::InvalidParam(Cow::from(format!(
+                                    "bit size {last} of input is larger than bit requested size {bit_size}",
+                                ))));
+                            }
+                        }
+                    }
+                    else {
+                        let input_bits_lsb = input.view_bits::<Lsb0>();
+                        if let Some(last) = input_bits_lsb.last_zero() {
+                            let last = last + 2;
+                            let max = bit_size;
+                            if last > max {
+                                return Err(DekuError::InvalidParam(Cow::from(format!(
+                                    "bit size {last} of input is larger than bit requested size {bit_size}",
+                                ))));
+                            }
+                        }
+                    }
 
-                writer.write_bytes(&input)?;
+                    // Example read 10 bits u32 [0xAB, 0b11_000000]
+                    // => [10101011, 00000011, 00000000, 00000000]
+                    let mut remaining_bits = bit_size;
+                    for chunk in input_bits.chunks(8) {
+                        if chunk.len() > remaining_bits {
+                            writer.write_bits(&chunk[chunk.len() - remaining_bits..])?;
+                            break;
+                        } else {
+                            writer.write_bits(&chunk)?;
+                        }
+                        remaining_bits -= chunk.len();
+                    }
+                } else {
+                    const MAX_TYPE_BITS: usize = BitSize::of::<$typ>().0;
+                    // Check for extra bits before sending into writer
+                    if *self>=0 {
+                        if let Some(first) = input_bits.first_one() {
+                            let max = (MAX_TYPE_BITS - bit_size);
+                            if max+1 > first {
+                                return Err(DekuError::InvalidParam(Cow::from(format!(
+                                    "bit size {} of input is larger than bit requested size {}",
+                                    MAX_TYPE_BITS - first,
+                                    bit_size,
+                                ))));
+                            }
+                        }
+                    }
+                    else {
+                        if let Some(first) = input_bits.first_zero() {
+                            let max = (MAX_TYPE_BITS - bit_size);
+                            if max+1 > first {
+                                return Err(DekuError::InvalidParam(Cow::from(format!(
+                                    "bit size {} of input is larger than bit requested size {}",
+                                    MAX_TYPE_BITS - first,
+                                    bit_size,
+                                ))));
+                            }
+                        }
+                    }
+                    // Example read 10 bits u32 [0xAB, 0b11_000000]
+                    // => [00000000, 00000000, 00000010, 10101111]
+                    writer.write_bits(&input_bits[input_bits.len() - bit_size..])?;
+                }
                 Ok(())
             }
         }
-
-        /// When using Endian and ByteSize, Order is not used
-        impl DekuWriter<(Endian, ByteSize, Order)> for $typ {
-            #[inline]
-            fn to_writer<W: Write + Seek>(
-                &self,
-                writer: &mut Writer<W>,
-                (endian, size, _order): (Endian, ByteSize, Order),
-            ) -> Result<(), DekuError> {
-                <$typ>::to_writer(self, writer, (endian, size))
-            }
-        }
     };
 }
 
@@ -1026,17 +1133,17 @@ macro_rules! ForwardDekuWrite {
         }
     };
 }
-macro_rules! ImplDekuTraitsBytes {
+macro_rules! ImplDekuTraitsBytesUnsigned {
     ($typ:ty) => {
         ImplDekuReadBytes!($typ, $typ);
-        ImplDekuWrite!($typ);
+        ImplDekuWrite!($typ, Unsigned);
     };
     ($typ:ty, $inner:ty) => {
         ImplDekuReadBytes!($typ, $inner);
     };
 }
 
-macro_rules! ImplDekuTraits {
+macro_rules! ImplDekuTraitsUnsigned {
     ($typ:ty) => {
         ImplDekuReadBits!($typ, $typ);
         ForwardDekuRead!($typ);
@@ -1048,46 +1155,46 @@ macro_rules! ImplDekuTraits {
         ImplDekuReadBits!($typ, $inner);
         ForwardDekuRead!($typ);
 
-        ImplDekuWrite!($typ);
+        ImplDekuWrite!($typ, Unsigned);
         ImplDekuWriteOnlyEndian!($typ);
         ForwardDekuWrite!($typ);
     };
 }
 
-macro_rules! ImplDekuTraitsSignExtend {
+macro_rules! ImplDekuTraitsSigned {
     ($typ:ty, $inner:ty) => {
         ImplDekuReadSignExtend!($typ, $inner);
         ForwardDekuRead!($typ);
 
-        ImplDekuWrite!($typ);
+        ImplDekuWrite!($typ, Signed);
         ImplDekuWriteOnlyEndian!($typ);
         ForwardDekuWrite!($typ);
     };
 }
 
-ImplDekuTraits!(u8);
-ImplDekuTraits!(u16);
-ImplDekuTraitsBytes!(u16);
-ImplDekuTraits!(u32);
-ImplDekuTraitsBytes!(u32);
-ImplDekuTraits!(u64);
-ImplDekuTraitsBytes!(u64);
-ImplDekuTraits!(u128);
-ImplDekuTraitsBytes!(u128);
-ImplDekuTraits!(usize);
-ImplDekuTraitsBytes!(usize);
-
-ImplDekuTraitsSignExtend!(i8, u8);
-ImplDekuTraitsSignExtend!(i16, u16);
-ImplDekuTraitsSignExtend!(i32, u32);
-ImplDekuTraitsSignExtend!(i64, u64);
-ImplDekuTraitsSignExtend!(i128, u128);
-ImplDekuTraitsSignExtend!(isize, usize);
-
-ImplDekuTraits!(f32, u32);
-ImplDekuTraitsBytes!(f32, u32);
-ImplDekuTraits!(f64, u64);
-ImplDekuTraitsBytes!(f64, u64);
+ImplDekuTraitsUnsigned!(u8);
+ImplDekuTraitsUnsigned!(u16);
+ImplDekuTraitsBytesUnsigned!(u16);
+ImplDekuTraitsUnsigned!(u32);
+ImplDekuTraitsBytesUnsigned!(u32);
+ImplDekuTraitsUnsigned!(u64);
+ImplDekuTraitsBytesUnsigned!(u64);
+ImplDekuTraitsUnsigned!(u128);
+ImplDekuTraitsBytesUnsigned!(u128);
+ImplDekuTraitsUnsigned!(usize);
+ImplDekuTraitsBytesUnsigned!(usize);
+
+ImplDekuTraitsSigned!(i8, u8);
+ImplDekuTraitsSigned!(i16, u16);
+ImplDekuTraitsSigned!(i32, u32);
+ImplDekuTraitsSigned!(i64, u64);
+ImplDekuTraitsSigned!(i128, u128);
+ImplDekuTraitsSigned!(isize, usize);
+
+ImplDekuTraitsUnsigned!(f32, u32);
+ImplDekuTraitsBytesUnsigned!(f32, u32);
+ImplDekuTraitsUnsigned!(f64, u64);
+ImplDekuTraitsBytesUnsigned!(f64, u64);
 
 #[cfg(test)]
 mod tests {


Test patch:
diff --git a/tests/test_attributes/mod.rs b/tests/test_attributes/mod.rs
index 918967f..af6aa5d 100644
--- a/tests/test_attributes/mod.rs
+++ b/tests/test_attributes/mod.rs
@@ -1,5 +1,7 @@
 mod test_assert;
 mod test_assert_eq;
+#[cfg(feature = "bits")]
+mod test_bitfield_values_range_check;
 mod test_cond;
 mod test_ctx;
 mod test_limits;
diff --git a/tests/test_attributes/test_bitfield_values_range_check.rs b/tests/test_attributes/test_bitfield_values_range_check.rs
new file mode 100644
index 0000000..f9f6d63
--- /dev/null
+++ b/tests/test_attributes/test_bitfield_values_range_check.rs
@@ -0,0 +1,534 @@
+use std::io::Cursor;
+
+use deku::prelude::*;
+
+#[test]
+fn check_big_unsigned_u10_decode_encode_positive_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "5", bits = "10", endian = "big")]
+        pub b: u16,
+    }
+
+    let buffer = vec![0b10000000, 0b00000010];
+    //                                 ^^    ^^^^^^^^10 bits
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = 1023;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = 1024;
+    assert!(test_struct.to_bytes().is_err());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_little_unsigned_u10_decode_encode_positive_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "5", bits = "10", endian = "little")]
+        pub b: u16,
+    }
+
+    let buffer = vec![0b10000000, 0b00001000];
+    //                                 ^^    ^^^^^^^^10 bits
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = 1023;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = 1024;
+    assert!(test_struct.to_bytes().is_err());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_big_signed_i10_decode_encode_positive_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "5", bits = "10", endian = "big")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b10000000, 0b00000010];
+    //                                 ^^    ^^^^^^^^10 bits
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = 511;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = 512;
+    assert!(test_struct.to_bytes().is_err());
+
+    let mut cursor = Cursor::new(buffer);
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+}
+
+#[test]
+fn check_little_signed_i10_decode_encode_positive_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "5", bits = "10", endian = "little")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b10000000, 0b00001000];
+    //                                 ^^    ^^^^^^^^10 bits
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = 511;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = 512;
+    assert!(test_struct.to_bytes().is_err());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_big_signed_i10_decode_encode_negative_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "5", bits = "10", endian = "big")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b10000011, 0b11111110];
+    //                                 ^^    ^^^^^^^^10 bits
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, -2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = -512;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = -513;
+    assert!(test_struct.to_bytes().is_err());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, -2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_little_signed_i10_decode_encode_negative_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "5", bits = "10", endian = "little")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b10000011, 0b11111011];
+    //                                 ^^    ^^^^^^^^10 bits
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, -2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = -512;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = -513;
+    assert!(test_struct.to_bytes().is_err());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, -2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_big_unsigned_u16_decode_encode_positive_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "15", bits = "16", endian = "big")]
+        pub b: u16,
+    }
+
+    let buffer = vec![0b10000000, 0b00000000, 0b00000000, 0b00000010];
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = 0xFFFF;
+    assert!(test_struct.to_bytes().is_ok());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_little_unsigned_u16_decode_encode_positive_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "15", bits = "16", endian = "little")]
+        pub b: u16,
+    }
+
+    let buffer = vec![0b10000000, 0b00000000, 0b00000010, 0b00000000];
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = 0xFFFF;
+    assert!(test_struct.to_bytes().is_ok());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+}
+
+#[test]
+fn check_big_signed_i16_decode_encode_positive_negative_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(pad_bits_before = "15", bits = "16", endian = "big", bit_order = "msb")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b10000000, 0b00000000, 0b00000000, 0b00000010];
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = i16::MIN;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = i16::MAX;
+    assert!(test_struct.to_bytes().is_ok());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+
+    let mut cursor = Cursor::new(&buffer[0..1]);
+    assert!(TestStruct::from_reader((&mut cursor, 0)).is_err());
+}
+
+#[test]
+fn check_little_signed_i16_decode_encode_positive_negative_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(bits = "1")]
+        pub a: bool,
+        #[deku(
+            pad_bits_before = "15",
+            bits = "16",
+            endian = "little",
+            bit_order = "msb"
+        )]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b10000000, 0b00000000, 0b00000010, 0b00000000];
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert!(test_struct.a);
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = i16::MIN;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = i16::MAX;
+    assert!(test_struct.to_bytes().is_ok());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert!(val.a);
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+
+    let mut cursor = Cursor::new(&buffer[0..1]);
+    assert!(TestStruct::from_reader((&mut cursor, 0)).is_err());
+}
+
+#[test]
+fn check_non_bitfield_big_signed_i16_decode_encode_positive_negative_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(endian = "big", bit_order = "msb")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b00000000, 0b00000010];
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = i16::MIN;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = i16::MAX;
+    assert!(test_struct.to_bytes().is_ok());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+
+    let mut cursor = Cursor::new(&buffer[0..1]);
+    assert!(TestStruct::from_reader((&mut cursor, 0)).is_err());
+}
+
+#[test]
+fn check_non_bitfield_little_signed_i16_decode_encode_positive_negative_value() {
+    #[derive(Debug, PartialEq, Default, Clone, DekuRead, DekuWrite)]
+    pub struct TestStruct {
+        #[deku(endian = "little", bit_order = "msb")]
+        pub b: i16,
+    }
+
+    let buffer = vec![0b00000010, 0b00000000];
+
+    let ((remaining_bytes, offset), mut test_struct) =
+        TestStruct::from_bytes((&buffer, 0)).expect("decoder error");
+
+    // everything consumed?
+    assert_eq!(offset, 0);
+    assert_eq!(remaining_bytes.len(), 0);
+
+    // check content
+    assert_eq!(test_struct.b, 2);
+
+    // write back and check
+    assert_eq!(buffer, test_struct.to_bytes().expect("encode error"));
+
+    test_struct.b = i16::MIN;
+    assert!(test_struct.to_bytes().is_ok());
+
+    test_struct.b = i16::MAX;
+    assert!(test_struct.to_bytes().is_ok());
+
+    let mut cursor = Cursor::new(buffer.clone());
+    let (_, val) = TestStruct::from_reader((&mut cursor, 0)).unwrap();
+    assert_eq!(val.b, 2);
+
+    let mut buffer2 = vec![];
+    let mut cursor2 = Cursor::new(&mut buffer2);
+    let mut writer = Writer::new(&mut cursor2);
+    val.to_writer(&mut writer, ()).unwrap();
+    assert_eq!(buffer2, buffer);
+
+    let mut cursor = Cursor::new(&buffer[0..1]);
+    assert!(TestStruct::from_reader((&mut cursor, 0)).is_err());
+}