Repository: sharksforarms/deku
Pull request #430 (2024-04-20 22:31:42)

Remove one writer alloc
* Remove allocation and force bit.len() < MAX_BITS_AMT like reader does
* Speed up deku_write_bits by 20% on my machine

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | **1295.0±15.77ns** | 1312.4±11.43ns | **+1.34%** |
| deku_read_byte | 5.2±0.09ns | 5.2±0.22ns | 0.00% |
| deku_read_enum | 2.5±0.07ns | 2.5±0.07ns | 0.00% |
| deku_read_vec | 35.6±0.82ns | 35.4±0.32ns | -0.56% |
| deku_write_bits | 186.9±5.06ns | **157.3±3.62ns** | **-15.84%** |
| deku_write_byte | **22.3±0.42ns** | 22.7±0.36ns | **+1.79%** |
| deku_write_enum | **20.8±0.34ns** | 21.1±0.21ns | **+1.44%** |
| deku_write_vec | 325.0±2.49ns | **306.7±4.58ns** | **-5.63%** |

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

| Test | Base         | PR               | % |
|------|--------------|------------------|---|
| deku_read_bits | 1281.1±11.27ns | 1282.6±41.56ns | +0.12% |
| deku_read_byte | **5.2±0.04ns** | 15.9±0.02ns | **+205.77%** |
| deku_read_enum | **2.5±0.07ns** | 15.5±0.05ns | **+520.00%** |
| deku_read_vec | **35.3±0.44ns** | 44.6±1.82ns | **+26.35%** |
| deku_write_bits | 183.9±3.76ns | **156.5±3.05ns** | **-14.90%** |
| deku_write_byte | 22.4±0.42ns | 22.6±0.49ns | +0.89% |
| deku_write_enum | **20.8±0.29ns** | 21.6±2.21ns | **+3.85%** |
| deku_write_vec | **295.6±4.91ns** | 306.8±6.20ns | **+3.79%** |

  </details>
  
wcampbell0x2a: @sharksforarms another rebase!

Patch:
diff --git a/src/reader.rs b/src/reader.rs
index c70e0f3..0846802 100644
--- a/src/reader.rs
+++ b/src/reader.rs
@@ -19,6 +19,9 @@ pub enum ReaderRet {
     Bits(Option<BitVec<u8, Msb0>>),
 }
 
+/// Max bits requested from [`Reader::read_bits`] during one call
+pub const MAX_BITS_AMT: usize = 128;
+
 /// Reader to use with `from_reader_with_ctx`
 pub struct Reader<'a, R: Read> {
     inner: &'a mut R,
@@ -28,9 +31,6 @@ pub struct Reader<'a, R: Read> {
     pub bits_read: usize,
 }
 
-/// Max bits requested from [`Reader::read_bits`] during one call
-pub const MAX_BITS_AMT: usize = 128;
-
 impl<'a, R: Read> Reader<'a, R> {
     /// Create a new `Reader`
     #[inline]
diff --git a/src/writer.rs b/src/writer.rs
index 362a29e..7e02d69 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -13,6 +13,9 @@ const fn bits_of<T>() -> usize {
     core::mem::size_of::<T>().saturating_mul(<u8>::BITS as usize)
 }
 
+/// Max bits written to [`Reader::write_bits`] during one call
+pub const MAX_BITS_AMT: usize = 128;
+
 /// Container to use with `to_writer`
 pub struct Writer<W: Write> {
     pub(crate) inner: W,
@@ -43,6 +46,9 @@ impl<W: Write> Writer<W> {
     ///
     /// Any leftover bits will be written before `bits`, and non-written bits will
     /// be stored in `self.leftover`.
+    ///
+    /// # Params
+    /// `bits`    - Amount of bits that will be written. length must be <= [`MAX_BITS_AMT`].
     #[inline(never)]
     pub fn write_bits(&mut self, bits: &BitSlice<u8, Msb0>) -> Result<(), DekuError> {
         #[cfg(feature = "logging")]
@@ -66,7 +72,8 @@ impl<W: Write> Writer<W> {
         };
 
         // one shot impl of BitSlice::read(no read_exact), but for no_std
-        let mut buf = alloc::vec![0x00; bits.len() / 8];
+        let mut buf = [0; MAX_BITS_AMT];
+        let buf = &mut buf[..bits.len() / 8];
         let mut count = 0;
         bits.chunks_exact(bits_of::<u8>())
             .zip(buf.iter_mut())
@@ -81,7 +88,7 @@ impl<W: Write> Writer<W> {
         bits = unsafe { bits.get_unchecked(count * bits_of::<u8>()..) };
 
         self.leftover = bits.to_bitvec();
-        if self.inner.write_all(&buf).is_err() {
+        if self.inner.write_all(buf).is_err() {
             return Err(DekuError::Write);
         }
 


Test patch:
diff --git a/tests/test_alloc.rs b/tests/test_alloc.rs
index 0ac56ab..8da0463 100644
--- a/tests/test_alloc.rs
+++ b/tests/test_alloc.rs
@@ -63,4 +63,19 @@ mod tests {
             (5, 0, 5)
         );
     }
+
+    #[test]
+    #[cfg_attr(miri, ignore)]
+    fn test_simple_write() {
+        let input = hex!("aa_bbbb_cc_0102_dd_ffffff_aa_0100ff");
+        let t = TestDeku::from_reader((&mut input.as_slice(), 0)).unwrap().1;
+
+        assert_eq!(
+            count_alloc(|| {
+                t.to_bytes().unwrap();
+            })
+            .0,
+            (2, 1, 2)
+        );
+    }
 }