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 4615462 : fn as_mut_rust_slice_full_zeroed(&mut self) -> &mut [u8] {
16 4615462 : // zero-initialize the uninitialized parts of the buffer so we can create a Rust slice
17 4615462 : //
18 4615462 : // SAFETY: we own `slice`, don't write outside the bounds
19 4615462 : unsafe {
20 4615462 : let to_init = self.bytes_total() - self.bytes_init();
21 4615462 : self.stable_mut_ptr()
22 4615462 : .add(self.bytes_init())
23 4615462 : .write_bytes(0, to_init);
24 4615462 : self.set_init(self.bytes_total());
25 4615462 : };
26 4615462 : let bytes_total = self.bytes_total();
27 4615462 : &mut self[0..bytes_total]
28 4615462 : }
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 12 : fn test_slice_full_zeroed() {
42 36 : 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 12 : let buf = Vec::with_capacity(3);
47 12 : let slice: Slice<_> = buf.slice_full();
48 12 : assert_eq!(slice.bytes_init(), 0);
49 12 : assert_eq!(slice.bytes_total(), 3);
50 12 : let rust_slice = &slice[..];
51 12 : assert_eq!(
52 12 : 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 12 : let buf = Vec::with_capacity(3);
61 12 : let slice: Slice<_> = buf.slice(0..2);
62 12 : assert_eq!(slice.bytes_init(), 0);
63 12 : assert_eq!(slice.bytes_total(), 2);
64 12 : let rust_slice = &slice[..];
65 12 : assert_eq!(
66 12 : 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 12 : let buf = Vec::with_capacity(3);
75 12 : let mut slice: Slice<_> = buf.slice_full();
76 12 : assert_eq!(slice[..].len(), 0);
77 12 : let mut file = make_fake_file();
78 12 : file.read_exact(&mut slice[..]).unwrap(); // one might think this reads 3 bytes but it reads 0
79 12 : 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 12 : {
86 12 : // TODO: demo
87 12 : }
88 12 :
89 12 : //
90 12 : // Ok, now that we have a shared understanding let's demo how to use the extension trait.
91 12 : //
92 12 :
93 12 : // slice_full()
94 12 : {
95 12 : let buf = Vec::with_capacity(3);
96 12 : let mut slice: Slice<_> = buf.slice_full();
97 12 : let rust_slice = slice.as_mut_rust_slice_full_zeroed();
98 12 : assert_eq!(rust_slice.len(), 3);
99 12 : assert_eq!(rust_slice, &[0, 0, 0]);
100 12 : let mut file = make_fake_file();
101 12 : file.read_exact(rust_slice).unwrap();
102 12 : assert_eq!(rust_slice, b"123");
103 12 : assert_eq!(&slice[..], b"123");
104 : }
105 :
106 : // .slice(..)
107 : {
108 12 : let buf = Vec::with_capacity(3);
109 12 : let mut slice: Slice<_> = buf.slice(0..2);
110 12 : let rust_slice = slice.as_mut_rust_slice_full_zeroed();
111 12 : assert_eq!(rust_slice.len(), 2);
112 12 : assert_eq!(rust_slice, &[0, 0]);
113 12 : let mut file = make_fake_file();
114 12 : file.read_exact(rust_slice).unwrap();
115 12 : assert_eq!(rust_slice, b"12");
116 12 : assert_eq!(&slice[..], b"12");
117 : }
118 12 : }
119 : }
|