Repository: sharksforarms/deku
Pull request #497 (2024-10-31 22:25:10)

Add ByteSize impl for CString
* Use the new dynamic bytes attribute to apply to CString, which in many protocols you already have the size of the CString, so might as well use it

github-actions[bot]: ## Benchmark for e130d8d
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| count_from_u8_specialize | **98.7±0.63ns** | 100.1±5.48ns | **+1.42%** |
| count_no_specialize | **2.4±0.02µs** | 2.6±0.02µs | **+8.33%** |
| count_specialize | **100.0±0.67ns** | 100.9±1.74ns | **+0.90%** |
| deku_read_bits | 1175.5±21.31ns | **1127.4±13.80ns** | **-4.09%** |
| deku_read_byte | 4.9±0.05ns | **4.6±0.05ns** | **-6.12%** |
| deku_read_enum | 4.1±0.06ns | **3.8±0.08ns** | **-7.32%** |
| deku_read_vec | **30.2±0.31ns** | 31.4±0.50ns | **+3.97%** |
| deku_write_bits | **189.8±6.24ns** | 198.8±8.03ns | **+4.74%** |
| deku_write_byte | 25.8±0.38ns | 25.8±0.43ns | 0.00% |
| deku_write_enum | 23.9±0.39ns | 24.1±1.30ns | +0.84% |
| deku_write_vec | 437.2±5.52ns | 433.4±13.06ns | -0.87% |
| read_all | **4.3±0.07µs** | 5.0±0.04µs | **+16.28%** |
| read_all_bytes | 4.8±0.11µs | **4.3±0.04µs** | **-10.42%** |

  </details>
  
github-actions[bot]: ## Benchmark for c01eb5b
  <details>
    <summary>Click to view benchmark</summary>

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| count_from_u8_specialize | 115.2±1.16ns | **91.2±1.58ns** | **-20.83%** |
| count_no_specialize | **2.5±0.05µs** | 2.6±0.01µs | **+4.00%** |
| count_specialize | 102.5±1.26ns | **96.8±1.30ns** | **-5.56%** |
| deku_read_bits | **1163.9±9.14ns** | 1174.8±23.60ns | **+0.94%** |
| deku_read_byte | 4.5±0.04ns | 4.5±0.06ns | 0.00% |
| deku_read_enum | 3.8±0.07ns | 3.8±0.06ns | 0.00% |
| deku_read_vec | 47.1±0.89ns | **30.1±0.19ns** | **-36.09%** |
| deku_write_bits | **187.4±5.62ns** | 209.7±7.88ns | **+11.90%** |
| deku_write_byte | 26.1±0.43ns | 26.1±0.40ns | 0.00% |
| deku_write_enum | 23.9±0.41ns | 23.9±0.40ns | 0.00% |
| deku_write_vec | 449.4±7.82ns | **431.0±4.73ns** | **-4.09%** |
| read_all | 5.0±0.02µs | 5.0±0.03µs | 0.00% |
| read_all_bytes | 5.1±0.29µs | **4.2±0.03µs** | **-17.65%** |

  </details>
  

Patch:
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();


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());
+}