Skip to content

Commit 2b47fd2

Browse files
committed
Prototype a new Buffer trait.
I'm experimenting with a `Buffer` trait similar to #908, however I've run into a few problems. See the questions in examples/new_read.rs for details.
1 parent f377e85 commit 2b47fd2

File tree

6 files changed

+178
-31
lines changed

6 files changed

+178
-31
lines changed

Diff for: examples/new_read.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use rustix::io::read;
2+
use rustix::stdio::stdin;
3+
4+
fn main() {
5+
let buf = Vec::new();
6+
let _x: Vec<u8> = read(stdin(), buf).unwrap();
7+
8+
let mut buf = Vec::new();
9+
let _x: usize = read(stdin(), &mut buf).unwrap();
10+
11+
let mut buf = [0, 0, 0];
12+
let _x: usize = read(stdin(), &mut buf).unwrap();
13+
14+
// Why doesn't this work? This is reduced from src/fs/inotify.rs line 177.
15+
struct Wrapper<'a>(&'a mut [u8]);
16+
impl<'a> Wrapper<'a> {
17+
fn read(&mut self) {
18+
let _x: usize = read(stdin(), self.0).unwrap();
19+
}
20+
}
21+
let mut buf = Vec::new();
22+
let mut wrapper = Wrapper(&mut buf);
23+
wrapper.read();
24+
25+
// Why does this get two error messages?
26+
let mut buf = [0, 0, 0];
27+
let _x = read(stdin(), buf).unwrap();
28+
}

Diff for: src/backend/libc/io/syscalls.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::io::ReadWriteFlags;
1818
use crate::io::{self, FdFlags};
1919
use crate::ioctl::{IoctlOutput, RawOpcode};
2020
use core::cmp::min;
21+
use core::mem::MaybeUninit;
2122
#[cfg(all(feature = "fs", feature = "net"))]
2223
use libc_errno::errno;
2324
#[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
@@ -26,8 +27,14 @@ use {
2627
crate::io::{IoSlice, IoSliceMut},
2728
};
2829

29-
pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> {
30-
ret_usize(c::read(borrowed_fd(fd), buf.cast(), min(len, READ_LIMIT)))
30+
pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
31+
unsafe {
32+
ret_usize(c::read(
33+
borrowed_fd(fd),
34+
buf.as_mut_ptr().cast(),
35+
min(buf.len(), READ_LIMIT),
36+
))
37+
}
3138
}
3239

3340
pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {

Diff for: src/backend/linux_raw/io/syscalls.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,19 @@ use crate::ioctl::{IoctlOutput, RawOpcode};
2626
#[cfg(all(feature = "fs", feature = "net"))]
2727
use crate::net::{RecvFlags, SendFlags};
2828
use core::cmp;
29+
use core::mem::MaybeUninit;
2930
use linux_raw_sys::general::{F_DUPFD_CLOEXEC, F_GETFD, F_SETFD};
3031

3132
#[inline]
32-
pub(crate) unsafe fn read(fd: BorrowedFd<'_>, buf: *mut u8, len: usize) -> io::Result<usize> {
33-
ret_usize(syscall!(__NR_read, fd, buf, pass_usize(len)))
33+
pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
34+
unsafe {
35+
ret_usize(syscall!(
36+
__NR_read,
37+
fd,
38+
buf.as_mut_ptr(),
39+
pass_usize(buf.len())
40+
))
41+
}
3442
}
3543

3644
#[inline]

Diff for: src/buffer.rs

+119
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,128 @@
22
33
#![allow(unsafe_code)]
44

5+
#[cfg(feature = "alloc")]
6+
use alloc::vec::Vec;
57
use core::mem::MaybeUninit;
68
use core::slice;
79

10+
/// A memory buffer that may be uninitialized.
11+
pub trait Buffer<T> {
12+
/// The result of the process operation.
13+
type Result;
14+
15+
/// Convert this buffer into a maybe-unitiailized view.
16+
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>];
17+
18+
/// Convert a finished buffer pointer into its result.
19+
///
20+
/// # Safety
21+
///
22+
/// At least `len` bytes of the buffer must now be initialized.
23+
unsafe fn finish(self, len: usize) -> Self::Result;
24+
}
25+
26+
/// Implements [`Buffer`] around the a slice of bytes.
27+
///
28+
/// `Result` is a `usize` indicating how many bytes were written.
29+
impl<T> Buffer<T> for &mut [T] {
30+
type Result = usize;
31+
32+
#[inline]
33+
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
34+
// SAFETY: This just casts away the knowledge that the elements are
35+
// initialized.
36+
unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit<T>]>(self) }
37+
}
38+
39+
#[inline]
40+
unsafe fn finish(self, len: usize) -> Self::Result {
41+
len
42+
}
43+
}
44+
45+
/// Implements [`Buffer`] around the a slice of bytes.
46+
///
47+
/// `Result` is a `usize` indicating how many bytes were written.
48+
impl<T, const N: usize> Buffer<T> for &mut [T; N] {
49+
type Result = usize;
50+
51+
#[inline]
52+
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
53+
// SAFETY: This just casts away the knowledge that the elements are
54+
// initialized.
55+
unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit<T>]>(*self) }
56+
}
57+
58+
#[inline]
59+
unsafe fn finish(self, len: usize) -> Self::Result {
60+
len
61+
}
62+
}
63+
64+
/// Implements [`Buffer`] around the a slice of bytes.
65+
///
66+
/// `Result` is a `usize` indicating how many bytes were written.
67+
impl<T> Buffer<T> for &mut Vec<T> {
68+
type Result = usize;
69+
70+
#[inline]
71+
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
72+
// SAFETY: This just casts away the knowledge that the elements are
73+
// initialized.
74+
unsafe { core::mem::transmute::<&mut [T], &mut [MaybeUninit<T>]>(self) }
75+
}
76+
77+
#[inline]
78+
unsafe fn finish(self, len: usize) -> Self::Result {
79+
len
80+
}
81+
}
82+
83+
/// Implements [`Buffer`] around the a slice of uninitialized bytes.
84+
///
85+
/// `Result` is a pair of slices giving the initialized and uninitialized
86+
/// subslices after the new data is written.
87+
impl<'a, T> Buffer<T> for &'a mut [MaybeUninit<T>] {
88+
type Result = (&'a mut [T], &'a mut [MaybeUninit<T>]);
89+
90+
#[inline]
91+
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
92+
self
93+
}
94+
95+
#[inline]
96+
unsafe fn finish(self, len: usize) -> Self::Result {
97+
let (init, uninit) = self.split_at_mut(len);
98+
99+
// SAFETY: The user asserts that the slice is now initialized.
100+
let init = slice::from_raw_parts_mut(init.as_mut_ptr().cast::<T>(), init.len());
101+
102+
(init, uninit)
103+
}
104+
}
105+
106+
/// Implements [`Buffer`] around the `Vec` type.
107+
///
108+
/// This implementation fills the buffer, overwriting any previous data, with
109+
/// the new data data and sets the length.
110+
#[cfg(feature = "alloc")]
111+
impl<T> Buffer<T> for Vec<T> {
112+
type Result = Vec<T>;
113+
114+
#[inline]
115+
fn as_maybe_uninitialized(&mut self) -> &mut [MaybeUninit<T>] {
116+
self.clear();
117+
self.spare_capacity_mut()
118+
}
119+
120+
#[inline]
121+
unsafe fn finish(mut self, len: usize) -> Self::Result {
122+
self.set_len(len);
123+
self
124+
}
125+
}
126+
8127
/// Split an uninitialized byte slice into initialized and uninitialized parts.
9128
///
10129
/// # Safety

Diff for: src/fs/inotify.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@
4040
//! # }
4141
4242
#![allow(unused_qualifications)]
43+
#![allow(dead_code)] // FIXME
44+
#![allow(unused_imports)] // FIXME
4345

4446
use super::inotify;
4547
pub use crate::backend::fs::inotify::{CreateFlags, ReadFlags, WatchFlags};
4648
use crate::backend::fs::syscalls;
4749
use crate::fd::{AsFd, OwnedFd};
4850
use crate::ffi::CStr;
4951
use crate::io;
50-
use crate::io::{read_uninit, Errno};
52+
use crate::io::{read, Errno};
5153
use core::mem::{align_of, size_of, MaybeUninit};
5254
use linux_raw_sys::general::inotify_event;
5355

@@ -174,14 +176,17 @@ impl<'buf, Fd: AsFd> Reader<'buf, Fd> {
174176
#[allow(clippy::should_implement_trait)]
175177
pub fn next(&mut self) -> io::Result<InotifyEvent<'_>> {
176178
if self.is_buffer_empty() {
177-
match read_uninit(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) {
179+
todo!("FIXME: see \"Why doesn't this work?\" in examples/new_read.rs");
180+
/*
181+
match read(self.fd.as_fd(), self.buf).map(|(init, _)| init.len()) {
178182
Ok(0) => return Err(Errno::INVAL),
179183
Ok(bytes_read) => {
180184
self.initialized = bytes_read;
181185
self.offset = 0;
182186
}
183187
Err(e) => return Err(e),
184188
}
189+
*/
185190
}
186191

187192
let ptr = self.buf[self.offset..].as_ptr();

Diff for: src/io/read_write.rs

+5-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![allow(unsafe_code)]
44

5-
use crate::buffer::split_init;
5+
use crate::buffer::{split_init, Buffer};
66
use crate::{backend, io};
77
use backend::fd::AsFd;
88
use core::mem::MaybeUninit;
@@ -16,9 +16,6 @@ pub use backend::io::types::ReadWriteFlags;
1616

1717
/// `read(fd, buf)`—Reads from a stream.
1818
///
19-
/// This takes a `&mut [u8]` which Rust requires to contain initialized memory.
20-
/// To use an uninitialized buffer, use [`read_uninit`].
21-
///
2219
/// # References
2320
/// - [POSIX]
2421
/// - [Linux]
@@ -40,27 +37,10 @@ pub use backend::io::types::ReadWriteFlags;
4037
/// [illumos]: https://illumos.org/man/2/read
4138
/// [glibc]: https://sourceware.org/glibc/manual/latest/html_node/I_002fO-Primitives.html#index-reading-from-a-file-descriptor
4239
#[inline]
43-
pub fn read<Fd: AsFd>(fd: Fd, buf: &mut [u8]) -> io::Result<usize> {
44-
unsafe { backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr(), buf.len()) }
45-
}
46-
47-
/// `read(fd, buf)`—Reads from a stream.
48-
///
49-
/// This is equivalent to [`read`], except that it can read into uninitialized
50-
/// memory. It returns the slice that was initialized by this function and the
51-
/// slice that remains uninitialized.
52-
#[inline]
53-
pub fn read_uninit<Fd: AsFd>(
54-
fd: Fd,
55-
buf: &mut [MaybeUninit<u8>],
56-
) -> io::Result<(&mut [u8], &mut [MaybeUninit<u8>])> {
57-
// Get number of initialized bytes.
58-
let length = unsafe {
59-
backend::io::syscalls::read(fd.as_fd(), buf.as_mut_ptr().cast::<u8>(), buf.len())
60-
};
61-
62-
// Split into the initialized and uninitialized portions.
63-
Ok(unsafe { split_init(buf, length?) })
40+
pub fn read<Fd: AsFd, Buf: Buffer<u8>>(fd: Fd, mut buf: Buf) -> io::Result<Buf::Result> {
41+
let len = backend::io::syscalls::read(fd.as_fd(), buf.as_maybe_uninitialized())?;
42+
// SAFETY: `read` works.
43+
unsafe { Ok(buf.finish(len)) }
6444
}
6545

6646
/// `write(fd, buf)`—Writes to a stream.

0 commit comments

Comments
 (0)