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 : }
|