Skip to content

Commit 13a0cee

Browse files
committed
Introduced a specialization trait ReadDirFromPath for ReadDir struct on most platforms to construct a ReadDir with zero cost conversion if given a PathBuf or something that could be converted into a PathBuf
1 parent a5c825c commit 13a0cee

9 files changed

Lines changed: 207 additions & 35 deletions

File tree

library/std/src/sys/fs/hermit.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use alloc_crate::borrow::Cow;
2+
13
use crate::ffi::{CStr, OsStr, OsString, c_char};
24
use crate::fs::TryLockError;
35
use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
@@ -53,6 +55,37 @@ impl ReadDir {
5355
}
5456
}
5557

58+
/// Specialization trait used to construct a `ReadDir`
59+
trait ReadDirFromPath<P> {
60+
fn from_path(dir: Vec<u8>, path: P) -> Self;
61+
}
62+
63+
impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
64+
default fn from_path(dir: Vec<u8>, path: P) -> Self {
65+
let inner = InnerReadDir { root: path.as_ref().to_path_buf(), dir };
66+
ReadDir::new(inner)
67+
}
68+
}
69+
70+
/// This constructs a `ReadDir` for all types that can be converted
71+
/// into `PathBuf` without allocating
72+
macro_rules! impl_read_dir_from_path {
73+
($t:ty) => {
74+
impl ReadDirFromPath<$t> for ReadDir {
75+
fn from_path(dir: Vec<u8>, path: $t) -> Self {
76+
let inner = InnerReadDir::new(path.into(), dir);
77+
ReadDir::new(inner)
78+
}
79+
}
80+
};
81+
}
82+
83+
impl_read_dir_from_path!(PathBuf);
84+
impl_read_dir_from_path!(Box<Path>);
85+
impl_read_dir_from_path!(Cow<'_, Path>);
86+
impl_read_dir_from_path!(OsString);
87+
impl_read_dir_from_path!(String);
88+
5689
pub struct DirEntry {
5790
/// path to the entry
5891
root: PathBuf,
@@ -512,12 +545,11 @@ impl FromRawFd for File {
512545
}
513546
}
514547

515-
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
516-
let fd_raw = run_path_with_cstr(path, &|path| {
548+
pub fn readdir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
549+
let fd_raw = run_path_with_cstr(path.as_ref(), &|path| {
517550
cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) })
518551
})?;
519552
let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
520-
let root = path.to_path_buf();
521553

522554
// read all director entries
523555
let mut vec: Vec<u8> = Vec::new();
@@ -551,7 +583,7 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> {
551583
}
552584
}
553585

554-
Ok(ReadDir::new(InnerReadDir::new(root, vec)))
586+
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(vec, path))
555587
}
556588

557589
pub fn unlink(path: &Path) -> io::Result<()> {

library/std/src/sys/fs/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub use imp::{
6363
ReadDir,
6464
};
6565

66-
pub fn read_dir(path: &Path) -> io::Result<ReadDir> {
66+
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
6767
// FIXME: use with_native_path on all platforms
6868
imp::readdir(path)
6969
}

library/std/src/sys/fs/motor.rs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use alloc_crate::borrow::Cow;
2+
13
use crate::ffi::OsString;
24
use crate::hash::Hash;
35
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
@@ -377,12 +379,49 @@ impl Drop for ReadDir {
377379
}
378380
}
379381

380-
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
381-
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
382-
Ok(ReadDir {
383-
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
384-
path: path.to_owned(),
385-
})
382+
/// Specialization trait used to construct a `ReadDir`
383+
trait ReadDirFromPath<P> {
384+
fn from_path(path: P) -> io::Result<ReadDir>;
385+
}
386+
387+
impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
388+
default fn from_path(path: P) -> io::Result<ReadDir> {
389+
let path = path.as_ref().to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
390+
Ok(ReadDir {
391+
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
392+
path: path.to_owned(),
393+
})
394+
}
395+
}
396+
397+
/// This constructs a `ReadDir` for all types that can be converted
398+
/// into `String` without allocating
399+
macro_rules! impl_read_dir_from_path {
400+
($t:ty) => {
401+
impl ReadDirFromPath<$t> for ReadDir {
402+
fn from_path(path: $t) -> io::Result<ReadDir> {
403+
let path_buf: PathBuf = path.into();
404+
let path = path_buf.into_os_string().into_string();
405+
match path {
406+
Err(_) => return Err(io::Error::from(io::ErrorKind::InvalidFilename)),
407+
Ok(path) => Ok(ReadDir {
408+
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
409+
path,
410+
}),
411+
}
412+
}
413+
}
414+
};
415+
}
416+
417+
impl_read_dir_from_path!(PathBuf);
418+
impl_read_dir_from_path!(Box<Path>);
419+
impl_read_dir_from_path!(Cow<'_, Path>);
420+
impl_read_dir_from_path!(OsString);
421+
impl_read_dir_from_path!(String);
422+
423+
pub fn readdir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
424+
<ReadDir as ReadDirFromPath<P>>::from_path(path)
386425
}
387426

388427
impl Iterator for ReadDir {

library/std/src/sys/fs/solid.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![allow(dead_code)]
22

3+
use alloc_crate::borrow::Cow;
4+
35
use crate::ffi::{CStr, CString, OsStr, OsString};
46
use crate::fmt;
57
use crate::fs::TryLockError;
@@ -146,19 +148,49 @@ impl FileType {
146148
}
147149
}
148150

149-
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
151+
pub fn readdir<P: AsRef<Path>>(p: P) -> io::Result<ReadDir> {
150152
unsafe {
151153
let mut dir = MaybeUninit::uninit();
152154
error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
153-
cstr(p)?.as_ptr(),
155+
cstr(p.as_ref())?.as_ptr(),
154156
dir.as_mut_ptr(),
155157
))
156158
.map_err(|e| e.as_io_error())?;
157-
let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
158-
Ok(ReadDir { inner })
159+
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(dir.assume_init(), p))
160+
}
161+
}
162+
163+
/// Specialization trait used to construct a `ReadDir`
164+
trait ReadDirFromPath<P> {
165+
fn from_path(dirp: abi::S_DIR, path: P) -> Self;
166+
}
167+
168+
impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
169+
default fn from_path(dirp: abi::S_DIR, path: P) -> Self {
170+
let inner = InnerReadDir { dirp, root: path.as_ref().to_path_buf() };
171+
ReadDir { inner: Arc::new(inner) }
159172
}
160173
}
161174

175+
/// This constructs a `ReadDir` for all types that can be converted
176+
/// into `PathBuf` without allocating
177+
macro_rules! impl_read_dir_from_path {
178+
($t:ty) => {
179+
impl ReadDirFromPath<$t> for ReadDir {
180+
fn from_path(dirp: abi::S_DIR, path: $t) -> Self {
181+
let inner = InnerReadDir { dirp, root: path.into() };
182+
ReadDir { inner: Arc::new(inner) }
183+
}
184+
}
185+
};
186+
}
187+
188+
impl_read_dir_from_path!(PathBuf);
189+
impl_read_dir_from_path!(Box<Path>);
190+
impl_read_dir_from_path!(Cow<'_, Path>);
191+
impl_read_dir_from_path!(OsString);
192+
impl_read_dir_from_path!(String);
193+
162194
impl fmt::Debug for ReadDir {
163195
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164196
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.

library/std/src/sys/fs/uefi.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,9 +415,8 @@ impl fmt::Debug for File {
415415
}
416416
}
417417

418-
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
419-
let path = crate::path::absolute(p)?;
420-
let f = uefi_fs::File::from_path(&path, file::MODE_READ, 0)?;
418+
pub fn readdir<P: AsRef<Path>>(p: P) -> io::Result<ReadDir> {
419+
let f = uefi_fs::File::from_path(p.as_ref(), file::MODE_READ, 0)?;
421420
let file_info = f.file_info()?;
422421
let file_attr = FileAttr::from_uefi(file_info);
423422

library/std/src/sys/fs/unix.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#[cfg(test)]
77
mod tests;
88

9+
use alloc_crate::borrow::Cow;
910
#[cfg(all(target_os = "linux", target_env = "gnu"))]
1011
use libc::c_char;
1112
#[cfg(any(
@@ -272,6 +273,37 @@ impl ReadDir {
272273
}
273274
}
274275

276+
/// Specialization trait used to construct a `ReadDir`
277+
trait ReadDirFromPath<P> {
278+
fn from_path(dirp: DirStream, path: P) -> Self;
279+
}
280+
281+
impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
282+
default fn from_path(dirp: DirStream, path: P) -> Self {
283+
let inner = InnerReadDir { dirp, root: path.as_ref().to_path_buf() };
284+
ReadDir::new(inner)
285+
}
286+
}
287+
288+
/// This constructs a `ReadDir` for all types that can be converted
289+
/// into `PathBuf` without allocating
290+
macro_rules! impl_read_dir_from_path {
291+
($t:ty) => {
292+
impl ReadDirFromPath<$t> for ReadDir {
293+
fn from_path(dirp: DirStream, path: $t) -> Self {
294+
let inner = InnerReadDir { dirp, root: path.into() };
295+
ReadDir::new(inner)
296+
}
297+
}
298+
};
299+
}
300+
301+
impl_read_dir_from_path!(PathBuf);
302+
impl_read_dir_from_path!(Box<Path>);
303+
impl_read_dir_from_path!(Cow<'_, Path>);
304+
impl_read_dir_from_path!(OsString);
305+
impl_read_dir_from_path!(String);
306+
275307
struct DirStream(*mut libc::DIR);
276308

277309
// dir::Dir requires openat support
@@ -2077,14 +2109,12 @@ impl fmt::Debug for Mode {
20772109
}
20782110
}
20792111

2080-
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
2081-
let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
2112+
pub fn readdir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
2113+
let ptr = run_path_with_cstr(path.as_ref(), &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
20822114
if ptr.is_null() {
20832115
Err(Error::last_os_error())
20842116
} else {
2085-
let root = path.to_path_buf();
2086-
let inner = InnerReadDir { dirp: DirStream(ptr), root };
2087-
Ok(ReadDir::new(inner))
2117+
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(DirStream(ptr), path))
20882118
}
20892119
}
20902120

library/std/src/sys/fs/unsupported.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ impl fmt::Debug for File {
297297
}
298298
}
299299

300-
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
300+
pub fn readdir<P: AsRef<Path>>(_p: P) -> io::Result<ReadDir> {
301301
unsupported()
302302
}
303303

library/std/src/sys/fs/vexos.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ impl Drop for File {
477477
}
478478
}
479479

480-
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
480+
pub fn readdir<P: AsRef<Path>>(_p: P) -> io::Result<ReadDir> {
481481
// While there *is* a userspace function for reading file directories,
482482
// the necessary implementation cannot currently be done cleanly, as
483483
// VEXos does not expose directory length to user programs.

library/std/src/sys/fs/windows.rs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,47 @@ pub struct ReadDir {
5353
first: Option<c::WIN32_FIND_DATAW>,
5454
}
5555

56+
/// Specialization trait used to construct a `ReadDir`
57+
trait ReadDirFromPath<P> {
58+
fn from_path(
59+
handle: Option<FindNextFileHandle>,
60+
path: P,
61+
first: Option<c::WIN32_FIND_DATAW>,
62+
) -> Self;
63+
}
64+
65+
impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
66+
default fn from_path(
67+
handle: Option<FindNextFileHandle>,
68+
path: P,
69+
first: Option<c::WIN32_FIND_DATAW>,
70+
) -> Self {
71+
ReadDir { handle, root: Arc::new(path.as_ref().to_path_buf()), first }
72+
}
73+
}
74+
75+
/// This constructs a `ReadDir` for all types that can be converted
76+
/// into `PathBuf` without allocating
77+
macro_rules! impl_read_dir_from_path {
78+
($t:ty) => {
79+
impl ReadDirFromPath<$t> for ReadDir {
80+
fn from_path(
81+
handle: Option<FindNextFileHandle>,
82+
path: $t,
83+
first: Option<c::WIN32_FIND_DATAW>,
84+
) -> Self {
85+
ReadDir { handle, root: Arc::new(path.into()), first }
86+
}
87+
}
88+
};
89+
}
90+
91+
impl_read_dir_from_path!(PathBuf);
92+
impl_read_dir_from_path!(Box<Path>);
93+
impl_read_dir_from_path!(Cow<'_, Path>);
94+
impl_read_dir_from_path!(OsString);
95+
impl_read_dir_from_path!(String);
96+
5697
struct FindNextFileHandle(c::HANDLE);
5798

5899
unsafe impl Send for FindNextFileHandle {}
@@ -1222,17 +1263,16 @@ impl DirBuilder {
12221263
}
12231264
}
12241265

1225-
pub fn readdir(p: &Path) -> io::Result<ReadDir> {
1266+
pub fn readdir<P: AsRef<Path>>(p: P) -> io::Result<ReadDir> {
12261267
// We push a `*` to the end of the path which cause the empty path to be
12271268
// treated as the current directory. So, for consistency with other platforms,
12281269
// we explicitly error on the empty path.
1229-
if p.as_os_str().is_empty() {
1270+
if p.as_ref().as_os_str().is_empty() {
12301271
// Return an error code consistent with other ways of opening files.
12311272
// E.g. fs::metadata or File::open.
12321273
return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));
12331274
}
1234-
let root = p.to_path_buf();
1235-
let star = p.join("*");
1275+
let star = p.as_ref().join("*");
12361276
let path = maybe_verbatim(&star)?;
12371277

12381278
unsafe {
@@ -1254,11 +1294,11 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
12541294
);
12551295

12561296
if find_handle != c::INVALID_HANDLE_VALUE {
1257-
Ok(ReadDir {
1258-
handle: Some(FindNextFileHandle(find_handle)),
1259-
root: Arc::new(root),
1260-
first: Some(wfd),
1261-
})
1297+
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(
1298+
Some(FindNextFileHandle(find_handle)),
1299+
p,
1300+
Some(wfd),
1301+
))
12621302
} else {
12631303
// The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function
12641304
// if no matching files can be found, but not necessarily that the path to find the
@@ -1273,7 +1313,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
12731313
// See issue #120040: https://github.com/rust-lang/rust/issues/120040.
12741314
let last_error = api::get_last_error();
12751315
if last_error == WinError::FILE_NOT_FOUND {
1276-
return Ok(ReadDir { handle: None, root: Arc::new(root), first: None });
1316+
return Ok(<ReadDir as ReadDirFromPath<P>>::from_path(None, p, None));
12771317
}
12781318

12791319
// Just return the error constructed from the raw OS error if the above is not the case.

0 commit comments

Comments
 (0)