LCOV - code coverage report
Current view: top level - pageserver/src/virtual_file/owned_buffers_io - slice.rs (source / functions) Coverage Total Hit
Test: 5fe7fa8d483b39476409aee736d6d5e32728bfac.info Lines: 97.0 % 67 65
Test Date: 2025-03-12 16:10:49 Functions: 100.0 % 5 5

            Line data    Source code
       1              : use tokio_epoll_uring::{BoundedBuf, BoundedBufMut, IoBufMut, Slice};
       2              : 
       3              : pub(crate) trait SliceMutExt {
       4              :     /// Get a `&mut[0..self.bytes_total()`] slice, for when you need to do borrow-based IO.
       5              :     ///
       6              :     /// See the test case `test_slice_full_zeroed` for the difference to just doing `&slice[..]`
       7              :     fn as_mut_rust_slice_full_zeroed(&mut self) -> &mut [u8];
       8              : }
       9              : 
      10              : impl<B> SliceMutExt for Slice<B>
      11              : where
      12              :     B: IoBufMut,
      13              : {
      14              :     #[inline(always)]
      15      1465253 :     fn as_mut_rust_slice_full_zeroed(&mut self) -> &mut [u8] {
      16      1465253 :         // zero-initialize the uninitialized parts of the buffer so we can create a Rust slice
      17      1465253 :         //
      18      1465253 :         // SAFETY: we own `slice`, don't write outside the bounds
      19      1465253 :         unsafe {
      20      1465253 :             let to_init = self.bytes_total() - self.bytes_init();
      21      1465253 :             self.stable_mut_ptr()
      22      1465253 :                 .add(self.bytes_init())
      23      1465253 :                 .write_bytes(0, to_init);
      24      1465253 :             self.set_init(self.bytes_total());
      25      1465253 :         };
      26      1465253 :         let bytes_total = self.bytes_total();
      27      1465253 :         &mut self[0..bytes_total]
      28      1465253 :     }
      29              : }
      30              : 
      31              : #[cfg(test)]
      32              : mod tests {
      33              :     use std::io::Read;
      34              : 
      35              :     use bytes::Buf;
      36              :     use tokio_epoll_uring::Slice;
      37              : 
      38              :     use super::*;
      39              : 
      40              :     #[test]
      41            4 :     fn test_slice_full_zeroed() {
      42           12 :         let make_fake_file = || bytes::BytesMut::from(&b"12345"[..]).reader();
      43              : 
      44              :         // before we start the test, let's make sure we have a shared understanding of what slice_full does
      45              :         {
      46            4 :             let buf = Vec::with_capacity(3);
      47            4 :             let slice: Slice<_> = buf.slice_full();
      48            4 :             assert_eq!(slice.bytes_init(), 0);
      49            4 :             assert_eq!(slice.bytes_total(), 3);
      50            4 :             let rust_slice = &slice[..];
      51            4 :             assert_eq!(
      52            4 :                 rust_slice.len(),
      53              :                 0,
      54            0 :                 "Slice only derefs to a &[u8] of the initialized part"
      55              :             );
      56              :         }
      57              : 
      58              :         // and also let's establish a shared understanding of .slice()
      59              :         {
      60            4 :             let buf = Vec::with_capacity(3);
      61            4 :             let slice: Slice<_> = buf.slice(0..2);
      62            4 :             assert_eq!(slice.bytes_init(), 0);
      63            4 :             assert_eq!(slice.bytes_total(), 2);
      64            4 :             let rust_slice = &slice[..];
      65            4 :             assert_eq!(
      66            4 :                 rust_slice.len(),
      67              :                 0,
      68            0 :                 "Slice only derefs to a &[u8] of the initialized part"
      69              :             );
      70              :         }
      71              : 
      72              :         // the above leads to the easy mistake of using slice[..] for borrow-based IO like so:
      73              :         {
      74            4 :             let buf = Vec::with_capacity(3);
      75            4 :             let mut slice: Slice<_> = buf.slice_full();
      76            4 :             assert_eq!(slice[..].len(), 0);
      77            4 :             let mut file = make_fake_file();
      78            4 :             file.read_exact(&mut slice[..]).unwrap(); // one might think this reads 3 bytes but it reads 0
      79            4 :             assert_eq!(&slice[..] as &[u8], &[][..] as &[u8]);
      80              :         }
      81              : 
      82              :         // With owned buffers IO like with VirtualFilem, you could totally
      83              :         // pass in a `Slice` with bytes_init()=0 but bytes_total()=5
      84              :         // and it will read 5 bytes into the slice, and return a slice that has bytes_init()=5.
      85            4 :         {
      86            4 :             // TODO: demo
      87            4 :         }
      88            4 : 
      89            4 :         //
      90            4 :         // Ok, now that we have a shared understanding let's demo how to use the extension trait.
      91            4 :         //
      92            4 : 
      93            4 :         // slice_full()
      94            4 :         {
      95            4 :             let buf = Vec::with_capacity(3);
      96            4 :             let mut slice: Slice<_> = buf.slice_full();
      97            4 :             let rust_slice = slice.as_mut_rust_slice_full_zeroed();
      98            4 :             assert_eq!(rust_slice.len(), 3);
      99            4 :             assert_eq!(rust_slice, &[0, 0, 0]);
     100            4 :             let mut file = make_fake_file();
     101            4 :             file.read_exact(rust_slice).unwrap();
     102            4 :             assert_eq!(rust_slice, b"123");
     103            4 :             assert_eq!(&slice[..], b"123");
     104              :         }
     105              : 
     106              :         // .slice(..)
     107              :         {
     108            4 :             let buf = Vec::with_capacity(3);
     109            4 :             let mut slice: Slice<_> = buf.slice(0..2);
     110            4 :             let rust_slice = slice.as_mut_rust_slice_full_zeroed();
     111            4 :             assert_eq!(rust_slice.len(), 2);
     112            4 :             assert_eq!(rust_slice, &[0, 0]);
     113            4 :             let mut file = make_fake_file();
     114            4 :             file.read_exact(rust_slice).unwrap();
     115            4 :             assert_eq!(rust_slice, b"12");
     116            4 :             assert_eq!(&slice[..], b"12");
     117              :         }
     118            4 :     }
     119              : }
        

Generated by: LCOV version 2.1-beta