Skip to content

Commit 638ce93

Browse files
authored
io: add read_exact_arc to safely read a new uninitialized Arc (#7165)
1 parent c853991 commit 638ce93

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

tokio-util/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ __docs_rs = ["futures-util"]
3535

3636
[dependencies]
3737
tokio = { version = "1.28.0", path = "../tokio", features = ["sync"] }
38-
bytes = "1.2.1"
38+
bytes = "1.5.0"
3939
futures-core = "0.3.0"
4040
futures-sink = "0.3.0"
4141
futures-io = { version = "0.3.0", optional = true }

tokio-util/src/io/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ mod sink_writer;
1818
mod stream_reader;
1919

2020
cfg_io_util! {
21+
mod read_arc;
22+
pub use self::read_arc::read_exact_arc;
23+
2124
mod sync_bridge;
2225
pub use self::sync_bridge::SyncIoBridge;
2326
}

tokio-util/src/io/read_arc.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use std::io;
2+
use std::mem::MaybeUninit;
3+
use std::sync::Arc;
4+
use tokio::io::{AsyncRead, AsyncReadExt};
5+
6+
/// Read data from an `AsyncRead` into an `Arc`.
7+
///
8+
/// This uses `Arc::new_uninit_slice` and reads into the resulting uninitialized `Arc`.
9+
///
10+
/// # Example
11+
///
12+
/// ```
13+
/// # #[tokio::main]
14+
/// # async fn main() -> std::io::Result<()> {
15+
/// use tokio_util::io::read_exact_arc;
16+
///
17+
/// let read = tokio::io::repeat(42);
18+
///
19+
/// let arc = read_exact_arc(read, 4).await?;
20+
///
21+
/// assert_eq!(&arc[..], &[42; 4]);
22+
/// # Ok(())
23+
/// # }
24+
/// ```
25+
pub async fn read_exact_arc<R: AsyncRead>(read: R, len: usize) -> io::Result<Arc<[u8]>> {
26+
tokio::pin!(read);
27+
// TODO(MSRV 1.82): When bumping MSRV, switch to `Arc::new_uninit_slice(len)`. The following is
28+
// equivalent, and generates the same assembly, but works without requiring MSRV 1.82.
29+
let arc: Arc<[MaybeUninit<u8>]> = (0..len).map(|_| MaybeUninit::uninit()).collect();
30+
// TODO(MSRV future): Use `Arc::get_mut_unchecked` once it's stabilized.
31+
// SAFETY: We're the only owner of the `Arc`, and we keep the `Arc` valid throughout this loop
32+
// as we write through this reference.
33+
let mut buf = unsafe { &mut *(Arc::as_ptr(&arc) as *mut [MaybeUninit<u8>]) };
34+
while !buf.is_empty() {
35+
if read.read_buf(&mut buf).await? == 0 {
36+
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "early eof"));
37+
}
38+
}
39+
// TODO(MSRV 1.82): When bumping MSRV, switch to `arc.assume_init()`. The following is
40+
// equivalent, and generates the same assembly, but works without requiring MSRV 1.82.
41+
// SAFETY: This changes `[MaybeUninit<u8>]` to `[u8]`, and we've initialized all the bytes in
42+
// the loop above.
43+
Ok(unsafe { Arc::from_raw(Arc::into_raw(arc) as *const [u8]) })
44+
}

0 commit comments

Comments
 (0)