diff options
author | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
---|---|---|
committer | Valentin Popov <valentin@popov.link> | 2024-01-08 00:21:28 +0300 |
commit | 1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch) | |
tree | 7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/rustix/src/path/arg.rs | |
parent | 5ecd8cf2cba827454317368b68571df0d13d7842 (diff) | |
download | fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip |
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/rustix/src/path/arg.rs')
-rw-r--r-- | vendor/rustix/src/path/arg.rs | 1099 |
1 files changed, 1099 insertions, 0 deletions
diff --git a/vendor/rustix/src/path/arg.rs b/vendor/rustix/src/path/arg.rs new file mode 100644 index 0000000..f71125d --- /dev/null +++ b/vendor/rustix/src/path/arg.rs @@ -0,0 +1,1099 @@ +//! Convenient and efficient string argument passing. +//! +//! This module defines the `Arg` trait and implements it for several common +//! string types. This allows users to pass any of these string types directly +//! to rustix APIs with string arguments, and it allows rustix to implement +//! NUL-termination without the need for copying where possible. + +use crate::ffi::CStr; +use crate::io; +#[cfg(feature = "itoa")] +use crate::path::DecInt; +use crate::path::SMALL_PATH_BUFFER_SIZE; +#[cfg(all(feature = "alloc", feature = "itoa"))] +use alloc::borrow::ToOwned; +use core::mem::MaybeUninit; +use core::{ptr, slice, str}; +#[cfg(feature = "std")] +use std::ffi::{OsStr, OsString}; +#[cfg(all(feature = "std", target_os = "hermit"))] +use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt}; +#[cfg(all(feature = "std", unix))] +use std::os::unix::ffi::{OsStrExt, OsStringExt}; +#[cfg(all(feature = "std", target_os = "vxworks"))] +use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt}; +#[cfg(all(feature = "std", target_os = "wasi"))] +use std::os::wasi::ffi::{OsStrExt, OsStringExt}; +#[cfg(feature = "std")] +use std::path::{Component, Components, Iter, Path, PathBuf}; +#[cfg(feature = "alloc")] +use {crate::ffi::CString, alloc::borrow::Cow}; +#[cfg(feature = "alloc")] +use {alloc::string::String, alloc::vec::Vec}; + +/// A trait for passing path arguments. +/// +/// This is similar to [`AsRef`]`<`[`Path`]`>`, but is implemented for more +/// kinds of strings and can convert into more kinds of strings. +/// +/// # Examples +/// +/// ``` +/// # #[cfg(any(feature = "fs", feature = "net"))] +/// use rustix::ffi::CStr; +/// use rustix::io; +/// # #[cfg(any(feature = "fs", feature = "net"))] +/// use rustix::path::Arg; +/// +/// # #[cfg(any(feature = "fs", feature = "net"))] +/// pub fn touch<P: Arg>(path: P) -> io::Result<()> { +/// let path = path.into_c_str()?; +/// _touch(&path) +/// } +/// +/// # #[cfg(any(feature = "fs", feature = "net"))] +/// fn _touch(path: &CStr) -> io::Result<()> { +/// // implementation goes here +/// Ok(()) +/// } +/// ``` +/// +/// Users can then call `touch("foo")`, `touch(cstr!("foo"))`, +/// `touch(Path::new("foo"))`, or many other things. +/// +/// [`AsRef`]: std::convert::AsRef +pub trait Arg { + /// Returns a view of this string as a string slice. + fn as_str(&self) -> io::Result<&str>; + + /// Returns a potentially-lossy rendering of this string as a + /// `Cow<'_, str>`. + #[cfg(feature = "alloc")] + fn to_string_lossy(&self) -> Cow<'_, str>; + + /// Returns a view of this string as a maybe-owned [`CStr`]. + #[cfg(feature = "alloc")] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>>; + + /// Consumes `self` and returns a view of this string as a maybe-owned + /// [`CStr`]. + #[cfg(feature = "alloc")] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b; + + /// Runs a closure with `self` passed in as a `&CStr`. + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>; +} + +impl Arg for &str { + #[inline] + fn as_str(&self) -> io::Result<&str> { + Ok(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_bytes(), f) + } +} + +#[cfg(feature = "alloc")] +impl Arg for &String { + #[inline] + fn as_str(&self) -> io::Result<&str> { + Ok(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(String::as_str(self)).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + self.as_str().into_c_str() + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_bytes(), f) + } +} + +#[cfg(feature = "alloc")] +impl Arg for String { + #[inline] + fn as_str(&self) -> io::Result<&str> { + Ok(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_str()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) + } +} + +#[cfg(feature = "std")] +impl Arg for &OsStr { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + OsStr::to_string_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_bytes(), f) + } +} + +#[cfg(feature = "std")] +impl Arg for &OsString { + #[inline] + fn as_str(&self) -> io::Result<&str> { + OsString::as_os_str(self).to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_os_str().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(OsString::as_os_str(self).as_bytes()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + self.as_os_str().into_c_str() + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_bytes(), f) + } +} + +#[cfg(feature = "std")] +impl Arg for OsString { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.as_os_str().to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_os_str().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(&CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?) + } +} + +#[cfg(feature = "std")] +impl Arg for &Path { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.as_os_str().to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + Path::to_string_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_os_str().as_bytes(), f) + } +} + +#[cfg(feature = "std")] +impl Arg for &PathBuf { + #[inline] + fn as_str(&self) -> io::Result<&str> { + PathBuf::as_path(self) + .as_os_str() + .to_str() + .ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_path().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(PathBuf::as_path(self).as_os_str().as_bytes()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + self.as_path().into_c_str() + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_os_str().as_bytes(), f) + } +} + +#[cfg(feature = "std")] +impl Arg for PathBuf { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.as_os_str().to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_os_str().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.into_os_string().into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f( + &CString::new(self.into_os_string().into_vec()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + ) + } +} + +impl Arg for &CStr { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.to_str().map_err(|_utf8_err| io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + CStr::to_string_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Borrowed(self)) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Borrowed(self)) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(self) + } +} + +#[cfg(feature = "alloc")] +impl Arg for &CString { + #[inline] + fn as_str(&self) -> io::Result<&str> { + unimplemented!() + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + unimplemented!() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Borrowed(self)) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Borrowed(self)) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(self) + } +} + +#[cfg(feature = "alloc")] +impl Arg for CString { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.to_str().map_err(|_utf8_err| io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + CStr::to_string_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Borrowed(self)) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned(self)) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(&self) + } +} + +#[cfg(feature = "alloc")] +impl<'a> Arg for Cow<'a, str> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + Ok(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_ref()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + match self { + Cow::Owned(s) => CString::new(s), + Cow::Borrowed(s) => CString::new(s), + } + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_bytes(), f) + } +} + +#[cfg(feature = "std")] +#[cfg(feature = "alloc")] +impl<'a> Arg for Cow<'a, OsStr> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + (**self).to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + (**self).to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + match self { + Cow::Owned(os) => CString::new(os.into_vec()), + Cow::Borrowed(os) => CString::new(os.as_bytes()), + } + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_bytes(), f) + } +} + +#[cfg(feature = "alloc")] +impl<'a> Arg for Cow<'a, CStr> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.to_str().map_err(|_utf8_err| io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + let borrow: &CStr = core::borrow::Borrow::borrow(self); + borrow.to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Borrowed(self)) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(self) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(&self) + } +} + +#[cfg(feature = "std")] +impl<'a> Arg for Component<'a> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.as_os_str().to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_os_str().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_os_str().as_bytes(), f) + } +} + +#[cfg(feature = "std")] +impl<'a> Arg for Components<'a> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.as_path().to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_path().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_path().as_os_str().as_bytes()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.as_path().as_os_str().as_bytes()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_path().as_os_str().as_bytes(), f) + } +} + +#[cfg(feature = "std")] +impl<'a> Arg for Iter<'a> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + self.as_path().to_str().ok_or(io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + self.as_path().to_string_lossy() + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_path().as_os_str().as_bytes()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.as_path().as_os_str().as_bytes()) + .map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self.as_path().as_os_str().as_bytes(), f) + } +} + +impl Arg for &[u8] { + #[inline] + fn as_str(&self) -> io::Result<&str> { + str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self, f) + } +} + +#[cfg(feature = "alloc")] +impl Arg for &Vec<u8> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + with_c_str(self, f) + } +} + +#[cfg(feature = "alloc")] +impl Arg for Vec<u8> { + #[inline] + fn as_str(&self) -> io::Result<&str> { + str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Owned( + CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned( + CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, + )) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) + } +} + +#[cfg(feature = "itoa")] +impl Arg for DecInt { + #[inline] + fn as_str(&self) -> io::Result<&str> { + Ok(self.as_str()) + } + + #[cfg(feature = "alloc")] + #[inline] + fn to_string_lossy(&self) -> Cow<'_, str> { + Cow::Borrowed(self.as_str()) + } + + #[cfg(feature = "alloc")] + #[inline] + fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { + Ok(Cow::Borrowed(self.as_c_str())) + } + + #[cfg(feature = "alloc")] + #[inline] + fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> + where + Self: 'b, + { + Ok(Cow::Owned(self.as_c_str().to_owned())) + } + + #[inline] + fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> + where + Self: Sized, + F: FnOnce(&CStr) -> io::Result<T>, + { + f(self.as_c_str()) + } +} + +/// Runs a closure with `bytes` passed in as a `&CStr`. +#[allow(unsafe_code, clippy::int_plus_one)] +#[inline] +fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T> +where + F: FnOnce(&CStr) -> io::Result<T>, +{ + // Most paths are less than `SMALL_PATH_BUFFER_SIZE` long. The rest can go + // through the dynamic allocation path. If you're opening many files in a + // directory with a long path, consider opening the directory and using + // `openat` to open the files under it, which will avoid this, and is often + // faster in the OS as well. + + // Test with >= so that we have room for the trailing NUL. + if bytes.len() >= SMALL_PATH_BUFFER_SIZE { + return with_c_str_slow_path(bytes, f); + } + + // Taken from + // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> + let mut buf = MaybeUninit::<[u8; SMALL_PATH_BUFFER_SIZE]>::uninit(); + let buf_ptr = buf.as_mut_ptr().cast::<u8>(); + + // This helps test our safety condition below. + debug_assert!(bytes.len() + 1 <= SMALL_PATH_BUFFER_SIZE); + + // SAFETY: `bytes.len() < SMALL_PATH_BUFFER_SIZE` which means we have space + // for `bytes.len() + 1` u8s: + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); + buf_ptr.add(bytes.len()).write(0); + } + + // SAFETY: We just wrote the bytes above and they will remain valid for the + // duration of `f` b/c buf doesn't get dropped until the end of the + // function. + match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { + Ok(s) => f(s), + Err(_) => Err(io::Errno::INVAL), + } +} + +/// The slow path which handles any length. In theory OS's only support up to +/// `PATH_MAX`, but we let the OS enforce that. +#[allow(unsafe_code, clippy::int_plus_one)] +#[cold] +fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T> +where + F: FnOnce(&CStr) -> io::Result<T>, +{ + #[cfg(feature = "alloc")] + { + f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?) + } + + #[cfg(not(feature = "alloc"))] + { + #[cfg(all(libc, not(target_os = "wasi")))] + const LARGE_PATH_BUFFER_SIZE: usize = libc::PATH_MAX as usize; + #[cfg(linux_raw)] + const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize; + #[cfg(target_os = "wasi")] + const LARGE_PATH_BUFFER_SIZE: usize = 4096 as usize; // TODO: upstream this + + // Taken from + // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> + let mut buf = MaybeUninit::<[u8; LARGE_PATH_BUFFER_SIZE]>::uninit(); + let buf_ptr = buf.as_mut_ptr().cast::<u8>(); + + // This helps test our safety condition below. + if bytes.len() + 1 > LARGE_PATH_BUFFER_SIZE { + return Err(io::Errno::NAMETOOLONG); + } + + // SAFETY: `bytes.len() < LARGE_PATH_BUFFER_SIZE` which means we have + // space for `bytes.len() + 1` u8s: + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); + buf_ptr.add(bytes.len()).write(0); + } + + // SAFETY: We just wrote the bytes above and they will remain valid for + // the duration of `f` b/c buf doesn't get dropped until the end of the + // function. + match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) + { + Ok(s) => f(s), + Err(_) => Err(io::Errno::INVAL), + } + } +} |