From bb2e2d9fc7d763553f66aa9bbed3774d7f04ce70 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 7 Jun 2024 20:20:51 +0200 Subject: [PATCH 1/4] add HermitOS support of vectored read/write operations In general, the I/O interface of hermit-abi is more POSIX-like interface. Consequently, platform abstraction layer for HermitOS has slightly adjusted and some inaccuracies remove. --- std/Cargo.toml | 2 +- std/src/sys/pal/hermit/fd.rs | 39 ++++++++++++++- std/src/sys/pal/hermit/fs.rs | 45 ++++++++---------- std/src/sys/pal/hermit/futex.rs | 2 +- std/src/sys/pal/hermit/io.rs | 82 ++++++++++++++++++++++++++++++++ std/src/sys/pal/hermit/mod.rs | 1 - std/src/sys/pal/hermit/net.rs | 13 +++-- std/src/sys/pal/hermit/os.rs | 2 +- std/src/sys/pal/hermit/stdio.rs | 67 +++++++++----------------- std/src/sys/pal/hermit/thread.rs | 2 +- std/src/sys/pal/hermit/time.rs | 18 +++---- 11 files changed, 181 insertions(+), 92 deletions(-) create mode 100644 std/src/sys/pal/hermit/io.rs diff --git a/std/Cargo.toml b/std/Cargo.toml index 68bba5c2be112..32479cd2836fa 100644 --- a/std/Cargo.toml +++ b/std/Cargo.toml @@ -50,7 +50,7 @@ dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.3.9", features = ['rustc-dep-of-std'], public = true } +hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "wasi")'.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } diff --git a/std/src/sys/pal/hermit/fd.rs b/std/src/sys/pal/hermit/fd.rs index d7dab08cfbd57..3c52b85de23a2 100644 --- a/std/src/sys/pal/hermit/fd.rs +++ b/std/src/sys/pal/hermit/fd.rs @@ -1,7 +1,8 @@ #![unstable(reason = "not public", issue = "none", feature = "fd")] use super::hermit_abi; -use crate::io::{self, Read}; +use crate::cmp; +use crate::io::{self, IoSlice, IoSliceMut, Read}; use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd}; use crate::sys::cvt; use crate::sys::unsupported; @@ -9,6 +10,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::os::hermit::io::*; +const fn max_iov() -> usize { + hermit_abi::IOV_MAX +} + #[derive(Debug)] pub struct FileDesc { fd: OwnedFd, @@ -21,6 +26,22 @@ impl FileDesc { Ok(result as usize) } + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::readv( + self.as_raw_fd(), + bufs.as_mut_ptr() as *mut hermit_abi::iovec as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { let mut me = self; (&mut me).read_to_end(buf) @@ -32,6 +53,22 @@ impl FileDesc { Ok(result as usize) } + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + let ret = cvt(unsafe { + hermit_abi::writev( + self.as_raw_fd(), + bufs.as_ptr() as *const hermit_abi::iovec, + cmp::min(bufs.len(), max_iov()), + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + pub fn duplicate(&self) -> io::Result { self.duplicate_path(&[]) } diff --git a/std/src/sys/pal/hermit/fs.rs b/std/src/sys/pal/hermit/fs.rs index a4a16e6e86b0c..a98a1006ef47c 100644 --- a/std/src/sys/pal/hermit/fs.rs +++ b/std/src/sys/pal/hermit/fs.rs @@ -1,7 +1,7 @@ use super::fd::FileDesc; use super::hermit_abi::{ self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, - O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, + O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, }; use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; @@ -62,7 +62,7 @@ pub struct DirEntry { /// 64-bit inode number ino: u64, /// File type - type_: u32, + type_: u8, /// name of the entry name: OsString, } @@ -90,7 +90,7 @@ pub struct FilePermissions { #[derive(Copy, Clone, Eq, Debug)] pub struct FileType { - mode: u32, + mode: u8, } impl PartialEq for FileType { @@ -112,31 +112,23 @@ pub struct DirBuilder { impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_mtime.try_into().unwrap(), - self.stat_val.st_mtime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec)) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_atime.try_into().unwrap(), - self.stat_val.st_atime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec)) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new( - self.stat_val.st_ctime.try_into().unwrap(), - self.stat_val.st_ctime_nsec.try_into().unwrap(), - )) + Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec)) } pub fn size(&self) -> u64 { self.stat_val.st_size as u64 } + pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat_val.st_mode) } + FilePermissions { mode: self.stat_val.st_mode } } pub fn file_type(&self) -> FileType { @@ -220,7 +212,7 @@ impl Iterator for ReadDir { let entry = DirEntry { root: self.inner.root.clone(), ino: dir.d_ino, - type_: dir.d_type as u32, + type_: dir.d_type, name: OsString::from_vec(name_bytes.to_vec()), }; @@ -251,7 +243,7 @@ impl DirEntry { } pub fn file_type(&self) -> io::Result { - Ok(FileType { mode: self.type_ as u32 }) + Ok(FileType { mode: self.type_ }) } #[allow(dead_code)] @@ -385,12 +377,12 @@ impl File { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|buf| self.read(buf), bufs) + self.0.read_vectored(bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - false + self.0.is_read_vectored() } pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { @@ -402,12 +394,12 @@ impl File { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|buf| self.write(buf), bufs) + self.0.write_vectored(bufs) } #[inline] pub fn is_write_vectored(&self) -> bool { - false + self.0.is_write_vectored() } #[inline] @@ -439,13 +431,13 @@ impl DirBuilder { pub fn mkdir(&self, path: &Path) -> io::Result<()> { run_path_with_cstr(path, &|path| { - cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ()) }) } #[allow(dead_code)] pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as u32; + self.mode = mode; } } @@ -501,8 +493,9 @@ impl FromRawFd for File { } pub fn readdir(path: &Path) -> io::Result { - let fd_raw = - run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::opendir(path.as_ptr()) }))?; + let fd_raw = run_path_with_cstr(path, &|path| { + cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) }) + })?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; let root = path.to_path_buf(); diff --git a/std/src/sys/pal/hermit/futex.rs b/std/src/sys/pal/hermit/futex.rs index 571b288565871..b2d74d1311bcb 100644 --- a/std/src/sys/pal/hermit/futex.rs +++ b/std/src/sys/pal/hermit/futex.rs @@ -10,7 +10,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) - let timespec = timeout.and_then(|dur| { Some(hermit_abi::timespec { tv_sec: dur.as_secs().try_into().ok()?, - tv_nsec: dur.subsec_nanos().into(), + tv_nsec: dur.subsec_nanos().try_into().ok()?, }) }); diff --git a/std/src/sys/pal/hermit/io.rs b/std/src/sys/pal/hermit/io.rs new file mode 100644 index 0000000000000..9de7b53e53c03 --- /dev/null +++ b/std/src/sys/pal/hermit/io.rs @@ -0,0 +1,82 @@ +use crate::marker::PhantomData; +use crate::os::hermit::io::{AsFd, AsRawFd}; +use crate::slice; + +use hermit_abi::{c_void, iovec}; + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a> { + vec: iovec, + _p: PhantomData<&'a [u8]>, +} + +impl<'a> IoSlice<'a> { + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice { + vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSlice beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +#[repr(transparent)] +pub struct IoSliceMut<'a> { + vec: iovec, + _p: PhantomData<&'a mut [u8]>, +} + +impl<'a> IoSliceMut<'a> { + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut { + vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, + _p: PhantomData, + } + } + + #[inline] + pub fn advance(&mut self, n: usize) { + if self.vec.iov_len < n { + panic!("advancing IoSliceMut beyond its length"); + } + + unsafe { + self.vec.iov_len -= n; + self.vec.iov_base = self.vec.iov_base.add(n); + } + } + + #[inline] + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } + + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } + } +} + +pub fn is_terminal(fd: &impl AsFd) -> bool { + let fd = fd.as_fd(); + hermit_abi::isatty(fd.as_raw_fd()) +} diff --git a/std/src/sys/pal/hermit/mod.rs b/std/src/sys/pal/hermit/mod.rs index a64323a3a296e..eca7351d54c8d 100644 --- a/std/src/sys/pal/hermit/mod.rs +++ b/std/src/sys/pal/hermit/mod.rs @@ -23,7 +23,6 @@ pub mod env; pub mod fd; pub mod fs; pub mod futex; -#[path = "../unsupported/io.rs"] pub mod io; pub mod net; pub mod os; diff --git a/std/src/sys/pal/hermit/net.rs b/std/src/sys/pal/hermit/net.rs index 00dbca86a4bae..6016d50eba085 100644 --- a/std/src/sys/pal/hermit/net.rs +++ b/std/src/sys/pal/hermit/net.rs @@ -175,12 +175,12 @@ impl Socket { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|b| self.read(b), bufs) + self.0.read_vectored(bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - false + self.0.is_read_vectored() } fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> { @@ -209,16 +209,15 @@ impl Socket { } pub fn write(&self, buf: &[u8]) -> io::Result { - let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?; - Ok(sz.try_into().unwrap()) + self.0.write(buf) } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|b| self.write(b), bufs) + self.0.write_vectored(bufs) } pub fn is_write_vectored(&self) -> bool { - false + self.0.is_write_vectored() } pub fn set_timeout(&self, dur: Option, kind: i32) -> io::Result<()> { @@ -265,7 +264,7 @@ impl Socket { Shutdown::Read => netc::SHUT_RD, Shutdown::Both => netc::SHUT_RDWR, }; - cvt(unsafe { netc::shutdown_socket(self.as_raw_fd(), how) })?; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; Ok(()) } diff --git a/std/src/sys/pal/hermit/os.rs b/std/src/sys/pal/hermit/os.rs index 91247d30462f8..a7a73c756f216 100644 --- a/std/src/sys/pal/hermit/os.rs +++ b/std/src/sys/pal/hermit/os.rs @@ -198,5 +198,5 @@ pub fn exit(code: i32) -> ! { } pub fn getpid() -> u32 { - unsafe { hermit_abi::getpid() } + unsafe { hermit_abi::getpid() as u32 } } diff --git a/std/src/sys/pal/hermit/stdio.rs b/std/src/sys/pal/hermit/stdio.rs index 777c57b391c89..3ea00f5cc5ec9 100644 --- a/std/src/sys/pal/hermit/stdio.rs +++ b/std/src/sys/pal/hermit/stdio.rs @@ -1,6 +1,9 @@ use super::hermit_abi; use crate::io; use crate::io::{IoSlice, IoSliceMut}; +use crate::mem::ManuallyDrop; +use crate::os::hermit::io::FromRawFd; +use crate::sys::fd::FileDesc; pub struct Stdin; pub struct Stdout; @@ -13,12 +16,14 @@ impl Stdin { } impl io::Read for Stdin { - fn read(&mut self, data: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(data)]) + fn read(&mut self, buf: &mut [u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read(buf) } } - fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result { - Ok(0) + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read_vectored(bufs) + } } #[inline] @@ -34,27 +39,13 @@ impl Stdout { } impl io::Write for Stdout { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) - } else { - Ok(len as usize) - } + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write(buf) } } - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print")) - } else { - Ok(len as usize) + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write_vectored(bufs) } } @@ -75,27 +66,13 @@ impl Stderr { } impl io::Write for Stderr { - fn write(&mut self, data: &[u8]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) - } else { - Ok(len as usize) - } + fn write(&mut self, buf: &[u8]) -> io::Result { + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write(buf) } } - fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - let len; - - unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) } - - if len < 0 { - Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print")) - } else { - Ok(len as usize) + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write_vectored(bufs) } } @@ -109,10 +86,10 @@ impl io::Write for Stderr { } } -pub const STDIN_BUF_SIZE: usize = 0; +pub const STDIN_BUF_SIZE: usize = 128; -pub fn is_ebadf(_err: &io::Error) -> bool { - true +pub fn is_ebadf(err: &io::Error) -> bool { + err.raw_os_error() == Some(hermit_abi::EBADF) } pub fn panic_output() -> Option { diff --git a/std/src/sys/pal/hermit/thread.rs b/std/src/sys/pal/hermit/thread.rs index b336dcd6860e4..07a843a597ea3 100644 --- a/std/src/sys/pal/hermit/thread.rs +++ b/std/src/sys/pal/hermit/thread.rs @@ -98,5 +98,5 @@ impl Thread { } pub fn available_parallelism() -> io::Result> { - unsafe { Ok(NonZero::new_unchecked(hermit_abi::get_processor_count())) } + unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } diff --git a/std/src/sys/pal/hermit/time.rs b/std/src/sys/pal/hermit/time.rs index 2bf24462fa825..e0cb7c2aa98a5 100644 --- a/std/src/sys/pal/hermit/time.rs +++ b/std/src/sys/pal/hermit/time.rs @@ -1,11 +1,13 @@ #![allow(dead_code)] -use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC}; +use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME}; use crate::cmp::Ordering; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::time::Duration; use core::hash::{Hash, Hasher}; +const NSEC_PER_SEC: i32 = 1_000_000_000; + #[derive(Copy, Clone, Debug)] struct Timespec { t: timespec, @@ -16,8 +18,8 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } - const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { - assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } } @@ -32,7 +34,7 @@ impl Timespec { } else { Duration::new( (self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, + (self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32, ) }) } else { @@ -48,9 +50,9 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; + let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap(); + if nsec >= NSEC_PER_SEC.try_into().unwrap() { + nsec -= u32::try_from(NSEC_PER_SEC).unwrap(); secs = secs.checked_add(1)?; } Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) @@ -200,7 +202,7 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { SystemTime(Timespec::new(tv_sec, tv_nsec)) } From c16d8b1f7a58a96c6ee541c3f78d001052502aae Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Tue, 11 Jun 2024 12:40:03 +0200 Subject: [PATCH 2/4] Unify guarantees about the default allocator `std::alloc` said that the default allocator is unspecified for all crrate types except `cdylib` and `staticlib`. Adjust `std::alloc::System` documentation to say the same. Fixes #125870. --- std/src/alloc.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/std/src/alloc.rs b/std/src/alloc.rs index b98fbbf762fa2..dc4924cdf581d 100644 --- a/std/src/alloc.rs +++ b/std/src/alloc.rs @@ -73,7 +73,9 @@ pub use alloc_crate::alloc::*; /// work, such as to serve alignment requests greater than the alignment /// provided directly by the backing system allocator. /// -/// This type implements the `GlobalAlloc` trait and Rust programs by default +/// This type implements the [`GlobalAlloc`] trait. Currently the default +/// global allocator is unspecified. Libraries, however, like `cdylib`s and +/// `staticlib`s are guaranteed to use the [`System`] by default and as such /// work as if they had this definition: /// /// ```rust From 8ccbe9e6c8a8e5708b128a527da77cdb56acc702 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Tue, 11 Jun 2024 15:16:47 -0700 Subject: [PATCH 3/4] `UniqueRc`: support allocators and `T: ?Sized`. Added the following (all unstable): * Defaulted type pararameter `A: Allocator`. * `UniqueRc::new_in()`. * `T: ?Sized` where possible. * `impl CoerceUnsized for UniqueRc`. * Drive-by doc polish: links and periods at the end of sentences. These changes are motivated by supporting the implementation of unsized `Rc::make_mut()` (PR #116113), but are also intended to be obvious generalizations of `UniqueRc` to support the things `Rc` does. --- alloc/src/rc.rs | 95 +++++++++++++++++++++++++++++-------------- alloc/src/rc/tests.rs | 26 ++++++++++++ 2 files changed, 91 insertions(+), 30 deletions(-) diff --git a/alloc/src/rc.rs b/alloc/src/rc.rs index f3a4803e0d4a5..336fd52d92fb3 100644 --- a/alloc/src/rc.rs +++ b/alloc/src/rc.rs @@ -3516,7 +3516,7 @@ fn data_offset_align(align: usize) -> usize { layout.size() + layout.padding_needed_for(align) } -/// A uniquely owned `Rc` +/// A uniquely owned [`Rc`]. /// /// This represents an `Rc` that is known to be uniquely owned -- that is, have exactly one strong /// reference. Multiple weak pointers can be created, but attempts to upgrade those to strong @@ -3554,13 +3554,24 @@ fn data_offset_align(align: usize) -> usize { /// including fallible or async constructors. #[unstable(feature = "unique_rc_arc", issue = "112566")] #[derive(Debug)] -pub struct UniqueRc { +pub struct UniqueRc< + T: ?Sized, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { ptr: NonNull>, phantom: PhantomData>, + alloc: A, } +#[unstable(feature = "unique_rc_arc", issue = "112566")] +impl, U: ?Sized, A: Allocator> CoerceUnsized> + for UniqueRc +{ +} + +// Depends on A = Global impl UniqueRc { - /// Creates a new `UniqueRc` + /// Creates a new `UniqueRc`. /// /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. @@ -3569,34 +3580,36 @@ impl UniqueRc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "unique_rc_arc", issue = "112566")] pub fn new(value: T) -> Self { - Self { - ptr: Box::leak(Box::new(RcBox { + Self::new_in(value, Global) + } +} + +impl UniqueRc { + /// Creates a new `UniqueRc` in the provided allocator. + /// + /// Weak references to this `UniqueRc` can be created with [`UniqueRc::downgrade`]. Upgrading + /// these weak references will fail before the `UniqueRc` has been converted into an [`Rc`]. + /// After converting the `UniqueRc` into an [`Rc`], any weak references created beforehand will + /// point to the new [`Rc`]. + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn new_in(value: T, alloc: A) -> Self { + let (ptr, alloc) = Box::into_unique(Box::new_in( + RcBox { strong: Cell::new(0), // keep one weak reference so if all the weak pointers that are created are dropped // the UniqueRc still stays valid. weak: Cell::new(1), value, - })) - .into(), - phantom: PhantomData, - } - } - - /// Creates a new weak reference to the `UniqueRc` - /// - /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted - /// to a [`Rc`] using [`UniqueRc::into_rc`]. - #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn downgrade(this: &Self) -> Weak { - // SAFETY: This pointer was allocated at creation time and we guarantee that we only have - // one strong reference before converting to a regular Rc. - unsafe { - this.ptr.as_ref().inc_weak(); - } - Weak { ptr: this.ptr, alloc: Global } + }, + alloc, + )); + Self { ptr: ptr.into(), phantom: PhantomData, alloc } } +} - /// Converts the `UniqueRc` into a regular [`Rc`] +impl UniqueRc { + /// Converts the `UniqueRc` into a regular [`Rc`]. /// /// This consumes the `UniqueRc` and returns a regular [`Rc`] that contains the `value` that /// is passed to `into_rc`. @@ -3604,19 +3617,41 @@ impl UniqueRc { /// Any weak references created before this method is called can now be upgraded to strong /// references. #[unstable(feature = "unique_rc_arc", issue = "112566")] - pub fn into_rc(this: Self) -> Rc { + pub fn into_rc(this: Self) -> Rc { let mut this = ManuallyDrop::new(this); + + // Move the allocator out. + // SAFETY: `this.alloc` will not be accessed again, nor dropped because it is in + // a `ManuallyDrop`. + let alloc: A = unsafe { ptr::read(&this.alloc) }; + // SAFETY: This pointer was allocated at creation time so we know it is valid. unsafe { // Convert our weak reference into a strong reference this.ptr.as_mut().strong.set(1); - Rc::from_inner(this.ptr) + Rc::from_inner_in(this.ptr, alloc) + } + } +} + +impl UniqueRc { + /// Creates a new weak reference to the `UniqueRc`. + /// + /// Attempting to upgrade this weak reference will fail before the `UniqueRc` has been converted + /// to a [`Rc`] using [`UniqueRc::into_rc`]. + #[unstable(feature = "unique_rc_arc", issue = "112566")] + pub fn downgrade(this: &Self) -> Weak { + // SAFETY: This pointer was allocated at creation time and we guarantee that we only have + // one strong reference before converting to a regular Rc. + unsafe { + this.ptr.as_ref().inc_weak(); } + Weak { ptr: this.ptr, alloc: this.alloc.clone() } } } #[unstable(feature = "unique_rc_arc", issue = "112566")] -impl Deref for UniqueRc { +impl Deref for UniqueRc { type Target = T; fn deref(&self) -> &T { @@ -3626,7 +3661,7 @@ impl Deref for UniqueRc { } #[unstable(feature = "unique_rc_arc", issue = "112566")] -impl DerefMut for UniqueRc { +impl DerefMut for UniqueRc { fn deref_mut(&mut self) -> &mut T { // SAFETY: This pointer was allocated at creation time so we know it is valid. We know we // have unique ownership and therefore it's safe to make a mutable reference because @@ -3636,7 +3671,7 @@ impl DerefMut for UniqueRc { } #[unstable(feature = "unique_rc_arc", issue = "112566")] -unsafe impl<#[may_dangle] T> Drop for UniqueRc { +unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueRc { fn drop(&mut self) { unsafe { // destroy the contained object @@ -3646,7 +3681,7 @@ unsafe impl<#[may_dangle] T> Drop for UniqueRc { self.ptr.as_ref().dec_weak(); if self.ptr.as_ref().weak() == 0 { - Global.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); + self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } } } diff --git a/alloc/src/rc/tests.rs b/alloc/src/rc/tests.rs index c8a40603d9db2..0f09be7721fa9 100644 --- a/alloc/src/rc/tests.rs +++ b/alloc/src/rc/tests.rs @@ -606,6 +606,23 @@ fn test_unique_rc_drops_contents() { assert!(dropped); } +/// Exercise the non-default allocator usage. +#[test] +fn test_unique_rc_with_alloc_drops_contents() { + let mut dropped = false; + struct DropMe<'a>(&'a mut bool); + impl Drop for DropMe<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + { + let rc = UniqueRc::new_in(DropMe(&mut dropped), std::alloc::System); + drop(rc); + } + assert!(dropped); +} + #[test] fn test_unique_rc_weak_clone_holding_ref() { let mut v = UniqueRc::new(0u8); @@ -614,3 +631,12 @@ fn test_unique_rc_weak_clone_holding_ref() { let _ = w.clone(); // touch weak count *r = 123; } + +#[test] +fn test_unique_rc_unsizing_coercion() { + let mut rc: UniqueRc<[u8]> = UniqueRc::new([0u8; 3]); + assert_eq!(rc.len(), 3); + rc[0] = 123; + let rc: Rc<[u8]> = UniqueRc::into_rc(rc); + assert_eq!(*rc, [123, 0, 0]); +} From 307d1afdbd4680f2d5eab656d83b0f0beb5d626f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 10 Apr 2024 20:55:59 -0700 Subject: [PATCH 4/4] Remove superfluous escaping from byte, byte str, and c str literals --- proc_macro/src/escape.rs | 57 ++++++++++++++++++++++++++++++++++++++++ proc_macro/src/lib.rs | 51 +++++++++++++++++++++++++---------- 2 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 proc_macro/src/escape.rs diff --git a/proc_macro/src/escape.rs b/proc_macro/src/escape.rs new file mode 100644 index 0000000000000..87a4d1d50fd48 --- /dev/null +++ b/proc_macro/src/escape.rs @@ -0,0 +1,57 @@ +#[derive(Copy, Clone)] +pub(crate) struct EscapeOptions { + /// Produce \'. + pub escape_single_quote: bool, + /// Produce \". + pub escape_double_quote: bool, + /// Produce \x escapes for non-ASCII, and use \x rather than \u for ASCII + /// control characters. + pub escape_nonascii: bool, +} + +pub(crate) fn escape_bytes(bytes: &[u8], opt: EscapeOptions) -> String { + let mut repr = String::new(); + + if opt.escape_nonascii { + for &byte in bytes { + escape_single_byte(byte, opt, &mut repr); + } + } else { + let mut chunks = bytes.utf8_chunks(); + while let Some(chunk) = chunks.next() { + for ch in chunk.valid().chars() { + escape_single_char(ch, opt, &mut repr); + } + for &byte in chunk.invalid() { + escape_single_byte(byte, opt, &mut repr); + } + } + } + + repr +} + +fn escape_single_byte(byte: u8, opt: EscapeOptions, repr: &mut String) { + if byte == b'\0' { + repr.push_str("\\0"); + } else if (byte == b'\'' && !opt.escape_single_quote) + || (byte == b'"' && !opt.escape_double_quote) + { + repr.push(byte as char); + } else { + // Escapes \t, \r, \n, \\, \', \", and uses \x## for non-ASCII and + // for ASCII control characters. + repr.extend(byte.escape_ascii().map(char::from)); + } +} + +fn escape_single_char(ch: char, opt: EscapeOptions, repr: &mut String) { + if (ch == '\'' && !opt.escape_single_quote) || (ch == '"' && !opt.escape_double_quote) { + repr.push(ch); + } else { + // Escapes \0, \t, \r, \n, \\, \', \", and uses \u{...} for + // non-printable characters and for Grapheme_Extend characters, which + // includes things like U+0300 "Combining Grave Accent". + repr.extend(ch.escape_debug()); + } +} diff --git a/proc_macro/src/lib.rs b/proc_macro/src/lib.rs index 3d7d36b27e53b..581d7e3efe373 100644 --- a/proc_macro/src/lib.rs +++ b/proc_macro/src/lib.rs @@ -43,10 +43,12 @@ pub mod bridge; mod diagnostic; +mod escape; #[unstable(feature = "proc_macro_diagnostic", issue = "54140")] pub use diagnostic::{Diagnostic, Level, MultiSpan}; +use crate::escape::{escape_bytes, EscapeOptions}; use std::ffi::CStr; use std::ops::{Range, RangeBounds}; use std::path::PathBuf; @@ -1356,40 +1358,61 @@ impl Literal { /// String literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn string(string: &str) -> Literal { - let quoted = format!("{:?}", string); - assert!(quoted.starts_with('"') && quoted.ends_with('"')); - let symbol = "ed[1..quoted.len() - 1]; - Literal::new(bridge::LitKind::Str, symbol, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: false, + }; + let repr = escape_bytes(string.as_bytes(), escape); + Literal::new(bridge::LitKind::Str, &repr, None) } /// Character literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn character(ch: char) -> Literal { - let quoted = format!("{:?}", ch); - assert!(quoted.starts_with('\'') && quoted.ends_with('\'')); - let symbol = "ed[1..quoted.len() - 1]; - Literal::new(bridge::LitKind::Char, symbol, None) + let escape = EscapeOptions { + escape_single_quote: true, + escape_double_quote: false, + escape_nonascii: false, + }; + let repr = escape_bytes(ch.encode_utf8(&mut [0u8; 4]).as_bytes(), escape); + Literal::new(bridge::LitKind::Char, &repr, None) } /// Byte character literal. #[stable(feature = "proc_macro_byte_character", since = "1.79.0")] pub fn byte_character(byte: u8) -> Literal { - let string = [byte].escape_ascii().to_string(); - Literal::new(bridge::LitKind::Byte, &string, None) + let escape = EscapeOptions { + escape_single_quote: true, + escape_double_quote: false, + escape_nonascii: true, + }; + let repr = escape_bytes(&[byte], escape); + Literal::new(bridge::LitKind::Byte, &repr, None) } /// Byte string literal. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn byte_string(bytes: &[u8]) -> Literal { - let string = bytes.escape_ascii().to_string(); - Literal::new(bridge::LitKind::ByteStr, &string, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: true, + }; + let repr = escape_bytes(bytes, escape); + Literal::new(bridge::LitKind::ByteStr, &repr, None) } /// C string literal. #[stable(feature = "proc_macro_c_str_literals", since = "1.79.0")] pub fn c_string(string: &CStr) -> Literal { - let string = string.to_bytes().escape_ascii().to_string(); - Literal::new(bridge::LitKind::CStr, &string, None) + let escape = EscapeOptions { + escape_single_quote: false, + escape_double_quote: true, + escape_nonascii: false, + }; + let repr = escape_bytes(string.to_bytes(), escape); + Literal::new(bridge::LitKind::CStr, &repr, None) } /// Returns the span encompassing this literal.